Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: update hmac-auth doc #8192

Merged
merged 3 commits into from
Oct 31, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions docs/en/latest/plugins/hmac-auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,15 +98,21 @@ curl http://127.0.0.1:9180/apisix/admin/routes/1 -H 'X-API-KEY: edd1c9f034335f13

The formula for calculating the signature is `signature = HMAC-SHAx-HEX(secret_key, signing_string)`.

In order to generate the signature, two parameters, `secret_key` and `signing_string` are required. The `secret_key` is configured by a Consumer and the `signing_string` is calculated as `signing_string = HTTP Method + \n + HTTP URI + \n + canonical_query_string + \n + access_key + \n + Date + \n + signed_headers_string`. If any of the terms are missing, they are replaced by an empty string. The different terms in this calculation are explained below:
In order to generate the signature, two parameters, `secret_key` and `signing_string` are required. The `secret_key` is configured by a Consumer and the `signing_string` is calculated as `signing_string = HTTP Method + \n + HTTP URI + \n + canonical_query_string + \n + access_key + \n + Date + \n + signed_headers_string`. The different terms in this calculation are explained below:

- **HTTP Method** : HTTP request method in uppercase. For example, GET, PUT, POST etc.
- **HTTP URI** : HTTP URI. Should start with "/" and "/" denotes an empty path.
- **Date** : Date in the HTTP header in GMT format.
- **canonical_query_string** : The result of encoding the query string in the URL (the string "key1 = value1 & key2 = value2" after the "?" in the URL).
- **signed_headers_string** : Concatenation of the specified request headers.

The algorithm for generating `canonical_query_string` is described below:
:::tip

If any of the terms are missing, they are replaced by an empty string.

:::

**The algorithm for generating `canonical_query_string` is described below:**

1. Extract the query terms from the URL.
2. Split the query terms into key-value pairs by using `&` as the separator.
Expand Down
101 changes: 60 additions & 41 deletions docs/zh/latest/plugins/hmac-auth.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,9 @@ curl http://127.0.0.1:9180/apisix/admin/consumers \
}'
```

<!--
你也可以通过 [APISIX Dashboard](/docs/dashboard/USER_GUIDE) 的 Web 界面完成操作。

<!--
![create a consumer](https://raw.githubusercontent.com/apache/apisix/master/docs/assets/images/plugin/hmac-auth-1.png)

![enable hmac plugin](https://raw.githubusercontent.com/apache/apisix/master/docs/assets/images/plugin/hmac-auth-2.png)
Expand All @@ -94,21 +94,29 @@ curl http://127.0.0.1:9180/apisix/admin/routes/1 \
}'
```

## 测试插件
## 签名算法详解

### 签名生成公式

需注意,在使用 `hmac-auth` 插件时,会涉及到签名。签名的计算公式为 `signature = HMAC-SHAx-HEX(secret_key, signing_string)`。
在使用 `hmac-auth` 插件时,会涉及到签名。签名的计算公式为 `signature = HMAC-SHAx-HEX(secret_key, signing_string)`。

为了生成签名需要两个参数:`secret_key` 和 `signing_string`。其中 `secret_key` 由对应 Consumer 配置,`signing_string` 的计算公式为 `signing_string = HTTP Method + \n + HTTP URI + \n + canonical_query_string + \n + access_key + \n + Date + \n + signed_headers_string`。如果 `signing_string` 中的某一项不存在,则需要使用一个空字符串代替
为了生成签名需要两个参数:`secret_key` 和 `signing_string`。其中 `secret_key` 由对应 Consumer 配置,`signing_string` 的计算公式为 `signing_string = HTTP Method + \n + HTTP URI + \n + canonical_query_string + \n + access_key + \n + Date + \n + signed_headers_string`。以下是对计算公式中各个字段的释义

- **HTTP Method**:指 HTTP 协议中定义的 GET、PUT、POST 等请求方法,必须使用全大写的形式。
- **HTTP URI**:HTTP URI。必须以 “/” 开头,“/” 表示空路径。
- **HTTP URI**:HTTP URI。必须以 `/` 开头,`/` 表示空路径。
- **Date**:请求头中的日期(GMT 格式)。
- **canonical_query_string**:对 URL 中的 query(query 即 URL 中 `?` 后面的 `key1=valve1&key2=valve2` 字符串)进行编码后的结果。
- **signed_headers_string**:从请求头中获取客户端指定的字段,并按顺序拼接字符串的结果。

> 生成 `canonical_query_string` 的算法描述如下:
:::tip 提示

- 如果 `signing_string` 中的任意一项不存在,则需要使用一个空字符串代替。

- 由于签名计算时,会区分大小写字母,在使用时,请规范其参数命名。

:::

**生成 `canonical_query_string` 的算法描述如下:**

1. 提取 URL 中的 query 项。
2. 使用 `&` 作为分隔符,将 query 拆分成键值对。
Expand All @@ -124,7 +132,7 @@ curl http://127.0.0.1:9180/apisix/admin/routes/1 \
- 当该项同时有 `key` 和 `value` 时,转换公式为 `key + "=" + value` 。此处 `value` 可以是空字符串。
- 将每一项转换后,以 `key` 按照字典顺序(ASCII 码由小到大)排序,并使用 `&` 符号连接起来,生成相应的 `canonical_query_string`。

> 生成 `signed_headers_string` 的算法如下:
**生成 `signed_headers_string` 的算法如下:**

1. 从请求头中获取指定的 headers 加入计算中。
2. 从请求头中按顺序取出 `SIGNED_HEADERS` 指定的 headers,并按顺序用 `name:value` 方式拼接起来,拼接完后就生成了 `signed_headers_string`。
Expand All @@ -136,7 +144,9 @@ HeaderKey2 + ":" + HeaderValue2 + "\n"\+
HeaderKeyN + ":" + HeaderValueN + "\n"
```

以下示例为你展示了签名字符串的拼接:
### 签名生成公式过程详解

接下来,我们将以下述请求为例,为你介绍签名生成公式的具体计算过程:

```shell
curl -i http://127.0.0.1:9080/index.html?name=james&age=36 \
Expand All @@ -145,30 +155,28 @@ curl -i http://127.0.0.1:9080/index.html?name=james&age=36 \
-H "User-Agent: curl/7.29.0"
```

### 签名生成公式过程详解

1. 上文请求默认的 HTTP Method 是 GET,得到 `signing_string` 为
1. 上文请求默认的 HTTP Method 是 GET,得到 `signing_string` 为:

```plain
"GET"
```

2. 请求的 URI 是 `/index.html`,根据 HTTP Method + \n + HTTP URI 得到 `signing_string` 为
2. 请求的 URI 是 `/index.html`,根据 HTTP Method + \n + HTTP URI 得到 `signing_string` 为

```plain
"GET
/index.html"
```

3. URL 中的 query 项是 `name=james&age=36`,假设 `encode_uri_params` 为 false,根据 `canonical_query_string` 的算法,重点是对 `key` 进行字典排序,得到 `age=36&name=james`;根据 HTTP Method + \n + HTTP URI + \n + canonical_query_string 得到 `signing_string` 为
3. URL 中的 query 项是 `name=james&age=36`,假设 `encode_uri_params` 为 false,根据 `canonical_query_string` 的算法,重点是对 `key` 进行字典排序,得到 `age=36&name=james`;根据 HTTP Method + \n + HTTP URI + \n + canonical_query_string 得到 `signing_string` 为

```plain
"GET
/index.html
age=36&name=james"
```

4. access_key 是 `user-key`,根据 HTTP Method + \n + HTTP URI + \n + canonical_query_string + \n + access_key 得到 `signing_string` 为
4. access_key 是 `user-key`,根据 HTTP Method + \n + HTTP URI + \n + canonical_query_string + \n + access_key 得到 `signing_string` 为

```plain
"GET
Expand All @@ -177,7 +185,7 @@ age=36&name=james
user-key"
```

5. Date 是指 GMT 格式的日期,形如 `Tue, 19 Jan 2021 11:33:20 GMT`, 根据 HTTP Method + \n + HTTP URI + \n + canonical_query_string + \n + access_key + \n + Date 得到 `signing_string` 为
5. Date 是指 GMT 格式的日期,形如 `Tue, 19 Jan 2021 11:33:20 GMT`, 根据 HTTP Method + \n + HTTP URI + \n + canonical_query_string + \n + access_key + \n + Date 得到 `signing_string` 为

```plain
"GET
Expand All @@ -189,7 +197,7 @@ Tue, 19 Jan 2021 11:33:20 GMT"

6. `signed_headers_string` 用来制定参与到签名的 headers,在上面示例中包括 `User-Agent: curl/7.29.0` 和 `x-custom-a: test`。

根据 HTTP Method + \n + HTTP URI + \n + canonical_query_string + \n + access_key + \n + Date + \n + signed_headers_string + `\n`,得到完整的 `signing_string` 为
根据 HTTP Method + \n + HTTP URI + \n + canonical_query_string + \n + access_key + \n + Date + \n + signed_headers_string + `\n`,得到完整的 `signing_string` 为

```plain
"GET
Expand All @@ -202,7 +210,36 @@ x-custom-a:test
"
```

以下示例是通过使用 Python 来生成签名 `SIGNATURE`:
### Body 校验

当 `validate_request_body` 设置为 `true` 时,插件将计算请求 body 的 `hmac-sha` 值,并与请求 headers 中的 `X-HMAC-DIGEST` 的值进行校验。

```
X-HMAC-DIGEST: base64(hmac-sha(<body>))
```

如果没有请求 body,你可以将 `X-HMAC-DIGEST` 的值设置为空字符串的 HMAC-SHA。

:::note 注意

当开启 body 校验时,为了计算请求 body 的 `hmac-sha` 值,该插件会把 body 加载到内存中,在请求 body 较大的情况下,可能会造成较高的内存消耗。

为了避免这种情况,你可以通过设置 `max_req_body`(默认值是 512KB)配置项来配置最大允许的 body 大小,body 超过此大小的请求会被拒绝。

:::

## 测试插件

假设当前请求为:

```shell
curl -i http://127.0.0.1:9080/index.html?name=james&age=36 \
-H "X-HMAC-SIGNED-HEADERS: User-Agent;x-custom-a" \
-H "x-custom-a: test" \
-H "User-Agent: curl/7.29.0"
```

通过以下 Python 代码为上述请求生成签名 `SIGNATURE`:

```python
import base64
Expand All @@ -229,27 +266,9 @@ print(base64.b64encode(hash.digest()))
| --------- | -------------------------------------------- |
| SIGNATURE | 8XV1GB7Tq23OJcoz6wjqTs4ZLxr9DiLoY4PxzScWGYg= |

您也可以参考 [Generating HMAC signatures](../examples/plugins-hmac-auth-generate-signature.md) 了解如何为不同的编程语言生成签名。

### Body 校验

当 `validate_request_body` 设置为 `true` 时,插件将计算请求 body 的 `hmac-sha` 值,并与请求 headers 中的 `X-HMAC-DIGEST` 的值进行校验。

```
X-HMAC-DIGEST: base64(hmac-sha(<body>))
```

如果没有请求 body,你可以将 `X-HMAC-DIGEST` 的值设置为空字符串的 HMAC-SHA。

:::note

当开启 body 校验时,为了计算请求 body 的 `hmac-sha` 值,插件会把 body 加载到内存中,在请求 body 较大的情况下,可能会造成较高的内存消耗。为了避免这种情况,你可以通过设置 `max_req_body`(默认值是 512KB)配置项来配置最大允许的 body 大小,body 超过此大小的请求会被拒绝。

:::

### 使用生成好的签名进行请求尝试
你也可以参考 [Generating HMAC signatures](../examples/plugins-hmac-auth-generate-signature.md) 了解如何为不同的编程语言生成签名。

你可以通过以下示例使用生成的签名发起请求:
签名生成后,你可以通过以下示例使用生成的签名发起请求:

```shell
curl -i "http://127.0.0.1:9080/index.html?name=james&age=36" \
Expand All @@ -272,7 +291,7 @@ Server: APISIX/2.2
......
```

签名可以放到请求头 `Authorization` 字段中:
你也可以将签名放到请求头 `Authorization` 字段中:

```shell
curl http://127.0.0.1:9080/index.html \
Expand All @@ -291,7 +310,7 @@ Accept-Ranges: bytes
...
```

也可以将签名单独放在另一个请求头中
还可以将签名单独放在另一个请求头中

```shell
curl http://127.0.0.1:9080/index.html \
Expand All @@ -313,15 +332,15 @@ Accept-Ranges: bytes
<html lang="cn">
```

:::note
:::note 注意

1. ACCESS_KEY、SIGNATURE、ALGORITHM、DATE、SIGNED_HEADERS 分别代表对应的变量。
2. SIGNED_HEADERS 为客户端指定的加入加密计算的 headers。若存在多个 headers 需以 “;” 分割,例如:`x-custom-header-a;x-custom-header-b`。
3. SIGNATURE 需要使用 base64 进行加密:`base64_encode(SIGNATURE)`。

:::

## 自定义 header 名称
### 自定义 header 名称

除了配置签名外,你还可以在配置文件(`conf/config.yaml`)中的`plugin_attr` 配置项下,添加 `hmac-auth` 插件的属性来自定义参数 header 名称。如下所示:

Expand Down