使用 Cloudflare API 配置 JWT 验证,这需要令牌配置和令牌验证规则。
令牌配置定义了 JSON Web 密钥集 (JWKs),用于验证客户端发送的 JSON Web 令牌 (JWT) 以及关于这些 JWT 在请求中发送位置的信息。
令牌配置需要以下信息:
| 字段名称 | 描述 | 示例 | 备注 |
|---|---|---|---|
title |
配置的人类可读名称,允许您快速识别配置的目的。 | Production JWT configuration | 限制为 50 个字符。 |
description |
比 title 给出更多细节的人类可读描述,用作允许客户更好记录配置用途的手段。 |
This configuration is used for all endpoints in endpoint management and checks the JWT in the authorization header. | 限制为 500 个字符。 |
token_sources |
请求中可能包含 JWT 的位置列表。 | http.request.headers[\"authorization\"][0] http.request.cookies[\"Authorization\"][0] |
请参阅下面的信息。 |
token_type |
指定要验证的令牌类型。 | jwt |
目前仅支持 jwt。 |
credentials |
描述应该用于验证 JWT 的加密公钥。此字段必须是 JSON Web 密钥。 | 请参阅下面的示例。 | 请参阅下面的信息。 |
每一项都必须是解析为字符串的规则集引擎 (Ruleset Engine) 表达式。
目前支持的字段是 http.request.headers 和 http.request.cookies。
您最多可以设置四个令牌来源。如果请求设置了这些字段中的多个,则只会使用一个。请求令牌中前导的 Bearer: 字符串会被自动忽略。
有关使用规则集引擎字段的详细信息,请参阅规则集引擎文档。
API Shield 支持 RS256、RS384、RS512、PS256、PS384、PS512、ES256 和 ES384 类型的凭据。RSA 密钥必须至少为 2048 位。每个 JSON Web 密钥都必须有一个 “KID”,该 “KID” 也必须存在于 JWT 的标头中,以允许 API Shield 对其进行匹配。
我们允许最多 4 个不同的密钥,以帮助进行密钥轮换。
Cloudflare 将从每个密钥中删除任何不必要的字段,并将放弃我们不支持的密钥。
强烈建议验证 API 调用的输出,以检查结果密钥是否符合预期。
下面的示例展示了一个 JSON 对象,其中包含使用 Cloudflare API 创建令牌配置所需的所有信息。如果您想创建用于测试的 JWKs,请参阅 mkjwk JSON Web Key Generator ↗。
{
"title": "Production JWT configuration",
"description": "This configuration checks the JWT in the authorization header or cookie.",
"token_sources": [
"http.request.headers[\"authorization\"][0]",
"http.request.cookies[\"Authorization\"][0]"
],
"token_type": "jwt",
"credentials": {
"keys": [
{
"kty": "EC",
"use": "sig",
"crv": "P-256",
"kid": "93UrzmNu1mqXs5cZcvCPkTlMHB2Jya30vSTkiBb0vhU",
"x": "QG3VFVwUX4IatQvBy7sqBvvmticCZ-eX5-nbtGKBOfI",
"y": "A3PXCshn7XcG7Ivvd2K_DerW4LHAlIVKdqhrUnczTD0",
"alg": "ES256"
}
]
}
}使用 cURL 或任何其他 API 客户端工具将新配置发送到 Cloudflare 的 API 以启用 JWT 验证。确保将 {zone_id} 替换为相关的区域 ID,并添加您的身份验证凭据标头。
curl "https://api.cloudflare.com/client/v4/zones/{zone_id}/token_validation/config" \
--header 'Content-Type: application/json' \
--data '{
"title": "Production JWT configuration",
"description": "This configuration checks the JWT in the authorization header or cookie.",
"token_sources": [
"http.request.headers[\"authorization\"][0]",
"http.request.cookies[\"Authorization\"][0]"
],
"token_type": "jwt",
"credentials": {
"keys": [
{
"kty": "EC",
"use": "sig",
"crv": "P-256",
"kid": "93UrzmNu1mqXs5cZcvCPkTlMHB2Jya30vSTkiBb0vhU",
"x": "QG3VFVwUX4IatQvBy7sqBvvmticCZ-eX5-nbtGKBOfI",
"y": "A3PXCshn7XcG7Ivvd2K_DerW4LHAlIVKdqhrUnczTD0",
"alg": "ES256"
}
]
}
}'响应将包含在 Cloudflare v4 响应信封中,其结果包含已创建的配置。请注意返回的 ID,因为在使用 API 创建令牌验证规则时,它将用于引用令牌配置。
{
"result": {
"id": "d5902294-00c3-4aed-b517-57e752e9cd58",
"token_type": "JWT",
"title": "Production JWT configuration",
"description": "This configuration checks the JWT in the authorization header or cookie.",
"token_sources": [
"http.request.headers[\"authorization\"][0]",
"http.request.cookies[\"Authorization\"][0]"
],
"credentials": {
"keys": [
{
"x": "QG3VFVwUX4IatQvBy7sqBvvmticCZ-eX5-nbtGKBOfI",
"y": "A3PXCshn7XcG7Ivvd2K_DerW4LHAlIVKdqhrUnczTD0",
"alg": "ES256",
"crv": "P-256",
"kid": "93UrzmNu1mqXs5cZcvCPkTlMHB2Jya30vSTkiBb0vhU",
"kty": "EC"
}
]
},
"created_at": "2023-11-08T16:45:17.236841Z",
"last_updated": "2023-11-08T16:45:17.236841Z"
},
"success": true,
"errors": [],
"messages": []
}令牌验证规则允许您使用现有令牌配置来强制执行安全策略。
令牌验证规则可以使用 Cloudflare API 或仪表板进行配置。
| 字段名称 | 描述 | 示例 | 备注 |
|---|---|---|---|
title |
一个人类可读的名称,允许您快速识别它。 | JWT validation on v1 and v2.example.com |
限制为 50 个字符。 |
description |
比 title 给出更多细节的人类可读描述,有助于记录它。 |
Log requests without a valid authorization header. |
限制为 500 个字符。 |
action |
对不符合 expression 的请求采取的防火墙操作。 |
log |
可能的值:log 或 block |
enabled |
启用或禁用该规则。 | true |
可能的值:true 或 false |
expression |
规则的安全策略。 | is_jwt_valid ("00170473-ec24-410e-968a-9905cf0a7d03") |
使用 Cloudflare API 创建规则时,请确保对任何引号进行转义。 请参阅下面的定义安全策略。 |
selector |
配置此规则涵盖哪些操作。 | 请参阅下面的将规则应用于操作。 |
选择器控制您的令牌验证规则的范围。
如果您只需要在顶级域的特定主机名或子域上进行 JWT 验证,请在选择器中使用该主机名将其包含在 JWT 验证规则中。
如果您需要将永远不会使用有效 JWT 的端点排除在 JWT 验证之外(根据设计),例如最初用于建立有效 JWT 的路径和方法,您必须使用端点的操作 ID 在选择器中排除该端点。
要找到操作 ID,请参阅端点管理或使用 Cloudflare API。
令牌验证规则的表达式定义了请求必须满足的安全策略。
例如,如果传入请求不包含至少一个有效的身份验证令牌,表达式 is_jwt_valid("51231d16-01f1-48e3-93f8-91c99e81288e") or is_jwt_valid("51231d16-01f1-48e3-93f8-91c99e81288e") 将被触发。
这些表达式类似于规则集引擎中使用的表达式,但有一些关键区别:
- 与规则集表达式相反,如果表达式的评估结果为
false,则会触发令牌验证规则操作。 - 令牌验证规则可以使用引用令牌配置的专用函数。
运算符(如 or、and、eq 等)在表达式中的使用方式与规则集引擎中使用的表达式相同。
以下函数可用于与请求中的 JWT 令牌进行交互:
is_jwt_valid(token_configuration_id)— 如果请求根据 ID 为token_configuration_id的令牌配置具有有效的令牌,则返回 true。is_jwt_present(token_configuration_id)— 如果请求包含 ID 为token_configuration_id的令牌配置中所配置的令牌,则返回 true。
请参阅以下示例用例以了解要使用哪种安全策略。对于大多数用例,Cloudflare 建议在您的 API 中要求提供有效的令牌,并使用选择器排除任何用于建立或刷新令牌的路径。
如果请求缺少 JWT,表达式 is_jwt_present("51231d16-01f1-48e3-93f8-91c99e81288e") 将触发操作。
它可以与令牌验证规则中的 log 操作结合使用,以记录缺少身份验证标头的请求。
如果请求没有有效的 JWT,表达式 is_jwt_valid("51231d16-01f1-48e3-93f8-91c99e81288e") 将触发操作。
它可以与令牌验证规则中的 block 操作结合使用,以阻止没有凭据或凭据无效的请求。
如果请求没有至少一个有效的令牌,表达式 is_jwt_valid("51231d16-01f1-48e3-93f8-91c99e81288e") or is_jwt_valid("fddfc39e-3686-4683-ab23-bf917da6bb43") 将触发操作。
如果您需要将 JWKs 拆分为多个令牌配置,可能会发生这种情况。
如果请求包含无效令牌,表达式 is_jwt_valid("51231d16-01f1-48e3-93f8-91c99e81288e") or not is_jwt_present("51231d16-01f1-48e3-93f8-91c99e81288e") 将触发操作,从而忽略完全没有令牌的请求。
一个操作只能应用一个令牌验证规则。如果一个操作被多个规则覆盖,则优先级最高的规则将生效。
您可以使用 selector 字段配置对哪些操作强制执行 JWT 验证。
例如,以下选择器将把规则应用于 v1.example.com 和 v2.example.com 中的所有操作,但这些主机上的两个操作除外:
{
"include": [
{
"host": ["v1.example.com", "v2.example.com"]
}
],
"exclude": [
{
"operation_ids": [
"f9c5615e-fe15-48ce-bec6-cfc1946f1bec", // POST v1.example.com/login
"56828eae-035a-4396-ba07-51c66d680a04" // POST v2.example.com/login
]
}
]
}操作可以在主机级别包含,并在每项操作的基础上忽略。
您可以使用 POST /zones/{zone_id}/token_validation/rules/preview 端点查看此规则所涵盖的操作:
curl --request PUT \
'https://api.cloudflare.com/client/v4/zones/{zone_id}/token_validation/rules/preview' \
--header 'Content-Type: application/json' \
--data '{
"include": [
{
"host": [
"v1.example.com",
"v2.example.com"
]
}
],
"exclude": [
{
"operation_ids": [
"f9c5615e-fe15-48ce-bec6-cfc1946f1bec", // POST v1.example.com/login
"56828eae-035a-4396-ba07-51c66d680a04" // POST v2.example.com/login
]
}
]
}'响应将包含区域上的所有操作,并带有一个附加的 state 字段。
state 字段可以是 ignored、excluded 或 included。Included 操作将匹配您指定的主机名选择器。Excluded 操作将匹配您在选择器中指定的操作 ID。Ignored 操作是那些与选择器中指定的任何内容都不匹配的操作。
{
"result": {
"operations": [
{
"operation_id": "ed15fcb6-5a73-41cd-91af-8c61e5bb1cdb",
"method": "GET",
"host": "example.com",
"endpoint": "/api/accounts/{var1}",
"last_updated": "2023-05-24T14:54:34.806506Z",
"state": "ignored"
},
{
"operation_id": "e7a582cd-3cfb-4061-ab5b-722e6e42f545",
"method": "GET",
"host": "v1.example.com",
"endpoint": "/api/accounts/{var1}",
"last_updated": "2023-05-24T14:54:34.806506Z",
"state": "included"
},
{
"operation_id": "ddd5df5a-795c-40ce-b38c-38e9d7ef9ae8",
"method": "GET",
"host": "v2.example.com",
"endpoint": "/api/accounts/{var1}",
"last_updated": "2023-05-24T14:54:34.806506Z",
"state": "included"
},
{
"operation_id": "4d20befb-0120-45d5-9b29-5835fd41b44e",
"method": "GET",
"host": "v3.example.com",
"endpoint": "/api/accounts/{var1}",
"last_updated": "2023-05-24T14:54:34.806506Z",
"state": "ignored"
},
{
"operation_id": "f9c5615e-fe15-48ce-bec6-cfc1946f1bec",
"method": "POST",
"host": "v1.example.com",
"endpoint": "/login",
"last_updated": "2023-05-24T14:54:34.806506Z",
"state": "excluded"
},
{
"operation_id": "56828eae-035a-4396-ba07-51c66d680a04",
"method": "POST",
"host": "v2.example.com",
"endpoint": "/login",
"last_updated": "2023-05-24T14:54:34.806506Z",
"state": "excluded"
},
{
"operation_id": "cf86874c-8d0c-4337-ae14-4e2459b541ac",
"method": "GET",
"host": "v3.example.com",
"endpoint": "login",
"last_updated": "2023-05-24T14:54:34.806506Z",
"state": "ignored"
}
],
"total": 7,
"included": 2,
"excluded": 2,
"ignored": 3,
"selected_hosts": ["v1.example.com", "v2.example.com"],
"available_hosts": [
"example.com",
"v1.example.com",
"v1.example.com",
"v3.example.com"
]
},
"success": true,
"errors": [],
"messages": [],
"result_info": {
"page": 1,
"per_page": 20,
"count": 20,
"total_count": 1631
}
}state 为 included 的操作将被令牌验证规则覆盖。该响应还在 result.selected_hosts 中显示包含的主机名,并在 result.available_hosts 中显示所有区域操作使用的所有主机名。
您也可以在请求正文中发送一个空对象:
curl --request PUT \
'https://api.cloudflare.com/client/v4/zones/{zone_id}/token_validation/rules/preview' \
--header 'Content-Type: application/json' \
--data '{ }'该响应将显示所有区域操作和所有可能的主机,您可以使用它们来构建自己的选择器。
下面的示例展示了一个 JSON 对象,其中包含使用 Cloudflare API 创建令牌验证规则所需的所有必要信息。
将任何令牌配置 ID 和操作 ID 替换为您的区域中存在的 ID。
[
{
"title": "JWT Validation on v1 and v2.example.com",
"description": "Log requests without a valid authorization header.",
"action": "log",
"enabled": true,
"expression": "is_jwt_valid(\"00170473-ec24-410e-968a-9905cf0a7d03\")",
"selector": {
"include": [
{
"host": ["v1.example.com", "v2.example.com"]
}
],
"exclude": [
{
"operation_ids": [
"f9c5615e-fe15-48ce-bec6-cfc1946f1bec",
"56828eae-035a-4396-ba07-51c66d680a04"
]
}
]
}
}
]使用 cURL 或任何其他 API 客户端工具将新配置发送到 Cloudflare 的 API 以启用 JWT 验证。确保将 {zone_id} 替换为相关的区域 ID,并添加您的身份验证凭据标头。
将任何令牌配置 ID 和操作 ID 替换为您的区域中存在的 ID。
单次请求可以创建多个规则。为此,请在请求正文的 JSON 数组中传递多个规则对象。
curl "https://api.cloudflare.com/client/v4/zones/{zone_id}/token_validation/rules/bulk" \
--header 'Content-Type: application/json' \
--data '[
{
"title": "JWT Validation on v1 and v2.example.com",
"description": "Log requests without a valid authorization header.",
"action": "log",
"enabled": true,
"expression": "is_jwt_valid(\"00170473-ec24-410e-968a-9905cf0a7d03\")",
"selector": {
"include": [
{
"host": [
"v1.example.com",
"v2.example.com"
]
}
],
"exclude": [
{
"operation_ids": [
"f9c5615e-fe15-48ce-bec6-cfc1946f1bec",
"56828eae-035a-4396-ba07-51c66d680a04"
]
}
]
}
]
}'响应将包含在 Cloudflare v4 响应信封中,其结果包含已创建的规则。请注意每个规则返回的 ID,它可以用于编辑或删除现有规则。
{
"result": [
{
"id": "5ec7c417-6964-4b24-b82c-a23a7ec8f90c",
"title": "JWT Validation on v1 and v2.example.com",
"description": "Log requests without a valid authorization header.",
"action": "log",
"enabled": true,
"expression": "is_jwt_valid(\"00170473-ec24-410e-968a-9905cf0a7d03\")",
"selector": {
"include": [
{
"host": ["v1.example.com", "v2.example.com"]
}
],
"exclude": [
{
"operation_ids": [
"f9c5615e-fe15-48ce-bec6-cfc1946f1bec",
"56828eae-035a-4396-ba07-51c66d680a04"
]
}
]
},
"created_at": "2023-10-18T12:08:09.575388Z",
"last_updated": "2023-10-18T12:08:09.575388Z",
"modified_by": "user@cloudflare.com"
}
],
"success": true,
"errors": [],
"messages": []
}最佳做法是在一段时间后轮换密钥。为了支持更新密钥,Cloudflare 允许每个配置最多包含四个密钥。这允许您将第二个新密钥添加到已经存在的密钥中。您可以开始仅使用新密钥签发 JWT,并在一段时间后删除旧密钥。此外,此功能允许在生产密钥旁边部署测试或开发密钥。
更新密钥的输入与创建配置时提供初始密钥相同,即需要是一个 JWK。
使用 PUT 命令更新密钥。
curl --request PUT \
'https://api.cloudflare.com/client/v4/zones/{zone_id}/token_validation/config/{config_id}/credentials' \
--header 'Content-Type: application/json' \
--data '{
"keys": [
{
"kty": "EC",
"use": "sig",
"kid": "test",
"x": "-0LNzBheJPn-Zy6JmanTIUX7xc3jgqU714IQY0oU6mw",
"y": "KONxBybUcRsJQmtu17jMAHsILSw009AuU3ulfUGv3FI",
"alg": "ES256"
},
{
"kty": "EC",
"crv": "P-256",
"kid": "test-2",
"x": "iIbPRbOeLzjGPvv7iwmzCOTU03R0xDqbenp2D6GUcWo",
"y": "tDkEh95PnfWwIXciCtdBBVA7wfghx_egmZ1Zcvu2lWw",
"alg": "ES256"
}
]
}'确保将 {zone_id} 替换为相关的区域 ID,并添加您的身份验证凭据标头。
可以使用 PATCH 请求更新令牌验证规则。单个 PATCH 请求可以更新多个规则。
PATCH 请求在请求正文中被指定为 JSON 数组。该数组中的每一项都包含对单个规则(由 id 定义)的更新。
以下示例更新了一个规则并禁用了另一个规则:
curl --request PATCH \
"https://api.cloudflare.com/client/v4/zones/{zone_id}/token_validation/rules/bulk" \
--header "Content-Type: application/json" \
--data '[
{
"id": "714d3dd0-cc59-4911-862f-8a27e22353cc",
"action": "log",
"title": "updated title"
},
{
"id": "7124f9bc-d6b5-430d-b488-b6bc2892f2fb",
"enabled": false
}
]'规则可以通过在 PATCH 正文中设置 position 字段来重新排序。
此示例将规则 714d3dd0-cc59-4911-862f-8a27e22353cc 放置在规则 7124f9bc-d6b5-430d-b488-b6bc2892f2fb 之后:
curl --request PATCH \
"https://api.cloudflare.com/client/v4/zones/{zone_id}/token_validation/rules/bulk" \
--header 'Content-Type: application/json' \
--data '[
{
"id": "714d3dd0-cc59-4911-862f-8a27e22353cc",
"position": {
"after": "7124f9bc-d6b5-430d-b488-b6bc2892f2fb"
}
}
]'此示例将规则 714d3dd0-cc59-4911-862f-8a27e22353cc 放置在规则 7124f9bc-d6b5-430d-b488-b6bc2892f2fb 之前:
curl --request PATCH \
"https://api.cloudflare.com/client/v4/zones/{zone_id}/token_validation/rules/bulk" \
--header 'Content-Type: application/json' \
--data '[
{
"id": "714d3dd0-cc59-4911-862f-8a27e22353cc",
"position": {
"before": "7124f9bc-d6b5-430d-b488-b6bc2892f2fb"
}
}
]'以下是 JWT 验证如何处理传入请求的概述:
- 我们根据传入请求的配置提取 JWT。
- 我们解码 JWT 并查找 JWT 标头的 KID 声明。
- 我们使用 KID 和 ALG 声明在提供的密钥列表中查找正确的密钥。
- 我们通过使用所选密钥检查签名来验证 JWT 的真实性。
- 如果 JWT 包含 EXP 声明(过期时间),我们将验证 JWT 是否未过期。
- 如果 JWT 包含 NBF 声明(生效时间),我们将验证 JWT 是否已经生效。
-
最终的验证结果以及是否完全存在令牌将提供给 WAF,WAF 会应用策略配置的操作 (
log/block)。 -
Cloudflare 仪表板中针对
API Shield - Token Validation服务的安全分析 (Security Analytics) 事件将在事件的Token validation violations部分中说明违规原因。