mirror of
https://gitee.com/iresty/apisix.git
synced 2024-12-05 21:47:36 +08:00
fix(hmac-auth): when the request contains escape characters, the signature verification fails (#2749)
fix #2710
This commit is contained in:
parent
8983a8dd27
commit
a6678c33c5
@ -74,6 +74,11 @@ local consumer_schema = {
|
||||
type = "boolean",
|
||||
title = "whether to keep the http request header",
|
||||
default = false,
|
||||
},
|
||||
encode_uri_params = {
|
||||
type = "boolean",
|
||||
title = "Whether to escape the uri parameter",
|
||||
default = true,
|
||||
}
|
||||
},
|
||||
required = {"access_key", "secret_key"},
|
||||
@ -175,6 +180,21 @@ local function get_consumer(access_key)
|
||||
end
|
||||
|
||||
|
||||
local function get_conf_field(access_key, field_name)
|
||||
local consumer, err = get_consumer(access_key)
|
||||
if err then
|
||||
return false, err
|
||||
end
|
||||
|
||||
return consumer.auth_conf[field_name]
|
||||
end
|
||||
|
||||
|
||||
local function do_nothing(v)
|
||||
return v
|
||||
end
|
||||
|
||||
|
||||
local function generate_signature(ctx, secret_key, params)
|
||||
local canonical_uri = ctx.var.uri
|
||||
local canonical_query_string = ""
|
||||
@ -194,14 +214,23 @@ local function generate_signature(ctx, secret_key, params)
|
||||
end
|
||||
core.table.sort(keys)
|
||||
|
||||
local field_val = get_conf_field(params.access_key, "encode_uri_params")
|
||||
core.log.info("encode_uri_params: ", field_val)
|
||||
|
||||
local encode_or_not = do_nothing
|
||||
if field_val then
|
||||
encode_or_not = escape_uri
|
||||
end
|
||||
|
||||
for _, key in pairs(keys) do
|
||||
local param = args[key]
|
||||
-- whether to encode the uri parameters
|
||||
if type(param) == "table" then
|
||||
for _, val in pairs(param) do
|
||||
core.table.insert(query_tab, escape_uri(key) .. "=" .. escape_uri(val))
|
||||
core.table.insert(query_tab, encode_or_not(key) .. "=" .. encode_or_not(val))
|
||||
end
|
||||
else
|
||||
core.table.insert(query_tab, escape_uri(key) .. "=" .. escape_uri(param))
|
||||
core.table.insert(query_tab, encode_or_not(key) .. "=" .. encode_or_not(param))
|
||||
end
|
||||
end
|
||||
canonical_query_string = core.table.concat(query_tab, "&")
|
||||
@ -296,16 +325,6 @@ local function validate(ctx, params)
|
||||
end
|
||||
|
||||
|
||||
local function get_keep_headers(access_key)
|
||||
local consumer, err = get_consumer(access_key)
|
||||
if err then
|
||||
return false, err
|
||||
end
|
||||
|
||||
return consumer.auth_conf.keep_headers
|
||||
end
|
||||
|
||||
|
||||
local function get_params(ctx)
|
||||
local params = {}
|
||||
local local_conf = core.config.local_conf()
|
||||
@ -359,7 +378,7 @@ local function get_params(ctx)
|
||||
params.date = date or ""
|
||||
params.signed_headers = signed_headers and ngx_re.split(signed_headers, ";")
|
||||
|
||||
local keep_headers = get_keep_headers(params.access_key)
|
||||
local keep_headers = get_conf_field(params.access_key, "keep_headers")
|
||||
core.log.info("keep_headers: ", keep_headers)
|
||||
|
||||
if not keep_headers then
|
||||
|
@ -44,6 +44,7 @@ The `consumer` then adds its key to request header to verify its request.
|
||||
| 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 |
|
||||
| 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. |
|
||||
|
||||
## How To Enable
|
||||
|
||||
@ -64,6 +65,8 @@ curl http://127.0.0.1:9080/apisix/admin/consumers -H 'X-API-KEY: edd1c9f034335f1
|
||||
}'
|
||||
```
|
||||
|
||||
The default `keep_headers` is false and `encode_uri_params` is true.
|
||||
|
||||
2. add a Route or add a Service, and enable the `hmac-auth` plugin
|
||||
|
||||
```shell
|
||||
@ -94,13 +97,20 @@ The calculation formula of the signature is `signature = HMAC-SHAx-HEX(secret_ke
|
||||
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:
|
||||
> The coding steps of canonical_query_string are as follows:
|
||||
|
||||
* Extract the `query` item in the URL, that is, the string "key1 = valve1 & key2 = valve2" after the "?" in the URL.
|
||||
* Split the `query` into several items according to the & separator, each item is in the form of key=value or only key.
|
||||
* Encoding each item after disassembly is divided into the following two situations.
|
||||
* When the item has only key, the conversion formula is UriEncode(key) + "=".
|
||||
* When the item is in the form of key=value, the conversion formula is in the form of UriEncode(key) + "=" + UriEncode(value). Here value can be an empty string.
|
||||
* According to whether the uri parameter is encoded, there are two situations:
|
||||
* When `encode_uri_params` is true:
|
||||
* Encoding each item after disassembly is divided into the following two situations.
|
||||
* When the item has only key, the conversion formula is uri_encode(key) + "=".
|
||||
* When the item is in the form of key=value, the conversion formula is in the form of uri_encode(key) + "=" + uri_encode(value). Here value can be an empty string.
|
||||
* After converting each item, sort by key in lexicographic order (ASCII code from small to large), and connect them with the & symbol to generate the corresponding canonical_query_string.
|
||||
* When `encode_uri_params` is false:
|
||||
* Encoding each item after disassembly is divided into the following two situations.
|
||||
* When the item has only key, the conversion formula is key + "=".
|
||||
* When the item is in the form of key=value, the conversion formula is in the form of key + "=" + value. Here value can be an empty string.
|
||||
* After converting each item, sort by key in lexicographic order (ASCII code from small to large), and connect them with the & symbol to generate the corresponding canonical_query_string.
|
||||
|
||||
> The signed_headers_string generation steps are as follows:
|
||||
|
@ -21,12 +21,12 @@
|
||||
|
||||
# 目录
|
||||
|
||||
- [**名字**](#名字)
|
||||
- [**属性**](#属性)
|
||||
- [**如何启用**](#如何启用)
|
||||
- [**测试插件**](#测试插件)
|
||||
- [**禁用插件**](#禁用插件)
|
||||
- [**签名生成示例**](#签名生成示例)
|
||||
- [**名字**](#名字)
|
||||
- [**属性**](#属性)
|
||||
- [**如何启用**](#如何启用)
|
||||
- [**测试插件**](#测试插件)
|
||||
- [**禁用插件**](#禁用插件)
|
||||
- [**签名生成示例**](#签名生成示例)
|
||||
|
||||
## 名字
|
||||
|
||||
@ -44,6 +44,7 @@
|
||||
| clock_skew | integer | 可选 | 0 | | 签名允许的时间偏移,以秒为单位的计时。比如允许时间偏移 10 秒钟,那么就应设置为 `10`。特别地,`0` 表示不对 `Date` 进行检查。 |
|
||||
| signed_headers | array[string] | 可选 | | | 限制加入加密计算的 headers ,指定后客户端请求只能在此范围内指定 headers ,此项为空时将把所有客户端请求指定的 headers 加入加密计算。如: ["User-Agent", "Accept-Language", "x-custom-a"] |
|
||||
| keep_headers | boolean | 可选 | false | [ true, false ] | 认证成功后的 http 请求中是否需要保留 `X-HMAC-SIGNATURE`、`X-HMAC-ALGORITHM` 和 `X-HMAC-SIGNED-HEADERS` 的请求头。true: 表示保留 http 请求头,false: 表示移除 http 请求头。 |
|
||||
| encode_uri_param | boolean | 可选 | true | [ true, false ] | 是否对签名中的 uri 参数进行编码,例如: `params1=hello%2Cworld` 进行了编码,`params2=hello,world` 没有进行编码。true: 表示对签名中的 uri 参数进行编码,false: 不对签名中的 uri 参数编码。 |
|
||||
|
||||
## 如何启用
|
||||
|
||||
@ -64,6 +65,8 @@ curl http://127.0.0.1:9080/apisix/admin/consumers -H 'X-API-KEY: edd1c9f034335f1
|
||||
}'
|
||||
```
|
||||
|
||||
默认 `keep_headers` 为 false,`encode_uri_param` 为 true。
|
||||
|
||||
2. 创建 Route 或 Service 对象,并开启 `hmac-auth` 插件。
|
||||
|
||||
```shell
|
||||
@ -98,10 +101,17 @@ curl http://127.0.0.1:9080/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f13
|
||||
|
||||
* 提取 URL 中的 query 项,即 URL 中 ? 后面的 key1=valve1&key2=valve2 字符串。
|
||||
* 将 query 根据&分隔符拆开成若干项,每一项是 key=value 或者只有 key 的形式。
|
||||
* 对拆开后的每一项进行编码处理,分以下两种情况:
|
||||
* 根据 uri 参数是否编码,有下面两种情况:
|
||||
* `encode_uri_param` 为 true 时:
|
||||
* 对拆开后的每一项进行编码处理,分以下两种情况:
|
||||
* 当该项只有 key 时,转换公式为 url_encode(key) + "=" 的形式。
|
||||
* 当该项是 key=value 的形式时,转换公式为 url_encode(key) + "=" + url_encode(value) 的形式。这里 value 可以是空字符串。
|
||||
* 将每一项转换后,以 key 按照字典顺序( ASCII 码由小到大)排序,并使用 & 符号连接起来,生成相应的 canonical_query_string 。
|
||||
* `encode_uri_param` 为 false 时:
|
||||
* 对拆开后的每一项进行编码处理,分以下两种情况:
|
||||
* 当该项只有 key 时,转换公式为 key + "=" 的形式。
|
||||
* 当该项是 key=value 的形式时,转换公式为 key + "=" + value 的形式。这里 value 可以是空字符串。
|
||||
* 将每一项转换后,以 key 按照字典顺序( ASCII 码由小到大)排序,并使用 & 符号连接起来,生成相应的 canonical_query_string 。
|
||||
|
||||
> signed_headers_string 生成步骤如下:
|
||||
|
||||
|
@ -1272,7 +1272,7 @@ GET /apisix/admin/schema/plugins/hmac-auth
|
||||
--- request
|
||||
GET /apisix/admin/schema/plugins/hmac-auth?schema_type=consumer
|
||||
--- response_body
|
||||
{"title":"work with consumer object","additionalProperties":false,"required":["access_key","secret_key"],"properties":{"clock_skew":{"default":0,"type":"integer"},"keep_headers":{"title":"whether to keep the http request header","default":false,"type":"boolean"},"secret_key":{"minLength":1,"maxLength":256,"type":"string"},"algorithm":{"type":"string","default":"hmac-sha256","enum":["hmac-sha1","hmac-sha256","hmac-sha512"]},"signed_headers":{"items":{"minLength":1,"maxLength":50,"type":"string"},"type":"array"},"access_key":{"minLength":1,"maxLength":256,"type":"string"}},"type":"object"}
|
||||
{"title":"work with consumer object","additionalProperties":false,"required":["access_key","secret_key"],"properties":{"clock_skew":{"default":0,"type":"integer"},"encode_uri_params":{"title":"Whether to escape the uri parameter","default":true,"type":"boolean"},"keep_headers":{"title":"whether to keep the http request header","default":false,"type":"boolean"},"secret_key":{"minLength":1,"maxLength":256,"type":"string"},"algorithm":{"type":"string","default":"hmac-sha256","enum":["hmac-sha1","hmac-sha256","hmac-sha512"]},"signed_headers":{"items":{"minLength":1,"maxLength":50,"type":"string"},"type":"array"},"access_key":{"minLength":1,"maxLength":256,"type":"string"}},"type":"object"}
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
@ -1285,3 +1285,351 @@ GET /apisix/admin/schema/plugins/hmac-auth?schema_type=consumer123123
|
||||
{"properties":{"disable":{"type":"boolean"}},"title":"work with route or service object","additionalProperties":false,"type":"object"}
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 35: enable hmac auth plugin using admin api
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local t = require("lib.test_admin").test
|
||||
local code, body = t('/apisix/admin/routes/1',
|
||||
ngx.HTTP_PUT,
|
||||
[[{
|
||||
"plugins": {
|
||||
"hmac-auth": {}
|
||||
},
|
||||
"upstream": {
|
||||
"nodes": {
|
||||
"127.0.0.1:1980": 1
|
||||
},
|
||||
"type": "roundrobin"
|
||||
},
|
||||
"uri": "/hello"
|
||||
}]]
|
||||
)
|
||||
|
||||
if code >= 300 then
|
||||
ngx.status = code
|
||||
end
|
||||
ngx.say(body)
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 36: encode_uri_params field is true, the signature of uri enables escaping
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local t = require("lib.test_admin").test
|
||||
local code, body = t('/apisix/admin/consumers',
|
||||
ngx.HTTP_PUT,
|
||||
[[{
|
||||
"username": "james",
|
||||
"plugins": {
|
||||
"hmac-auth": {
|
||||
"access_key": "my-access-key6",
|
||||
"secret_key": "my-secret-key6"
|
||||
}
|
||||
}
|
||||
}]]
|
||||
)
|
||||
|
||||
if code >= 300 then
|
||||
ngx.status = code
|
||||
end
|
||||
ngx.say(body)
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 37: verify: invalid signature (Lowercase letters of escape characters are converted to uppercase.)
|
||||
--- 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")
|
||||
local ngx_encode_base64 = ngx.encode_base64
|
||||
|
||||
local secret_key = "my-secret-key6"
|
||||
local timestamp = ngx_time()
|
||||
local gmt = ngx_http_time(timestamp)
|
||||
local access_key = "my-access-key6"
|
||||
local custom_header_a = "asld$%dfasf"
|
||||
local custom_header_b = "23879fmsldfk"
|
||||
|
||||
local signing_string = {
|
||||
"GET",
|
||||
"/hello",
|
||||
"name=LeBron%2Cjames&name2=%2c%3e",
|
||||
access_key,
|
||||
gmt,
|
||||
"x-custom-header-a:" .. custom_header_a,
|
||||
"x-custom-header-b:" .. custom_header_b
|
||||
}
|
||||
signing_string = core.table.concat(signing_string, "\n") .. "\n"
|
||||
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 headers = {}
|
||||
headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature)
|
||||
headers["X-HMAC-ALGORITHM"] = "hmac-sha256"
|
||||
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
|
||||
headers["x-custom-header-b"] = custom_header_b
|
||||
|
||||
local code, body = t.test('/hello?name=LeBron%2Cjames&name2=%2c%3e',
|
||||
ngx.HTTP_GET,
|
||||
"",
|
||||
nil,
|
||||
headers
|
||||
)
|
||||
|
||||
ngx.status = code
|
||||
ngx.say(body)
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- error_code: 401
|
||||
--- response_body eval
|
||||
qr/\{"message":"Invalid signature"\}/
|
||||
--- error_log eval
|
||||
qr/name=LeBron\%2Cjames\&name2=\%2C\%3E/
|
||||
|
||||
|
||||
|
||||
=== TEST 38: verify: ok (The letters in the escape character are all uppercase.)
|
||||
--- 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")
|
||||
local ngx_encode_base64 = ngx.encode_base64
|
||||
|
||||
local secret_key = "my-secret-key6"
|
||||
local timestamp = ngx_time()
|
||||
local gmt = ngx_http_time(timestamp)
|
||||
local access_key = "my-access-key6"
|
||||
local custom_header_a = "asld$%dfasf"
|
||||
local custom_header_b = "23879fmsldfk"
|
||||
|
||||
local signing_string = {
|
||||
"GET",
|
||||
"/hello",
|
||||
"name=LeBron%2Cjames&name2=%2C%3E",
|
||||
access_key,
|
||||
gmt,
|
||||
"x-custom-header-a:" .. custom_header_a,
|
||||
"x-custom-header-b:" .. custom_header_b
|
||||
}
|
||||
signing_string = core.table.concat(signing_string, "\n") .. "\n"
|
||||
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 headers = {}
|
||||
headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature)
|
||||
headers["X-HMAC-ALGORITHM"] = "hmac-sha256"
|
||||
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
|
||||
headers["x-custom-header-b"] = custom_header_b
|
||||
|
||||
local code, body = t.test('/hello?name=LeBron%2Cjames&name2=%2C%3E',
|
||||
ngx.HTTP_GET,
|
||||
"",
|
||||
nil,
|
||||
headers
|
||||
)
|
||||
|
||||
ngx.status = code
|
||||
ngx.say(body)
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- no_error_log
|
||||
|
||||
|
||||
|
||||
=== TEST 39: encode_uri_params field is false, uri’s signature is enabled for escaping
|
||||
--- config
|
||||
location /t {
|
||||
content_by_lua_block {
|
||||
local t = require("lib.test_admin").test
|
||||
local code, body = t('/apisix/admin/consumers',
|
||||
ngx.HTTP_PUT,
|
||||
[[{
|
||||
"username": "james",
|
||||
"plugins": {
|
||||
"hmac-auth": {
|
||||
"access_key": "my-access-key6",
|
||||
"secret_key": "my-secret-key6",
|
||||
"encode_uri_params": false
|
||||
}
|
||||
}
|
||||
}]]
|
||||
)
|
||||
|
||||
if code >= 300 then
|
||||
ngx.status = code
|
||||
end
|
||||
ngx.say(body)
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 40: verify: invalid signature (uri’s signature is enabled for escaping)
|
||||
--- 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")
|
||||
local ngx_encode_base64 = ngx.encode_base64
|
||||
|
||||
local secret_key = "my-secret-key6"
|
||||
local timestamp = ngx_time()
|
||||
local gmt = ngx_http_time(timestamp)
|
||||
local access_key = "my-access-key6"
|
||||
local custom_header_a = "asld$%dfasf"
|
||||
local custom_header_b = "23879fmsldfk"
|
||||
|
||||
local signing_string = {
|
||||
"GET",
|
||||
"/hello",
|
||||
"name=LeBron%2Cjames&name2=%2c%3e",
|
||||
access_key,
|
||||
gmt,
|
||||
"x-custom-header-a:" .. custom_header_a,
|
||||
"x-custom-header-b:" .. custom_header_b
|
||||
}
|
||||
signing_string = core.table.concat(signing_string, "\n") .. "\n"
|
||||
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 headers = {}
|
||||
headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature)
|
||||
headers["X-HMAC-ALGORITHM"] = "hmac-sha256"
|
||||
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
|
||||
headers["x-custom-header-b"] = custom_header_b
|
||||
|
||||
local code, body = t.test('/hello?name=LeBron%2Cjames&name2=%2c%3e',
|
||||
ngx.HTTP_GET,
|
||||
"",
|
||||
nil,
|
||||
headers
|
||||
)
|
||||
|
||||
ngx.status = code
|
||||
ngx.say(body)
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- error_code: 401
|
||||
--- response_body eval
|
||||
qr/\{"message":"Invalid signature"\}/
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
||||
|
||||
|
||||
=== TEST 41: 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")
|
||||
local ngx_encode_base64 = ngx.encode_base64
|
||||
|
||||
local secret_key = "my-secret-key6"
|
||||
local timestamp = ngx_time()
|
||||
local gmt = ngx_http_time(timestamp)
|
||||
local access_key = "my-access-key6"
|
||||
local custom_header_a = "asld$%dfasf"
|
||||
local custom_header_b = "23879fmsldfk"
|
||||
|
||||
local signing_string = {
|
||||
"GET",
|
||||
"/hello",
|
||||
"name=LeBron,james&name2=,>",
|
||||
access_key,
|
||||
gmt,
|
||||
"x-custom-header-a:" .. custom_header_a,
|
||||
"x-custom-header-b:" .. custom_header_b
|
||||
}
|
||||
signing_string = core.table.concat(signing_string, "\n") .. "\n"
|
||||
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 headers = {}
|
||||
headers["X-HMAC-SIGNATURE"] = ngx_encode_base64(signature)
|
||||
headers["X-HMAC-ALGORITHM"] = "hmac-sha256"
|
||||
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
|
||||
headers["x-custom-header-b"] = custom_header_b
|
||||
|
||||
local code, body = t.test('/hello?name=LeBron%2Cjames&name2=%2c%3e',
|
||||
ngx.HTTP_GET,
|
||||
"",
|
||||
nil,
|
||||
headers
|
||||
)
|
||||
|
||||
ngx.status = code
|
||||
ngx.say(body)
|
||||
}
|
||||
}
|
||||
--- request
|
||||
GET /t
|
||||
--- response_body
|
||||
passed
|
||||
--- no_error_log
|
||||
[error]
|
||||
|
Loading…
Reference in New Issue
Block a user