This is an automated email from the ASF dual-hosted git repository.
kayx23 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/apisix.git
The following commit(s) were added to refs/heads/master by this push:
new 64d831f4f3 docs(plugin): sign the Digest header in the hmac-auth body
validation example (#14159)
64d831f4f3 is described below
commit 64d831f4f389ad652eb9b180fd4fd5f9c794b5b0
Author: Abhinav <[email protected]>
AuthorDate: Fri Nov 13 09:05:40 2026 +0530
docs(plugin): sign the Digest header in the hmac-auth body validation
example (#14159)
* docs(plugin): sign the Digest header in the hmac-auth body validation
example
The hmac-sig-digest-header-gen.py example sent a Digest header that was
never covered by the HMAC signature. The example now signs digest in headers
@request-target date digest, matching the validate_request_body configuration
it accompanies.
* docs(plugin): require digest to be signed in body-validation examples
Per review: sign-digest examples now list digest in signed_headers, the
successful response and 401 example are consistent, and the 401 example uses a
tampered body.
* docs(hmac-auth): use fresh digest signatures in examples
* docs(hmac-auth): align Chinese body validation example
---------
Co-authored-by: kayx23 <[email protected]>
---
docs/en/latest/plugins/hmac-auth.md | 95 +++++++++++++++++++-----------------
docs/zh/latest/plugins/hmac-auth.md | 96 ++++++++++++++++++++-----------------
2 files changed, 103 insertions(+), 88 deletions(-)
diff --git a/docs/en/latest/plugins/hmac-auth.md
b/docs/en/latest/plugins/hmac-auth.md
index da2d9486c4..90879885f3 100644
--- a/docs/en/latest/plugins/hmac-auth.md
+++ b/docs/en/latest/plugins/hmac-auth.md
@@ -668,7 +668,8 @@ curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
"methods": ["POST"],
"plugins": {
"hmac-auth": {
- "validate_request_body": true
+ "validate_request_body": true,
+ "signed_headers": ["date", "digest"]
}
},
"upstream": {
@@ -706,6 +707,9 @@ services:
plugins:
hmac-auth:
validate_request_body: true
+ signed_headers:
+ - date
+ - digest
upstream:
type: roundrobin
nodes:
@@ -771,6 +775,9 @@ spec:
_meta:
disable: false
validate_request_body: true
+ signed_headers:
+ - date
+ - digest
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
@@ -853,6 +860,9 @@ spec:
enable: true
config:
validate_request_body: true
+ signed_headers:
+ - date
+ - digest
```
Apply the configuration to your cluster:
@@ -890,6 +900,10 @@ body = '{"name": "world"}' # example request body
# skew to prolong the validity within the advised security boundary
gmt_time = datetime.now(timezone.utc).strftime('%a, %d %b %Y %H:%M:%S GMT')
+# create the SHA-256 digest of the request body and include it in the signing
string
+body_digest = hashlib.sha256(body.encode('utf-8')).digest()
+digest_header = "SHA-256=" + base64.b64encode(body_digest).decode('utf-8')
+
# construct the signing string (ordered)
# the date and any subsequent custom headers should be lowercased and
separated by a
# single space character, i.e. `<key>:<space><value>`
@@ -898,51 +912,39 @@ signing_string = (
f"{key_id}\n"
f"{request_method} {request_path}\n"
f"date: {gmt_time}\n"
+ f"digest: {digest_header}\n"
)
# create signature
signature = hmac.new(secret_key, signing_string.encode('utf-8'),
hashlib.sha256).digest()
signature_base64 = base64.b64encode(signature).decode('utf-8')
-# create the SHA-256 digest of the request body and base64 encode it
-body_digest = hashlib.sha256(body.encode('utf-8')).digest()
-body_digest_base64 = base64.b64encode(body_digest).decode('utf-8')
-
-# construct the request headers
-headers = {
- "Date": gmt_time,
- "Digest": f"SHA-256={body_digest_base64}",
- "Authorization": (
- f'Signature keyId="{key_id}",algorithm="hmac-sha256",'
- f'headers="@request-target date",'
- f'signature="{signature_base64}"'
- )
-}
-
-# print headers
-print(headers)
-```
-
-Run the script:
-
-```shell
-python3 hmac-sig-digest-header-gen.py
-```
-
-You should see the request headers printed:
+# construct the Authorization header
+authorization = (
+ f'Signature keyId="{key_id}",algorithm="hmac-sha256",'
+ f'headers="@request-target date digest",'
+ f'signature="{signature_base64}"'
+)
-```text
-{'Date': 'Fri, 06 Sep 2024 09:16:16 GMT', 'Digest':
'SHA-256=78qzJuLwSpZ8HacsTdFCQJWxzPMOf8bYctRk2ySLpS8=', 'Authorization':
'Signature keyId="john-key",algorithm="hmac-sha256",headers="@request-target
date",signature="rjS6NxOBKmzS8CZL05uLiAfE16hXdIpMD/L/HukOTYE="'}
+# print one value per line for the verification command to read
+print(gmt_time)
+print(digest_header)
+print(authorization)
```
-Using the headers generated, send a request to the Route:
+Generate fresh values and pass them directly to the request:
```shell
-curl "http://127.0.0.1:9080/post" -X POST \
- -H "Date: Fri, 06 Sep 2024 09:16:16 GMT" \
- -H "Digest: SHA-256=78qzJuLwSpZ8HacsTdFCQJWxzPMOf8bYctRk2ySLpS8=" \
- -H 'Authorization: Signature
keyId="john-key",algorithm="hmac-sha256",headers="@request-target
date",signature="rjS6NxOBKmzS8CZL05uLiAfE16hXdIpMD/L/HukOTYE="' \
- -d '{"name": "world"}'
+python3 hmac-sig-digest-header-gen.py | {
+ IFS= read -r HMAC_DATE
+ IFS= read -r HMAC_DIGEST
+ IFS= read -r HMAC_AUTHORIZATION
+ curl "http://127.0.0.1:9080/post" -X POST \
+ -H "Date: ${HMAC_DATE}" \
+ -H "Digest: ${HMAC_DIGEST}" \
+ -H "Authorization: ${HMAC_AUTHORIZATION}" \
+ -d '{"name": "world"}'
+}
```
You should see an `HTTP/1.1 200 OK` response similar to the following:
@@ -957,11 +959,11 @@ You should see an `HTTP/1.1 200 OK` response similar to
the following:
},
"headers": {
"Accept": "*/*",
- "Authorization": "Signature
keyId=\"john-key\",algorithm=\"hmac-sha256\",headers=\"@request-target
date\",signature=\"rjS6NxOBKmzS8CZL05uLiAfE16hXdIpMD/L/HukOTYE=\"",
+ "Authorization": "<generated Authorization header>",
"Content-Length": "17",
"Content-Type": "application/x-www-form-urlencoded",
- "Date": "Fri, 06 Sep 2024 09:16:16 GMT",
- "Digest": "SHA-256=78qzJuLwSpZ8HacsTdFCQJWxzPMOf8bYctRk2ySLpS8=",
+ "Date": "<generated Date header>",
+ "Digest": "<generated Digest header>",
"Host": "127.0.0.1",
"User-Agent": "curl/8.6.0",
"X-Amzn-Trace-Id": "Root=1-66d978c3-49f929ad5237da5340bbbeb4",
@@ -975,14 +977,19 @@ You should see an `HTTP/1.1 200 OK` response similar to
the following:
}
```
-If you send a request without the digest or with an invalid digest:
+If you send the same signed headers with a different body, the `Digest` header
no longer matches:
```shell
-curl "http://127.0.0.1:9080/post" -X POST \
- -H "Date: Fri, 06 Sep 2024 09:16:16 GMT" \
- -H "Digest: SHA-256=78qzJuLwSpZ8HacsTdFCQJWxzPMOf8bYctRk2ySLpS8=" \
- -H 'Authorization: Signature
keyId="john-key",algorithm="hmac-sha256",headers="@request-target
date",signature="rjS6NxOBKmzS8CZL05uLiAfE16hXdIpMD/L/HukOTYE="' \
- -d '{"name": "world"}'
+python3 hmac-sig-digest-header-gen.py | {
+ IFS= read -r HMAC_DATE
+ IFS= read -r HMAC_DIGEST
+ IFS= read -r HMAC_AUTHORIZATION
+ curl "http://127.0.0.1:9080/post" -X POST \
+ -H "Date: ${HMAC_DATE}" \
+ -H "Digest: ${HMAC_DIGEST}" \
+ -H "Authorization: ${HMAC_AUTHORIZATION}" \
+ -d '{"name": "tampered"}'
+}
```
You should see an `HTTP/1.1 401 Unauthorized` response with the following
message:
diff --git a/docs/zh/latest/plugins/hmac-auth.md
b/docs/zh/latest/plugins/hmac-auth.md
index 7082219796..ed9bd593a5 100644
--- a/docs/zh/latest/plugins/hmac-auth.md
+++ b/docs/zh/latest/plugins/hmac-auth.md
@@ -618,7 +618,7 @@ curl -X GET "http://127.0.0.1:9080/get" \
### 启用请求正文验证
-以下示例演示如何启用请求正文验证以确保请求正文的完整性。
+以下示例演示如何启用请求正文验证,并将正文摘要纳入 HMAC 签名,以确保请求正文的完整性。
<Tabs
groupId="api"
@@ -668,7 +668,8 @@ curl "http://127.0.0.1:9180/apisix/admin/routes" -X PUT \
"methods": ["POST"],
"plugins": {
"hmac-auth": {
- "validate_request_body": true
+ "validate_request_body": true,
+ "signed_headers": ["date", "digest"]
}
},
"upstream": {
@@ -706,6 +707,9 @@ services:
plugins:
hmac-auth:
validate_request_body: true
+ signed_headers:
+ - date
+ - digest
upstream:
type: roundrobin
nodes:
@@ -771,6 +775,9 @@ spec:
_meta:
disable: false
validate_request_body: true
+ signed_headers:
+ - date
+ - digest
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
@@ -853,6 +860,9 @@ spec:
enable: true
config:
validate_request_body: true
+ signed_headers:
+ - date
+ - digest
```
将配置应用到集群:
@@ -890,6 +900,10 @@ body = '{"name": "world"}' # example request body
# skew to prolong the validity within the advised security boundary
gmt_time = datetime.now(timezone.utc).strftime('%a, %d %b %Y %H:%M:%S GMT')
+# create the SHA-256 digest of the request body and include it in the signing
string
+body_digest = hashlib.sha256(body.encode('utf-8')).digest()
+digest_header = "SHA-256=" + base64.b64encode(body_digest).decode('utf-8')
+
# construct the signing string (ordered)
# the date and any subsequent custom headers should be lowercased and
separated by a
# single space character, i.e. `<key>:<space><value>`
@@ -898,51 +912,39 @@ signing_string = (
f"{key_id}\n"
f"{request_method} {request_path}\n"
f"date: {gmt_time}\n"
+ f"digest: {digest_header}\n"
)
# create signature
signature = hmac.new(secret_key, signing_string.encode('utf-8'),
hashlib.sha256).digest()
signature_base64 = base64.b64encode(signature).decode('utf-8')
-# create the SHA-256 digest of the request body and base64 encode it
-body_digest = hashlib.sha256(body.encode('utf-8')).digest()
-body_digest_base64 = base64.b64encode(body_digest).decode('utf-8')
-
-# construct the request headers
-headers = {
- "Date": gmt_time,
- "Digest": f"SHA-256={body_digest_base64}",
- "Authorization": (
- f'Signature keyId="{key_id}",algorithm="hmac-sha256",'
- f'headers="@request-target date",'
- f'signature="{signature_base64}"'
- )
-}
-
-# print headers
-print(headers)
-```
-
-运行脚本:
-
-```shell
-python3 hmac-sig-digest-header-gen.py
-```
-
-您应该看到打印的请求标头:
+# construct the Authorization header
+authorization = (
+ f'Signature keyId="{key_id}",algorithm="hmac-sha256",'
+ f'headers="@request-target date digest",'
+ f'signature="{signature_base64}"'
+)
-```text
-{'Date': 'Fri, 06 Sep 2024 09:16:16 GMT', 'Digest':
'SHA-256=78qzJuLwSpZ8HacsTdFCQJWxzPMOf8bYctRk2ySLpS8=', 'Authorization':
'Signature keyId="john-key",algorithm="hmac-sha256",headers="@request-target
date",signature="rjS6NxOBKmzS8CZL05uLiAfE16hXdIpMD/L/HukOTYE="'}
+# print one value per line for the verification command to read
+print(gmt_time)
+print(digest_header)
+print(authorization)
```
-使用生成的标头,向 Route 发送请求:
+生成新值,并将其直接传入请求:
```shell
-curl "http://127.0.0.1:9080/post" -X POST \
- -H "Date: Fri, 06 Sep 2024 09:16:16 GMT" \
- -H "Digest: SHA-256=78qzJuLwSpZ8HacsTdFCQJWxzPMOf8bYctRk2ySLpS8=" \
- -H 'Authorization: Signature
keyId="john-key",algorithm="hmac-sha256",headers="@request-target
date",signature="rjS6NxOBKmzS8CZL05uLiAfE16hXdIpMD/L/HukOTYE="' \
- -d '{"name": "world"}'
+python3 hmac-sig-digest-header-gen.py | {
+ IFS= read -r HMAC_DATE
+ IFS= read -r HMAC_DIGEST
+ IFS= read -r HMAC_AUTHORIZATION
+ curl "http://127.0.0.1:9080/post" -X POST \
+ -H "Date: ${HMAC_DATE}" \
+ -H "Digest: ${HMAC_DIGEST}" \
+ -H "Authorization: ${HMAC_AUTHORIZATION}" \
+ -d '{"name": "world"}'
+}
```
您应该会看到类似于以下内容的 `HTTP/1.1 200 OK` 响应:
@@ -957,11 +959,11 @@ curl "http://127.0.0.1:9080/post" -X POST \
},
"headers": {
"Accept": "*/*",
- "Authorization": "Signature
keyId=\"john-key\",algorithm=\"hmac-sha256\",headers=\"@request-target
date\",signature=\"rjS6NxOBKmzS8CZL05uLiAfE16hXdIpMD/L/HukOTYE=\"",
+ "Authorization": "<generated Authorization header>",
"Content-Length": "17",
"Content-Type": "application/x-www-form-urlencoded",
- "Date": "Fri, 06 Sep 2024 09:16:16 GMT",
- "Digest": "SHA-256=78qzJuLwSpZ8HacsTdFCQJWxzPMOf8bYctRk2ySLpS8=",
+ "Date": "<generated Date header>",
+ "Digest": "<generated Digest header>",
"Host": "127.0.0.1",
"User-Agent": "curl/8.6.0",
"X-Amzn-Trace-Id": "Root=1-66d978c3-49f929ad5237da5340bbbeb4",
@@ -975,13 +977,19 @@ curl "http://127.0.0.1:9080/post" -X POST \
}
```
-如果您发送的请求没有 digest 或 digest 无效:
+如果使用相同的签名标头发送不同的请求正文,`Digest` 标头将不再与正文匹配:
```shell
-curl "http://127.0.0.1:9080/post" -X POST \
- -H "Date: Fri, 06 Sep 2024 09:16:16 GMT" \
- -H 'Authorization: Signature
keyId="john-key",algorithm="hmac-sha256",headers="@request-target
date",signature="rjS6NxOBKmzS8CZL05uLiAfE16hXdIpMD/L/HukOTYE="' \
- -d '{"name": "world"}'
+python3 hmac-sig-digest-header-gen.py | {
+ IFS= read -r HMAC_DATE
+ IFS= read -r HMAC_DIGEST
+ IFS= read -r HMAC_AUTHORIZATION
+ curl "http://127.0.0.1:9080/post" -X POST \
+ -H "Date: ${HMAC_DATE}" \
+ -H "Digest: ${HMAC_DIGEST}" \
+ -H "Authorization: ${HMAC_AUTHORIZATION}" \
+ -d '{"name": "tampered"}'
+}
```
您应该看到一个 `HTTP/1.1 401 Unauthorized` 响应,其中包含以下消息: