This is an automated email from the ASF dual-hosted git repository. membphis 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 c375bd0 feat: replace timestamp with date and time in GMT format in plugin `hmac-auth` (#2301) c375bd0 is described below commit c375bd0cac6de4470782fa964323960e798cca40 Author: nic-chen <33000667+nic-c...@users.noreply.github.com> AuthorDate: Mon Sep 28 09:10:26 2020 +0800 feat: replace timestamp with date and time in GMT format in plugin `hmac-auth` (#2301) --- apisix/plugins/hmac-auth.lua | 30 ++++++----- doc/plugins/hmac-auth.md | 43 ++++++++++++--- doc/zh-cn/plugins/hmac-auth.md | 44 ++++++++++++--- t/APISIX.pm | 2 +- t/plugin/consumer-restriction.t | 30 +++++++---- t/plugin/custom_hmac_auth.t | 39 ++++++++------ t/plugin/hmac-auth.t | 116 +++++++++++++++++++++++++++------------- 7 files changed, 213 insertions(+), 91 deletions(-) diff --git a/apisix/plugins/hmac-auth.lua b/apisix/plugins/hmac-auth.lua index 33c1ddf..0c37ad7 100644 --- a/apisix/plugins/hmac-auth.lua +++ b/apisix/plugins/hmac-auth.lua @@ -32,7 +32,7 @@ local ngx_decode_base64 = ngx.decode_base64 local SIGNATURE_KEY = "X-HMAC-SIGNATURE" local ALGORITHM_KEY = "X-HMAC-ALGORITHM" -local TIMESTAMP_KEY = "X-HMAC-TIMESTAMP" +local DATE_KEY = "Date" local ACCESS_KEY = "X-HMAC-ACCESS-KEY" local SIGNED_HEADERS_KEY = "X-HMAC-SIGNED-HEADERS" local plugin_name = "hmac-auth" @@ -57,7 +57,7 @@ local schema = { }, clock_skew = { type = "integer", - default = 300 + default = 0 }, signed_headers = { type = "array", @@ -218,7 +218,7 @@ local function generate_signature(ctx, secret_key, params) local signing_string = request_method .. canonical_uri .. canonical_query_string - .. params.access_key .. params.timestamp + .. params.access_key .. params.date .. core.table.concat(canonical_headers, "") core.log.info("signing_string:", signing_string, @@ -246,10 +246,16 @@ local function validate(ctx, params) core.log.info("clock_skew: ", conf.clock_skew) if conf.clock_skew and conf.clock_skew > 0 then - local diff = abs(ngx_time() - params.timestamp) - core.log.info("timestamp diff: ", diff) + local time = ngx.parse_http_time(params.date) + core.log.info("params.date: ", params.date, " time: ", time) + if not time then + return nil, {message = "Invalid GMT format time"} + end + + local diff = abs(ngx_time() - time) + core.log.info("gmt diff: ", diff) if diff > conf.clock_skew then - return nil, {message = "Invalid timestamp"} + return nil, {message = "Clock skew exceeded"} end end @@ -285,7 +291,7 @@ local function get_params(ctx) local access_key = ACCESS_KEY local signature_key = SIGNATURE_KEY local algorithm_key = ALGORITHM_KEY - local timestamp_key = TIMESTAMP_KEY + local date_key = DATE_KEY local signed_headers_key = SIGNED_HEADERS_KEY if try_attr(local_conf, "plugin_attr", "hmac-auth") then @@ -293,14 +299,14 @@ local function get_params(ctx) access_key = attr.access_key or access_key signature_key = attr.signature_key or signature_key algorithm_key = attr.algorithm_key or algorithm_key - timestamp_key = attr.timestamp_key or timestamp_key + date_key = attr.date_key or date_key signed_headers_key = attr.signed_headers_key or signed_headers_key end local app_key = core.request.header(ctx, access_key) local signature = core.request.header(ctx, signature_key) local algorithm = core.request.header(ctx, algorithm_key) - local timestamp = core.request.header(ctx, timestamp_key) + local date = core.request.header(ctx, date_key) local signed_headers = core.request.header(ctx, signed_headers_key) core.log.info("signature_key: ", signature_key) @@ -316,11 +322,11 @@ local function get_params(ctx) #auth_data, " auth_data: ", core.json.delay_encode(auth_data)) - if #auth_data == 6 and auth_data[1] == "hmac-auth-v2" then + if #auth_data == 6 and auth_data[1] == "hmac-auth-v1" then app_key = auth_data[2] signature = auth_data[3] algorithm = auth_data[4] - timestamp = auth_data[5] + date = auth_data[5] signed_headers = auth_data[6] end end @@ -328,7 +334,7 @@ local function get_params(ctx) params.access_key = app_key params.algorithm = algorithm params.signature = signature - params.timestamp = timestamp or 0 + params.date = date or "" params.signed_headers = signed_headers and ngx_re.split(signed_headers, ";") core.log.info("params: ", core.json.delay_encode(params)) diff --git a/doc/plugins/hmac-auth.md b/doc/plugins/hmac-auth.md index 2093dcc..a3d31e2 100644 --- a/doc/plugins/hmac-auth.md +++ b/doc/plugins/hmac-auth.md @@ -40,7 +40,7 @@ The `consumer` then adds its key to request header to verify its request. | access_key | string | required | | | Different `consumer` objects should have different values, and it should be unique. If different consumers use the same `access_key`, a request matching exception will occur. | | secret_key | string | required | | | Use as a pair with `access_key`. | | algorithm | string | optional | "hmac-sha256" | ["hmac-sha1", "hmac-sha256", "hmac-sha512"] | Encryption algorithm. | -| clock_skew | integer | optional | 300 | | The clock skew allowed by the signature in seconds. For example, if the time is allowed to skew by 10 seconds, then it should be set to `10`. especially, `0` means not checking timestamp | +| clock_skew | integer | optional | 0 | | The clock skew allowed by the signature in seconds. For example, if the time is allowed to skew by 10 seconds, then it should be set to `10`. especially, `0` means not checking `Date` | | signed_headers | array[string] | optional | | | Restrict the headers that are added to the encrypted calculation. After the specified, the client request can only specify the headers within this range. When this item is empty, all the headers specified by the client request will be added to the encrypted calculation | ## How To Enable @@ -83,12 +83,13 @@ curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f13 ## Test Plugin ### generate signature: -The calculation formula of the signature is `signature = HMAC-SHAx-HEX(secret_key, signing_string)`. From the formula, it can be seen that in order to obtain the signature, two parameters, `SECRET_KEY` and `signing_STRING`, are required. Where secret_key is configured by the corresponding consumer, the calculation formula of `signing_STRING` is `signing_string = HTTP Method + HTTP URI + canonical_query_string + access_key + timestamp + signed_headers_string` +The calculation formula of the signature is `signature = HMAC-SHAx-HEX(secret_key, signing_string)`. From the formula, it can be seen that in order to obtain the signature, two parameters, `SECRET_KEY` and `signing_STRING`, are required. Where secret_key is configured by the corresponding consumer, the calculation formula of `signing_STRING` is `signing_string = HTTP Method + HTTP URI + canonical_query_string + access_key + Date + signed_headers_string` 1. **HTTP Method** : Refers to the GET, PUT, POST and other request methods defined in the HTTP protocol, and must be in all uppercase. 2. **HTTP URI** : `HTTP URI` requirements must start with "/", those that do not start with "/" need to be added, and the empty path is "/". -3. **canonical_query_string** :`canonical_query_string` is the result of encoding the `query` in the URL (`query` is the string "key1 = valve1 & key2 = valve2" after the "?" in the URL). -4. **signed_headers_string** :`signed_headers_string` is the result of obtaining the fields specified by the client from the request header and concatenating the strings in order. +3. **Date** : Date in http header(GMT format). +4. **canonical_query_string** :`canonical_query_string` is the result of encoding the `query` in the URL (`query` is the string "key1 = valve1 & key2 = valve2" after the "?" in the URL). +5. **signed_headers_string** :`signed_headers_string` is the result of obtaining the fields specified by the client from the request header and concatenating the strings in order. > The coding steps are as follows: @@ -107,13 +108,13 @@ The calculation formula of the signature is `signature = HMAC-SHAx-HEX(secret_ke ### Use the generated signature to try the request -**Note: ACCESS_KEY, SIGNATURE, ALGORITHM, TIMESTAMP, SIGNED_HEADERS respectively represent the corresponding variables** +**Note: ACCESS_KEY, SIGNATURE, ALGORITHM, DATE, SIGNED_HEADERS respectively represent the corresponding variables** **Note: SIGNED_HEADERS is the headers specified by the client to join the encryption calculation, multiple separated by semicolons, Example: User-Agent;Accept-Language** * The signature information is put together in the request header `Authorization` field: ```shell -$ curl http://127.0.0.1:9080/index.html -H 'Authorization: hmac-auth-v1# + ACCESS_KEY + # + base64_encode(SIGNATURE) + # + ALGORITHM + # + TIMESTAMP + # + SIGNED_HEADERS' -i +$ curl http://127.0.0.1:9080/index.html -H 'Authorization: hmac-auth-v1# + ACCESS_KEY + # + base64_encode(SIGNATURE) + # + ALGORITHM + # + DATE + # + SIGNED_HEADERS' -i HTTP/1.1 200 OK Content-Type: text/html Content-Length: 13175 @@ -128,7 +129,35 @@ Accept-Ranges: bytes * The signature information is separately placed in the request header: ```shell -$ curl http://127.0.0.1:9080/index.html -H 'X-HMAC-SIGNATURE: base64_encode(SIGNATURE)' -H 'X-HMAC-ALGORITHM: ALGORITHM' -H 'X-HMAC-TIMESTAMP: TIMESTAMP' -H 'X-HMAC-ACCESS-KEY: ACCESS_KEY' -H 'X-HMAC-SIGNED-HEADERS: SIGNED_HEADERS' -i +$ curl http://127.0.0.1:9080/index.html -H 'X-HMAC-SIGNATURE: base64_encode(SIGNATURE)' -H 'X-HMAC-ALGORITHM: ALGORITHM' -H 'Date: DATE' -H 'X-HMAC-ACCESS-KEY: ACCESS_KEY' -H 'X-HMAC-SIGNED-HEADERS: SIGNED_HEADERS' -i +HTTP/1.1 200 OK +Content-Type: text/html +Content-Length: 13175 +... +Accept-Ranges: bytes + +<!DOCTYPE html> +<html lang="cn"> +``` + +## Custom header key + +We can customize header key for auth parameters by adding the attribute configuration of the plugin under `plugin_attr` in `conf / config.yaml`. + +```yaml +plugin_attr: + hmac-auth: + signature_key: X-APISIX-HMAC-SIGNATURE + algorithm_key: X-APISIX-HMAC-ALGORITHM + date_key: X-APISIX-DATE + access_key: X-APISIX-HMAC-ACCESS-KEY + signed_headers_key: X-APISIX-HMAC-SIGNED-HEADERS +``` + +**After customizing the header, request example:** + +```shell +$ curl http://127.0.0.1:9080/index.html -H 'X-APISIX-HMAC-SIGNATURE: base64_encode(SIGNATURE)' -H 'X-APISIX-HMAC-ALGORITHM: ALGORITHM' -H 'X-APISIX-DATE: DATE' -H 'X-APISIX-HMAC-ACCESS-KEY: ACCESS_KEY' -H 'X-APISIX-HMAC-SIGNED-HEADERS: SIGNED_HEADERS' -i HTTP/1.1 200 OK Content-Type: text/html Content-Length: 13175 diff --git a/doc/zh-cn/plugins/hmac-auth.md b/doc/zh-cn/plugins/hmac-auth.md index 3024373..912003f 100644 --- a/doc/zh-cn/plugins/hmac-auth.md +++ b/doc/zh-cn/plugins/hmac-auth.md @@ -40,7 +40,7 @@ | access_key | string | 必须 | | | 不同的 `consumer` 对象应有不同的值,它应当是唯一的。不同 consumer 使用了相同的 `access_key` ,将会出现请求匹配异常。 | | secret_key | string | 必须 | | | 与 `access_key` 配对使用。 | | algorithm | string | 可选 | "hmac-sha256" | ["hmac-sha1", "hmac-sha256", "hmac-sha512"] | 加密算法。 | -| clock_skew | integer | 可选 | 300 | | 签名允许的时间偏移,以秒为单位的计时。比如允许时间偏移 10 秒钟,那么就应设置为 `10`。特别地,`0` 表示不对 `timestamp` 进行检查。 | +| clock_skew | integer | 可选 | 0 | | 签名允许的时间偏移,以秒为单位的计时。比如允许时间偏移 10 秒钟,那么就应设置为 `10`。特别地,`0` 表示不对 `Date` 进行检查。 | | signed_headers | array[string] | 可选 | | | 限制加入加密计算的 headers ,指定后客户端请求只能在此范围内指定 headers ,此项为空时将把所有客户端请求指定的 headers 加入加密计算。如: ["User-Agent", "Accept-Language", "x-custom-a"] | @@ -85,12 +85,13 @@ curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f13 ### 签名生成公式 -签名的计算公式为 `signature = HMAC-SHAx-HEX(secret_key, signing_string)`,从公式可以看出,想要获得签名需要得到 `secret_key` 和 `signing_string` 两个参数。其中 `secret_key` 为对应 consumer 所配置的, `signing_string` 的计算公式为 `signing_string = HTTP Method + HTTP URI + canonical_query_string + access_key + timestamp + signed_headers_string` +签名的计算公式为 `signature = HMAC-SHAx-HEX(secret_key, signing_string)`,从公式可以看出,想要获得签名需要得到 `secret_key` 和 `signing_string` 两个参数。其中 `secret_key` 为对应 consumer 所配置的, `signing_string` 的计算公式为 `signing_string = HTTP Method + HTTP URI + canonical_query_string + access_key + Date + signed_headers_string` 1. **HTTP Method**:指 HTTP 协议中定义的 GET、PUT、POST 等请求方法,必须使用全大写的形式。 2. **HTTP URI**:要求必须以“/”开头,不以“/”开头的需要补充上,空路径为“/”。 -3. **canonical_query_string**:是对于 URL 中的 query( query 即 URL 中 ? 后面的 key1=valve1&key2=valve2 字符串)进行编码后的结果。 -4. **signed_headers_string**:是从请求头中获取客户端指定的字段,并按顺序拼接字符串的结果。 +3. **Date**:请求头中的 Date ( GMT 格式 )。 +4. **canonical_query_string**:是对于 URL 中的 query( query 即 URL 中 ? 后面的 key1=valve1&key2=valve2 字符串)进行编码后的结果。 +5. **signed_headers_string**:是从请求头中获取客户端指定的字段,并按顺序拼接字符串的结果。 > canonical_query_string 编码步骤如下: @@ -109,13 +110,13 @@ curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f13 ### 使用生成好的签名进行请求尝试 -**注: ACCESS_KEY, SIGNATURE, ALGORITHM, TIMESTAMP, SIGNED_HEADERS 分别代表对应的变量** +**注: ACCESS_KEY, SIGNATURE, ALGORITHM, DATE, SIGNED_HEADERS 分别代表对应的变量** **注: SIGNED_HEADERS 为客户端指定的加入加密计算的 headers ,多个以英文分号分隔如:User-Agent;Accept-Language** * 签名信息拼一起放到请求头 `Authorization` 字段中: ```shell -$ curl http://127.0.0.1:9080/index.html -H 'Authorization: hmac-auth-v1# + ACCESS_KEY + # + base64_encode(SIGNATURE) + # + ALGORITHM + # + TIMESTAMP + # + SIGNED_HEADERS' -i +$ curl http://127.0.0.1:9080/index.html -H 'Authorization: hmac-auth-v1# + ACCESS_KEY + # + base64_encode(SIGNATURE) + # + ALGORITHM + # + DATE + # + SIGNED_HEADERS' -i HTTP/1.1 200 OK Content-Type: text/html Content-Length: 13175 @@ -130,7 +131,36 @@ Accept-Ranges: bytes * 签名信息分开分别放到请求头: ```shell -$ curl http://127.0.0.1:9080/index.html -H 'X-HMAC-SIGNATURE: base64_encode(SIGNATURE)' -H 'X-HMAC-ALGORITHM: ALGORITHM' -H 'X-HMAC-TIMESTAMP: TIMESTAMP' -H 'X-HMAC-ACCESS-KEY: ACCESS_KEY' -H 'X-HMAC-SIGNED-HEADERS: SIGNED_HEADERS' -i +$ curl http://127.0.0.1:9080/index.html -H 'X-HMAC-SIGNATURE: base64_encode(SIGNATURE)' -H 'X-HMAC-ALGORITHM: ALGORITHM' -H 'Date: DATE' -H 'X-HMAC-ACCESS-KEY: ACCESS_KEY' -H 'X-HMAC-SIGNED-HEADERS: SIGNED_HEADERS' -i +HTTP/1.1 200 OK +Content-Type: text/html +Content-Length: 13175 +... +Accept-Ranges: bytes + +<!DOCTYPE html> +<html lang="cn"> +``` + + +## 自定义 header 名称 + +我们可以在 `conf/config.yaml` 中,`plugin_attr` 下添加插件的属性配置来自定义参数 header 名称。 + +```yaml +plugin_attr: + hmac-auth: + signature_key: X-APISIX-HMAC-SIGNATURE + algorithm_key: X-APISIX-HMAC-ALGORITHM + date_key: X-APISIX-DATE + access_key: X-APISIX-HMAC-ACCESS-KEY + signed_headers_key: X-APISIX-HMAC-SIGNED-HEADERS +``` + +**自定义 header 后,请求示例:** + +```shell +$ curl http://127.0.0.1:9080/index.html -H 'X-APISIX-HMAC-SIGNATURE: base64_encode(SIGNATURE)' -H 'X-APISIX-HMAC-ALGORITHM: ALGORITHM' -H 'X-APISIX-DATE: DATE' -H 'X-APISIX-HMAC-ACCESS-KEY: ACCESS_KEY' -H 'X-APISIX-HMAC-SIGNED-HEADERS: SIGNED_HEADERS' -i HTTP/1.1 200 OK Content-Type: text/html Content-Length: 13175 diff --git a/t/APISIX.pm b/t/APISIX.pm index d577228..c951cd9 100644 --- a/t/APISIX.pm +++ b/t/APISIX.pm @@ -103,7 +103,7 @@ plugin_attr: hmac-auth: signature_key: X-APISIX-HMAC-SIGNATURE algorithm_key: X-APISIX-HMAC-ALGORITHM - timestamp_key: X-APISIX-HMAC-TIMESTAMP + date_key: X-APISIX-DATE access_key: X-APISIX-HMAC-ACCESS-KEY signed_headers_key: X-APISIX-HMAC-SIGNED-HEADERS _EOC_ diff --git a/t/plugin/consumer-restriction.t b/t/plugin/consumer-restriction.t index 69708ea..f93cb16 100644 --- a/t/plugin/consumer-restriction.t +++ b/t/plugin/consumer-restriction.t @@ -619,7 +619,7 @@ passed "access_key": "my-access-key", "secret_key": "my-secret-key", "algorithm": "hmac-sha256", - "clock_skew": 300 + "clock_skew": 0 }, "consumer-restriction": { "type": "service_id", @@ -709,7 +709,8 @@ passed --- config location /t { content_by_lua_block { - local ngx_time = ngx.time + local ngx_time = ngx.time + local ngx_http_time = ngx.http_time local core = require("apisix.core") local t = require("lib.test_admin") local hmac = require("resty.hmac") @@ -717,19 +718,20 @@ location /t { local secret_key = "my-secret-key" local timestamp = ngx_time() + local gmt = ngx_http_time(timestamp) local access_key = "my-access-key" local custom_header_a = "asld$%dfasf" local custom_header_b = "23879fmsldfk" local signing_string = "GET" .. "/hello" .. "" .. - access_key .. timestamp .. custom_header_a .. custom_header_b + access_key .. gmt .. custom_header_a .. custom_header_b local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) core.log.info("signature:", ngx_encode_base64(signature)) local headers = {} headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) headers["X-HMAC-ALGORITHM"] = "hmac-sha256" - headers["X-HMAC-TIMESTAMP"] = timestamp + headers["Date"] = gmt headers["X-HMAC-ACCESS-KEY"] = access_key headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" headers["x-custom-header-a"] = custom_header_a @@ -865,6 +867,7 @@ passed location /t { content_by_lua_block { local ngx_time = ngx.time + local ngx_http_time = ngx.http_time local core = require("apisix.core") local t = require("lib.test_admin") local hmac = require("resty.hmac") @@ -872,19 +875,20 @@ location /t { local secret_key = "my-secret-key" local timestamp = ngx_time() + local gmt = ngx_http_time(timestamp) local access_key = "my-access-key" local custom_header_a = "asld$%dfasf" local custom_header_b = "23879fmsldfk" local signing_string = "GET" .. "/hello" .. "" .. - access_key .. timestamp .. custom_header_a .. custom_header_b + access_key .. gmt .. custom_header_a .. custom_header_b local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) core.log.info("signature:", ngx_encode_base64(signature)) local headers = {} headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) headers["X-HMAC-ALGORITHM"] = "hmac-sha256" - headers["X-HMAC-TIMESTAMP"] = timestamp + headers["Date"] = gmt headers["X-HMAC-ACCESS-KEY"] = access_key headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" headers["x-custom-header-a"] = custom_header_a @@ -943,7 +947,7 @@ qr/\{"message":"The service_id is forbidden."\}/ "access_key": "my-access-key", "secret_key": "my-secret-key", "algorithm": "hmac-sha256", - "clock_skew": 300 + "clock_skew": 0 }, "consumer-restriction": { "type": "service_id", @@ -1034,6 +1038,7 @@ passed location /t { content_by_lua_block { local ngx_time = ngx.time + local ngx_http_time = ngx.http_time local core = require("apisix.core") local t = require("lib.test_admin") local hmac = require("resty.hmac") @@ -1041,19 +1046,20 @@ location /t { local secret_key = "my-secret-key" local timestamp = ngx_time() + local gmt = ngx_http_time(timestamp) local access_key = "my-access-key" local custom_header_a = "asld$%dfasf" local custom_header_b = "23879fmsldfk" local signing_string = "GET" .. "/hello" .. "" .. - access_key .. timestamp .. custom_header_a .. custom_header_b + access_key .. gmt .. custom_header_a .. custom_header_b local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) core.log.info("signature:", ngx_encode_base64(signature)) local headers = {} headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) headers["X-HMAC-ALGORITHM"] = "hmac-sha256" - headers["X-HMAC-TIMESTAMP"] = timestamp + headers["Date"] = gmt headers["X-HMAC-ACCESS-KEY"] = access_key headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" headers["x-custom-header-a"] = custom_header_a @@ -1143,6 +1149,7 @@ passed location /t { content_by_lua_block { local ngx_time = ngx.time + local ngx_http_time = ngx.http_time local core = require("apisix.core") local t = require("lib.test_admin") local hmac = require("resty.hmac") @@ -1150,19 +1157,20 @@ location /t { local secret_key = "my-secret-key" local timestamp = ngx_time() + local gmt = ngx_http_time(timestamp) local access_key = "my-access-key" local custom_header_a = "asld$%dfasf" local custom_header_b = "23879fmsldfk" local signing_string = "GET" .. "/hello" .. "" .. - access_key .. timestamp .. custom_header_a .. custom_header_b + access_key .. gmt .. custom_header_a .. custom_header_b local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) core.log.info("signature:", ngx_encode_base64(signature)) local headers = {} headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) headers["X-HMAC-ALGORITHM"] = "hmac-sha256" - headers["X-HMAC-TIMESTAMP"] = timestamp + headers["Date"] = gmt headers["X-HMAC-ACCESS-KEY"] = access_key headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" headers["x-custom-header-a"] = custom_header_a diff --git a/t/plugin/custom_hmac_auth.t b/t/plugin/custom_hmac_auth.t index 49e3287..e02705b 100644 --- a/t/plugin/custom_hmac_auth.t +++ b/t/plugin/custom_hmac_auth.t @@ -40,7 +40,8 @@ __DATA__ "plugins": { "hmac-auth": { "access_key": "my-access-key", - "secret_key": "my-secret-key" + "secret_key": "my-secret-key", + "clock_skew": 10 } } }]], @@ -53,7 +54,7 @@ __DATA__ "access_key": "my-access-key", "secret_key": "my-secret-key", "algorithm": "hmac-sha256", - "clock_skew": 300 + "clock_skew": 10 } } } @@ -184,7 +185,7 @@ GET /hello --- more_headers X-APISIX-HMAC-SIGNATURE: asdf X-APISIX-HMAC-ALGORITHM: hmac-sha256 -X-APISIX-HMAC-TIMESTAMP: 112 +X-APISIX-Date: Thu, 24 Sep 2020 06:39:52 GMT X-APISIX-HMAC-ACCESS-KEY: sdf --- error_code: 401 --- response_body @@ -200,7 +201,7 @@ GET /hello --- more_headers X-APISIX-HMAC-SIGNATURE: asdf X-APISIX-HMAC-ALGORITHM: ljlj -X-APISIX-HMAC-TIMESTAMP: 112 +X-APISIX-Date: Thu, 24 Sep 2020 06:39:52 GMT X-APISIX-HMAC-ACCESS-KEY: sdf --- error_code: 401 --- response_body @@ -210,17 +211,17 @@ X-APISIX-HMAC-ACCESS-KEY: sdf -=== TEST 8: verify: invalid timestamp +=== TEST 8: verify: Invalid GMT format time --- request GET /hello --- more_headers X-APISIX-HMAC-SIGNATURE: asdf X-APISIX-HMAC-ALGORITHM: hmac-sha256 -X-APISIX-HMAC-TIMESTAMP: 112 +X-APISIX-Date: adfa X-APISIX-HMAC-ACCESS-KEY: my-access-key --- error_code: 401 --- response_body -{"message":"Invalid timestamp"} +{"message":"Invalid GMT format time"} --- no_error_log [error] @@ -230,26 +231,28 @@ X-APISIX-HMAC-ACCESS-KEY: my-access-key --- config location /t { content_by_lua_block { - local ngx_time = ngx.time + local ngx_time = ngx.time + local ngx_http_time = ngx.http_time local core = require("apisix.core") local t = require("lib.test_admin") local hmac = require("resty.hmac") local ngx_encode_base64 = ngx.encode_base64 local secret_key = "my-secret-key" - local timestamp = ngx_time() + local time = ngx_time() + local gmt = ngx_http_time(time) local access_key = "my-access-key" local custom_header_a = "asld$%dfasf" local custom_header_b = "23879fmsldfk" local signing_string = "GET" .. "/hello" .. "" .. - access_key .. timestamp .. custom_header_a .. custom_header_b + access_key .. gmt .. custom_header_a .. custom_header_b local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) core.log.info("signature:", ngx_encode_base64(signature)) local headers = {} headers["X-APISIX-HMAC-SIGNATURE"] = ngx_encode_base64(signature) headers["X-APISIX-HMAC-ALGORITHM"] = "hmac-sha256" - headers["X-APISIX-HMAC-TIMESTAMP"] = timestamp + headers["X-APISIX-DATE"] = gmt headers["X-APISIX-HMAC-ACCESS-KEY"] = access_key headers["X-APISIX-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" headers["x-custom-header-a"] = custom_header_a @@ -323,23 +326,25 @@ passed -=== TEST 11: verify: invalid timestamp +=== TEST 11: verify: Clock skew exceeded --- config location /t { content_by_lua_block { - local ngx_time = ngx.time + local ngx_time = ngx.time + local ngx_http_time = ngx.http_time local core = require("apisix.core") local t = require("lib.test_admin") local hmac = require("resty.hmac") local ngx_encode_base64 = ngx.encode_base64 local secret_key = "my-secret-key2" - local timestamp = ngx_time() + local time = ngx_time() + local gmt = ngx_http_time(time) local access_key = "my-access-key2" local custom_header_a = "asld$%dfasf" local custom_header_b = "23879fmsldfk" local signing_string = "GET" .. "/hello" .. "" .. - access_key .. timestamp .. custom_header_a .. custom_header_b + access_key .. gmt .. custom_header_a .. custom_header_b ngx.sleep(2) @@ -348,7 +353,7 @@ location /t { local headers = {} headers["X-APISIX-HMAC-SIGNATURE"] = ngx_encode_base64(signature) headers["X-APISIX-HMAC-ALGORITHM"] = "hmac-sha256" - headers["X-APISIX-HMAC-TIMESTAMP"] = timestamp + headers["X-APISIX-DATE"] = gmt headers["X-APISIX-HMAC-ACCESS-KEY"] = access_key local code, body = t.test('/hello', @@ -366,6 +371,6 @@ location /t { GET /t --- error_code: 401 --- response_body eval -qr/\{"message":"Invalid timestamp"\}/ +qr/\{"message":"Clock skew exceeded"\}/ --- no_error_log [error] diff --git a/t/plugin/hmac-auth.t b/t/plugin/hmac-auth.t index dd584dc..efdf9f0 100644 --- a/t/plugin/hmac-auth.t +++ b/t/plugin/hmac-auth.t @@ -36,7 +36,8 @@ __DATA__ "plugins": { "hmac-auth": { "access_key": "my-access-key", - "secret_key": "my-secret-key" + "secret_key": "my-secret-key", + "clock_skew": 10 } } }]], @@ -49,7 +50,7 @@ __DATA__ "access_key": "my-access-key", "secret_key": "my-secret-key", "algorithm": "hmac-sha256", - "clock_skew": 300 + "clock_skew": 10 } } } @@ -246,7 +247,7 @@ GET /hello --- more_headers X-HMAC-SIGNATURE: asdf X-HMAC-ALGORITHM: hmac-sha256 -X-HMAC-TIMESTAMP: 112 +Date: Thu, 24 Sep 2020 06:39:52 GMT X-HMAC-ACCESS-KEY: sdf --- error_code: 401 --- response_body @@ -262,7 +263,7 @@ GET /hello --- more_headers X-HMAC-SIGNATURE: asdf X-HMAC-ALGORITHM: ljlj -X-HMAC-TIMESTAMP: 112 +Date: Thu, 24 Sep 2020 06:39:52 GMT X-HMAC-ACCESS-KEY: my-access-key --- error_code: 401 --- response_body @@ -272,27 +273,59 @@ X-HMAC-ACCESS-KEY: my-access-key -=== TEST 10: verify: invalid timestamp +=== TEST 10: verify: Clock skew exceeded --- request GET /hello --- more_headers X-HMAC-SIGNATURE: asdf X-HMAC-ALGORITHM: hmac-sha256 -X-HMAC-TIMESTAMP: 112 +Date: Thu, 24 Sep 2020 06:39:52 GMT X-HMAC-ACCESS-KEY: my-access-key --- error_code: 401 --- response_body -{"message":"Invalid timestamp"} +{"message":"Clock skew exceeded"} --- no_error_log [error] -=== TEST 11: verify: ok +=== TEST 11: verify: missing Date +--- request +GET /hello +--- more_headers +X-HMAC-SIGNATURE: asdf +X-HMAC-ALGORITHM: hmac-sha256 +X-HMAC-ACCESS-KEY: my-access-key +--- error_code: 401 +--- response_body +{"message":"Invalid GMT format time"} +--- no_error_log +[error] + + + +=== TEST 12: verify: Invalid GMT format time +--- request +GET /hello +--- more_headers +X-HMAC-SIGNATURE: asdf +X-HMAC-ALGORITHM: hmac-sha256 +Date: adfsdf +X-HMAC-ACCESS-KEY: my-access-key +--- error_code: 401 +--- response_body +{"message":"Invalid GMT format time"} +--- no_error_log +[error] + + + +=== TEST 13: verify: ok --- config location /t { content_by_lua_block { local ngx_time = ngx.time + local ngx_http_time = ngx.http_time local core = require("apisix.core") local t = require("lib.test_admin") local hmac = require("resty.hmac") @@ -300,19 +333,20 @@ location /t { local secret_key = "my-secret-key" local timestamp = ngx_time() + local gmt = ngx_http_time(timestamp) local access_key = "my-access-key" local custom_header_a = "asld$%dfasf" local custom_header_b = "23879fmsldfk" local signing_string = "GET" .. "/hello" .. "" .. - access_key .. timestamp .. custom_header_a .. custom_header_b + access_key .. gmt .. custom_header_a .. custom_header_b local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) core.log.info("signature:", ngx_encode_base64(signature)) local headers = {} headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) headers["X-HMAC-ALGORITHM"] = "hmac-sha256" - headers["X-HMAC-TIMESTAMP"] = timestamp + headers["Date"] = gmt headers["X-HMAC-ACCESS-KEY"] = access_key headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" headers["x-custom-header-a"] = custom_header_a @@ -338,7 +372,7 @@ passed -=== TEST 12: add consumer with 0 clock skew +=== TEST 14: add consumer with 0 clock skew --- config location /t { content_by_lua_block { @@ -386,13 +420,13 @@ passed -=== TEST 13: verify: invalid signature +=== TEST 15: verify: invalid signature --- request GET /hello --- more_headers X-HMAC-SIGNATURE: asdf X-HMAC-ALGORITHM: hmac-sha256 -X-HMAC-TIMESTAMP: 112 +Date: Thu, 24 Sep 2020 06:39:52 GMT X-HMAC-ACCESS-KEY: my-access-key3 --- error_code: 401 --- response_body @@ -402,7 +436,7 @@ X-HMAC-ACCESS-KEY: my-access-key3 -=== TEST 14: add consumer with 1 clock skew +=== TEST 16: add consumer with 1 clock skew --- config location /t { content_by_lua_block { @@ -450,11 +484,12 @@ passed -=== TEST 15: verify: invalid timestamp +=== TEST 17: verify: Invalid GMT format time --- config location /t { content_by_lua_block { local ngx_time = ngx.time + local ngx_http_time = ngx.http_time local core = require("apisix.core") local t = require("lib.test_admin") local hmac = require("resty.hmac") @@ -462,6 +497,7 @@ location /t { local secret_key = "my-secret-key2" local timestamp = ngx_time() + local gmt = ngx_http_time(timestamp) local access_key = "my-access-key2" local custom_header_a = "asld$%dfasf" local custom_header_b = "23879fmsldfk" @@ -469,14 +505,14 @@ location /t { ngx.sleep(2) local signing_string = "GET" .. "/hello" .. "" .. - access_key .. timestamp .. custom_header_a .. custom_header_b + access_key .. gmt .. custom_header_a .. custom_header_b local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) core.log.info("signature:", ngx_encode_base64(signature)) local headers = {} headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) headers["X-HMAC-ALGORITHM"] = "hmac-sha256" - headers["X-HMAC-TIMESTAMP"] = timestamp + headers["Date"] = gmt headers["X-HMAC-ACCESS-KEY"] = access_key headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" headers["x-custom-header-a"] = custom_header_a @@ -497,17 +533,18 @@ location /t { GET /t --- error_code: 401 --- response_body eval -qr/\{"message":"Invalid timestamp"\}/ +qr/\{"message":"Clock skew exceeded"\}/ --- no_error_log [error] -=== TEST 16: verify: put ok +=== TEST 18: verify: put ok --- config location /t { content_by_lua_block { - local ngx_time = ngx.time + local ngx_time = ngx.time + local ngx_http_time = ngx.http_time local core = require("apisix.core") local t = require("lib.test_admin") local hmac = require("resty.hmac") @@ -519,19 +556,20 @@ location /t { local secret_key = "my-secret-key" local timestamp = ngx_time() + local gmt = ngx_http_time(timestamp) local access_key = "my-access-key" local custom_header_a = "asld$%dfasf" local custom_header_b = "23879fmsldfk" local signing_string = "PUT" .. "/hello" .. "" .. - access_key .. timestamp .. custom_header_a .. custom_header_b + access_key .. gmt .. custom_header_a .. custom_header_b local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) core.log.info("signature:", ngx_encode_base64(signature)) local headers = {} headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) headers["X-HMAC-ALGORITHM"] = "hmac-sha256" - headers["X-HMAC-TIMESTAMP"] = timestamp + headers["Date"] = gmt headers["X-HMAC-ACCESS-KEY"] = access_key headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-b" headers["x-custom-header-a"] = custom_header_a @@ -557,11 +595,12 @@ passed -=== TEST 17: verify: put ok (pass auth data by header `Authorization`) +=== TEST 19: verify: put ok (pass auth data by header `Authorization`) --- config location /t { content_by_lua_block { local ngx_time = ngx.time + local ngx_http_time = ngx.http_time local core = require("apisix.core") local t = require("lib.test_admin") local hmac = require("resty.hmac") @@ -573,17 +612,18 @@ location /t { local secret_key = "my-secret-key" local timestamp = ngx_time() + local gmt = ngx_http_time(timestamp) local access_key = "my-access-key" local custom_header_a = "asld$%dfasf" local custom_header_b = "23879fmsldfk" local signing_string = "PUT" .. "/hello" .. "" .. - access_key .. timestamp .. custom_header_a .. custom_header_b + access_key .. gmt .. custom_header_a .. custom_header_b core.log.info("signing_string:", signing_string) local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) core.log.info("signature:", ngx_encode_base64(signature)) - local auth_string = "hmac-auth-v2#" .. access_key .. "#" .. ngx_encode_base64(signature) .. "#" .. - "hmac-sha256#" .. timestamp .. "#x-custom-header-a;x-custom-header-b" + local auth_string = "hmac-auth-v1#" .. access_key .. "#" .. ngx_encode_base64(signature) .. "#" .. + "hmac-sha256#" .. gmt .. "#x-custom-header-a;x-custom-header-b" local headers = {} headers["Authorization"] = auth_string @@ -610,7 +650,7 @@ passed -=== TEST 18: hit route without auth info +=== TEST 20: hit route without auth info --- request GET /hello --- error_code: 401 @@ -621,7 +661,7 @@ GET /hello -=== TEST 19: add consumer with signed_headers +=== TEST 21: add consumer with signed_headers --- config location /t { content_by_lua_block { @@ -647,7 +687,7 @@ GET /hello "access_key": "my-access-key5", "secret_key": "my-secret-key5", "algorithm": "hmac-sha256", - "clock_skew": 300, + "clock_skew": 0, "signed_headers": ["x-custom-header-a", "x-custom-header-b"] } } @@ -670,11 +710,12 @@ passed -=== TEST 20: verify with invalid signed header +=== TEST 22: verify with invalid signed header --- config location /t { content_by_lua_block { local ngx_time = ngx.time + local ngx_http_time = ngx.http_time local core = require("apisix.core") local t = require("lib.test_admin") local hmac = require("resty.hmac") @@ -682,19 +723,20 @@ location /t { local secret_key = "my-secret-key5" local timestamp = ngx_time() + local gmt = ngx_http_time(timestamp) local access_key = "my-access-key5" local custom_header_a = "asld$%dfasf" local custom_header_c = "23879fmsldfk" local signing_string = "GET" .. "/hello" .. "" .. - access_key .. timestamp .. custom_header_a .. custom_header_c + access_key .. gmt .. custom_header_a .. custom_header_c local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) core.log.info("signature:", ngx_encode_base64(signature)) local headers = {} headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) headers["X-HMAC-ALGORITHM"] = "hmac-sha256" - headers["X-HMAC-TIMESTAMP"] = timestamp + headers["Date"] = gmt headers["X-HMAC-ACCESS-KEY"] = access_key headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a;x-custom-header-c" headers["x-custom-header-a"] = custom_header_a @@ -721,11 +763,12 @@ qr/\{"message":"Invalid signed header x-custom-header-c"\}/ -=== TEST 21: verify ok with signed headers +=== TEST 23: verify ok with signed headers --- config location /t { content_by_lua_block { - local ngx_time = ngx.time + local ngx_time = ngx.time + local ngx_http_time = ngx.http_time local core = require("apisix.core") local t = require("lib.test_admin") local hmac = require("resty.hmac") @@ -733,18 +776,19 @@ location /t { local secret_key = "my-secret-key5" local timestamp = ngx_time() + local gmt = ngx_http_time(timestamp) local access_key = "my-access-key5" local custom_header_a = "asld$%dfasf" local signing_string = "GET" .. "/hello" .. "" .. - access_key .. timestamp .. custom_header_a + access_key .. gmt .. custom_header_a local signature = hmac:new(secret_key, hmac.ALGOS.SHA256):final(signing_string) core.log.info("signature:", ngx_encode_base64(signature)) local headers = {} headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature) headers["X-HMAC-ALGORITHM"] = "hmac-sha256" - headers["X-HMAC-TIMESTAMP"] = timestamp + headers["date"] = gmt headers["X-HMAC-ACCESS-KEY"] = access_key headers["X-HMAC-SIGNED-HEADERS"] = "x-custom-header-a" headers["x-custom-header-a"] = custom_header_a