Skip to content

feat(hmac-auth): Add validate request body for hmac auth plugin#5038

Merged
spacewander merged 12 commits into
apache:masterfrom
arthur-zhang:dev-hmac-body-validate
Sep 16, 2021
Merged

feat(hmac-auth): Add validate request body for hmac auth plugin#5038
spacewander merged 12 commits into
apache:masterfrom
arthur-zhang:dev-hmac-body-validate

Conversation

@arthur-zhang

Copy link
Copy Markdown
Contributor

What this PR does / why we need it:

Add validate request body for hmac auth plugin, by calculate body hmac hash and put it in Digest Header

Pre-submission checklist:

  • Did you explain what problem does this PR solve? Or what new features have been added?
  • Have you added corresponding test cases?
  • Have you modified the corresponding document?
  • Is this PR backward compatible? If it is not backward compatible, please discuss on the mailing list first

Comment thread apisix/plugins/hmac-auth.lua Outdated
if not req_body then
req_body = ""
end
local digest_header = core.request.header(ctx, DIGEST)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

digest header looks not good for me.

@arthur-zhang arthur-zhang Sep 10, 2021

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason I use this header is that:
The Digest HTTP header provides a digest of the requested resource.

Date: Tue, 19 Jan 2021 11:33:20 GMT
Server: APISIX/2.2
......
```

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you help to update the English doc?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will

@arthur-zhang
arthur-zhang requested a review from starsz September 11, 2021 01:33
Comment thread apisix/plugins/hmac-auth.lua Outdated
Comment on lines +210 to +212
if not req_body then
req_body = ""
end

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if not req_body then
req_body = ""
end
req_body = req_body or ""

Comment thread apisix/plugins/hmac-auth.lua Outdated
Comment on lines +214 to +217
if not digest_header then
-- it's ok if there is no digest header and no body
return req_body == ""
end

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we judge this first?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe not, because we can omit digest header when body is empty.

Comment thread docs/zh/latest/plugins/hmac-auth.md Outdated

### Body 校验

把 `validate_request_body` 设置为 true 来进行请求 body 的校验。 插件将计算 hmac-sha 值,对比头部中的 Digest 头部值。

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
`validate_request_body` 设置为 true 来进行请求 body 的校验。 插件将计算 hmac-sha 值,对比头部中的 Digest 头部值。
`validate_request_body` 设置为 true 来进行请求 body 的校验。插件将计算 hmac-sha 值,对比头部中的 Digest 头部值。

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Digest in code is digest, better to keep same.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment thread apisix/plugins/hmac-auth.lua Outdated
req_body = ""
end
local digest_header = core.request.header(ctx, DIGEST)
if not digest_header then

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could check this before reading the body

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe not, because we can omit digest header when body is empty.

@tzssangglass

Copy link
Copy Markdown
Member

hi @arthur-zhang, I notice this 当无请求 body 时,可不传 Digest 头部,网关会校验是否确实无请求 body。如果要传 Digest 头部,可计算长度为 0 的空字符串的 hmac-sha 值。, should we add test cases about GET method?

@arthur-zhang

arthur-zhang commented Sep 13, 2021

Copy link
Copy Markdown
Contributor Author

hi @arthur-zhang, I notice this 当无请求 body 时,可不传 Digest 头部,网关会校验是否确实无请求 body。如果要传 Digest 头部,可计算长度为 0 的空字符串的 hmac-sha 值。, should we add test cases about GET method?

I Add test === TEST 4: missing digest header and body is empty for post request without body, GET is no difference.

Comment thread docs/zh/latest/plugins/hmac-auth.md Outdated

### Body 校验

把 `validate_request_body` 设置为 true 来进行请求 body 的校验。插件将计算 hmac-sha 值,对比头部中的 Digest 头部值。

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
`validate_request_body` 设置为 true 来进行请求 body 的校验。插件将计算 hmac-sha 值,对比头部中的 Digest 头部值
`validate_request_body` 设置为 true 时,插件将计算请求 body `hmac-sha` 值,并与请求 headers 中的 Digest 的值进行校验

Comment thread t/plugin/hmac-auth3.t Outdated



=== TEST 3: missing body digest when validate_request_body is true

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
=== TEST 3: missing body digest when validate_request_body is true
=== TEST 3: missing body digest when validate_request_body is enable

Comment thread t/plugin/hmac-auth3.t Outdated



=== TEST 4: missing digest header and body is empty

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
=== TEST 4: missing digest header and body is empty
=== TEST 4: no digest header and request body is empty

Comment thread t/plugin/hmac-auth3.t

local code, body = t.test('/hello',
ngx.HTTP_POST,
body,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

here body should be nil?

Comment thread docs/zh/latest/plugins/hmac-auth.md Outdated
Digest: base64(hmac-sha(<body>))
```

当无请求 body 时,可不传 Digest 头部,网关会校验是否确实无请求 body。如果要传 Digest 头部,可计算长度为 0 的空字符串的 hmac-sha 值。

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
当无请求 body 时,可不传 Digest 头部,网关会校验是否确实无请求 body。如果要传 Digest 头部,可计算长度为 0 的空字符串的 hmac-sha 值。
当没有请求 body 时,可不传 Digest 头部,网关会校验是否确实无请求 body。如果要传 Digest 头部,插件将请求 body 默认为长度为 0 的空字符串,并参与到计算 hmac-sha 值。

Comment thread apisix/plugins/hmac-auth.lua Outdated
default = false,
},
max_req_body = {
type = "number",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
type = "number",
type = "integer",

Comment thread docs/en/latest/plugins/hmac-auth.md Outdated
| keep_headers | boolean | optional | false | [ true, false ] | Whether it is necessary to keep the request headers of `X-HMAC-SIGNATURE`, `X-HMAC-ALGORITHM` and `X-HMAC-SIGNED-HEADERS` in the http request after successful authentication. true: means to keep the http request header, false: means to remove the http request header. |
| encode_uri_params | boolean | optional | true | [ true, false ] | Whether to encode the uri parameter in the signature, for example: `params1=hello%2Cworld` is encoded, `params2=hello,world` is not encoded. true: means to encode the uri parameter in the signature, false: not to encode the uri parameter in the signature. |
| validate_request_body | boolean | optional | false | [ true, false ] | Whether to check request body. |
| max_req_body | number | optional | 512KB | | Max allowed body size. |

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to use a number instead of string in the doc's default value cell

-H 'Content-Type: text/plain; charset=utf-8' \
-d "{\"hello\":\"world\"}"

HTTP/1.1 200 OK

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to only show the first line? APISIX 2.2 doesn't support this feature. We can avoid confusing users by only show the first line.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it

Comment thread apisix/plugins/hmac-auth.lua Outdated
local ngx_decode_base64 = ngx.decode_base64
local ngx_encode_base64 = ngx.encode_base64

local DIGEST = "Digest"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this header configurable and default to "X-HMAC-DIGEST"?

There is already a Digest header in the HTTP standard and it is not relative to the hmac.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Digest

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK


local validate_request_body = get_conf_field(params.access_key, "validate_request_body")
if validate_request_body then
local max_req_body = get_conf_field(params.access_key, "max_req_body")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we could return here if no params.body_digest.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should check whether the params.body_digest exists or not.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when validate_request_body == true and no params.body_digest, why not return false?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When there is no request body, the X-HMAC-DIGEST header can be omitted.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@arthur-zhang

OK. Thanks for your explanation. How about when validate_request_body is enabled, X-APISIX-HMAC-BODY-DIGEST is required? Just pass empty strings for it when no body.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fix it

Comment thread t/plugin/hmac-auth3.t Outdated
headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature)
headers["X-HMAC-ALGORITHM"] = "hmac-sha256"
headers["Date"] = gmt
headers["X-Digest"] = ngx_encode_base64(body_digest)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be X-HMAC-DIGEST?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any string here is ok, I will change it to X-HMAC-DIGEST.


### Request body checking

When `validate_request_body` is assigned to `true`, the plugin will check the request body. The plugin will calculate the hmac-sha value of the request body,and check against the `X-HMAC-DIGEST` header.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better to mention the max_req_body limitation

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I add a note to that

Comment thread docs/en/latest/plugins/hmac-auth.md Outdated
X-HMAC-DIGEST: base64(hmac-sha(<body>))
```

When there is no request body, the `X-HMAC-DIGEST` header can be omitted. If you want to send request with this header whether the body is empty or not, you can set `X-HMAC-DIGEST` value to the hmac-sha of empty string.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
When there is no request body, the `X-HMAC-DIGEST` header can be omitted. If you want to send request with this header whether the body is empty or not, you can set `X-HMAC-DIGEST` value to the hmac-sha of empty string.
When there is no request body, the `X-HMAC-DIGEST` header can be omitted. If you want to send a request with this header when the body is missing, you can set `X-HMAC-DIGEST` value to the hmac-sha of empty string.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment thread apisix/plugins/hmac-auth.lua Outdated
},
max_req_body = {
type = "integer",
title = "Max request body allowed",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"Max request body size" would be better.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK

Comment thread docs/en/latest/plugins/hmac-auth.md Outdated

When there is no request body, the `X-HMAC-DIGEST` header can be omitted. If you want to send a request with this header when the body is missing, you can set `X-HMAC-DIGEST` value to the hmac-sha of empty string.

**Note:** The plugin will load the request body to memory to calculate the digest of the request body, which wight cause high memory consumption with large bodies. You can limit the max allowed body size by the configuration of `max_req_body`(default=512KB), request body larger than that will be rejected.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
**Note:** The plugin will load the request body to memory to calculate the digest of the request body, which wight cause high memory consumption with large bodies. You can limit the max allowed body size by the configuration of `max_req_body`(default=512KB), request body larger than that will be rejected.
**Note:** The plugin will load the request body to memory to calculate the digest of the request body, which might cause high memory consumption with large bodies. You can limit the max allowed body size by the configuration of `max_req_body`(default=512KB), request body larger than that will be rejected.

@spacewander
spacewander merged commit d6c09fb into apache:master Sep 16, 2021
@arthur-zhang
arthur-zhang deleted the dev-hmac-body-validate branch September 16, 2021 07:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants