证书吊销
当由于泄露、策略更改或关系终止而需要吊销证书时,您需要一种机制来在 mTLS 握手期间拒绝这些证书。CloudFront 提供了两种原生的证书吊销方法,您可以将它们组合起来进行分层控制。
-
OCSP(在线证书状态协议):CloudFront 实时查询证书颁发机构的 OCSP 响应者,以检查客户端证书是否已被吊销。在信任存储上启用 OCSP,CloudFront 将在 TLS 握手期间自动处理验证。OCSP 结果也显示在连接函数中,这使您能够通过编程访问吊销状态以进行自定义决策。
-
CloudFront Functions 和 KeyValueStore:您在 CloudFront KeyValueStore 中维护一份已吊销证书序列号的列表。连接函数在 TLS 握手期间查询 KeyValueStore,并允许或拒绝连接。这使您能够完全控制吊销数据、更新时间和自定义逻辑,例如宽限期或基于 IP 的异常。
| OCSP | CloudFront Functions + KeyValueStore | |
|---|---|---|
| 数据来源 | 证书颁发机构的 OCSP 响应者 | 您管理吊销列表 |
| 更新机制 | 对 CA 进行实时查询 | 您向 KeyValueStore 推送更新 |
| 自定义逻辑 | 可通过连接函数提供 | 内置在函数代码中 |
| 外部依赖项 | 需要 CA OCSP 响应者可用性 | 无外部依赖项 |
| 适用于 | 维护 OCSP 响应者的 CA;实时 CA 授权状态 | 自行管理的吊销;自定义策略;不支持 OCSP 的 CA |
而是可以同时使用这两种方法。启用 OCSP 以进行 CA 授权吊销检查,然后使用连接函数在 OCSP 结果之上叠加其它逻辑,例如,支持在宽限期内从可信 IP 范围吊销的证书。
OCSP(在线证书状态协议)
OCSP 是一种实时协议,可直接向证书颁发机构(CA)检查证书的吊销状态。在证书签名过程中,CA 会在证书中嵌入 OCSP 响应者 URL。当客户在 mTLS 握手期间出示证书时,CloudFront 向嵌入式响应者 URL 发送 OCSP 请求,并根据响应采取行动:继续实施有效的证书,终止已吊销的证书。
CloudFront 根据各自的 OCSP 响应者 URL 验证整个证书链(叶证书和最多三个中间证书)。OCSP 验证不包括信任存储中的根证书。
启用 OCSP
在信任存储上启用 OCSP。启用后,CloudFront 自动对任何在其授权信息访问(AIA)扩展中包含 OCSP 响应者 URL 的客户端证书执行 OCSP 验证。启用 OCSP 后,整个客户端证书链必须有一个 OCSP URL。如果客户端证书链中的任何证书都不包含 OCSP URL,则 CloudFront 将无法建立连接。
CloudFront 在边缘缓存 OCSP 响应,以减少往返时间并防止 OCSP 响应者停机。OCSP 响应缓存达约 30 分钟,更新后的吊销状态可能需要长达 30 分钟才能反映出来。
OCSP 导致连接函数
在同一分配上配置连接函数时,CloudFront 在 OCSP 验证完成后调用该函数。连接对象包含叶证书和中间证书的 OCSP 状态:
{ "clientCertificate": { "certificates": { "leaf": { "subject": "CN=client.example.com, O=Example Org", "issuer": "CN=Intermediate CA, O=Example Org", "serialNumber": "00:a7:30:9e:73:7b:3e:63:bd:b7:c0:7e:bf:d5:c9:86", "validity": { "notBefore": "2024-01-01T00:00:00Z", "notAfter": "2025-01-01T00:00:00Z" }, "sha256Fingerprint": "AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90", "ocspEndpoint": "http://ocsp.example.org" }, "intermediates": [ { "subject": "CN=Intermediate CA, O=Example Org", "issuer": "CN=Root CA, O=Example Org", "serialNumber": "00:a7:30:9e:73:7b:3e:63:bd:b7:c0:7e:bf:d5:c9:86", "validity": { "notBefore": "2020-01-01T00:00:00Z", "notAfter": "2030-01-01T00:00:00Z" }, "sha256Fingerprint": "12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF:12:34:56:78:90:AB:CD:EF", "ocspEndpoint": "http://ocsp.example.org" } ] }, "revocationStatus": { "chainValidity": "Valid", // "Valid" | "Invalid" | "Unknown" "certificates": { "leaf": { "method": "OCSP", // "OCSP" "status": "Good", // "Good" | "Revoked" | "Unknown" | "Error" "serialNumber": "00:a7:30:9e:73:7b:3e:63:bd:b7:c0:7e:bf:d5:c9:86" }, "intermediates": [ { "method": "OCSP", // "OCSP" "status": "Error", // "Good" | "Revoked" | "Unknown" | "Error" "errorType": "InternalError", // "InternalError" | "OCSP response verification failed: {Type}" "serialNumber": "00:a7:30:9e:73:7b:3e:63:bd:b7:c0:7e:bf:d5:c9:86" } ] } } }, "clientIp":"127.0.0.1", "endpoint":"d123.cloudfront.net", "distributionId":"E1NXS4MQZH501R", "connectionId":"xdzQ6lJUDUt8b7OuqOD8lmzOC9HcMaXPmhH5ZdzLCZpKxqzfCPpR4A==" }
chainValidity 字段可以为 Valid、Invalid 或 Unknown。单个证书 status 值可以为 Good、Revoked、Unknown 或 Error。当状态为 Error 时,errorType 字段包含 InternalError 或 OCSP response verification
failed: {Type}。
连接函数可以覆盖 OCSP 结果,例如,支持来自可信 IP 范围的已吊销证书,或者根据其它业务逻辑拒绝 OCSP 报告为“良好”的证书。
注意
仅当状态为 Error 时,errorType 字段才会出现。
示例:可信 IP 异常的自定义 OCSP 处理
function connectionHandler(connection) { var revocationStatus = connection.clientCertificate.revocationStatus; var trustedIP = (connection.clientIp === "[IP_ADDRESS]"); if (revocationStatus.chainValidity === "Invalid") { if (trustedIP) { connection.allow(); } else { connection.deny(); } } else if (revocationStatus.certificates.leaf.status === "Error") { console.log(revocationStatus.certificates.leaf.errorType); connection.deny(); } else { connection.allow(); } }
OCSP 失败行为
当 CloudFront 无法确定证书的吊销状态时(由于 OCSP 响应者无法访问、返回错误或返回“未知”),CloudFront 默认情况下将拒绝连接。要实现软失败行为,请使用连接函数。如果启用 OCSP 且客户端证书中没有 OCSP URL,则不会执行连接函数。OCSP 结果可在连接对象中提供,当 OCSP 状态未确定时,您的函数可以允许连接:
async function connectionHandler(connection) { var revocationStatus = connection.clientCertificate.revocationStatus; if (revocationStatus.certificates.leaf.status === "Error" || revocationStatus.certificates.leaf.status === "Unknown") { // OCSP responder unreachable — allow connection (soft-fail) connection.logCustomData(`OCSP_SOFT_FAIL:${revocationStatus.certificates.leaf.errorType}`); return connection.allow(); } if (revocationStatus.chainValidity === "Invalid") { return connection.deny(); } return connection.allow(); }
使用 CloudFront Functions 和 KeyValueStore 吊销证书
您可以将 CloudFront 连接函数与 KeyValueStore 结合使用来实施证书吊销检查,而无需任何外部依赖项。您在 KeyValueStore 中维护一个吊销的证书序列号列表,而连接函数在 TLS 握手期间根据此列表检查每个客户端证书。
证书吊销流程如下所述:
-
将已吊销证书的序列号存储在 CloudFront KeyValueStore 中。
-
当客户端提供证书时,系统将调用您的连接函数。
-
该函数会根据 KeyValueStore 中的数据核对证书序列号。
-
如果在存储中找到了序列号,则证书将被吊销。
-
您的函数拒绝已吊销证书的连接。
此方法可在 CloudFront 的全球边缘网络中提供近乎实时的吊销检查。
要实施此方法,您需要:
-
已配置查看器 mTLS 的分配
-
包含已吊销证书序列号的 KeyValueStore
-
一个连接函数,用于查询 KeyValueStore 以检查证书状态
当客户端连接时,CloudFront 会根据信任存储验证证书,然后运行您的连接函数。您的函数会根据 KeyValueStore 检查证书序列号,并允许或拒绝连接。
第 1 步:为已吊销证书创建 KeyValueStore
以 JSON 格式准备已吊销证书的序列号:
{ "data": [ { "key": "ABC123DEF456", "value": "" }, { "key": "789XYZ012GHI", "value": "" } ] }
将此 JSON 文件上传到 S3 存储桶,然后创建 KeyValueStore:
aws s3 cp revoked-serials.json s3://your-bucket-name/revoked-serials.json aws cloudfront create-key-value-store \ --name revoked-serials-kvs \ --import-source '{ "SourceType": "S3", "SourceARN": "arn:aws:s3:::your-bucket-name/revoked-serials.json" }'
等待 KeyValueStore 完成预调配。通过以下方式查看状态:
aws cloudfront get-key-value-store --name "revoked-serials-kvs"
第 2 步:创建吊销连接函数
创建一个连接函数,该函数会根据 KeyValueStore 中的数据检查证书序列号:
aws cloudfront create-connection-function \ --name "revocation-control" \ --connection-function-config file://connection-function-config.json \ --connection-function-code file://connection-function-code.txt
配置文件用于指定 KeyValueStore 关联:
{ "Runtime": "cloudfront-js-2.0", "Comment": "A function that implements revocation control via KVS", "KeyValueStoreAssociations": { "Quantity": 1, "Items": [ { "KeyValueStoreArn": "arn:aws:cloudfront::account-id:key-value-store/kvs-id" } ] } }
示例连接函数代码:
import cf from 'cloudfront'; async function connectionHandler(connection) { const kvsHandle = cf.kvs(); // Get client serial number from client certificate const clientSerialNumber = connection.clientCertificate.certificates.leaf.serialNumber; // Check KVS to see if serial number exists as a key // Remove : from the clientSerialNumber if KVS entries dont have it const serialNumberExistsInKvs = await kvsHandle.exists(clientSerialNumber.replaceAll(":", "")); // Deny connection if serial number exists in KVS if (serialNumberExistsInKvs) { console.log("Connection denied — certificate revoked"); connection.logCustomData("Connection denied — certificate revoked"); return connection.deny(); } // Allow connections that don't exist in KVS console.log("Connection allowed"); return connection.allow(); }
第 3 步:测试吊销函数
使用 CloudFront 控制台,通过示例证书测试您的连接函数。在控制台中导航到连接函数,然后使用“测试”选项卡。
-
将 PEM 格式的示例证书粘贴到测试界面中。
-
(可选)指定用于测试基于 IP 的逻辑的客户端 IP 地址。
-
选择测试函数以查看执行结果。
-
查看执行日志以验证函数逻辑。
使用有效证书和已吊销证书进行测试,确保您的函数可以正确处理这两种场景。
第 4 步:将函数关联到分配
在发布连接函数后,将其与已启用 mTLS 的分配相关联,以激活证书吊销检查。导航到您的分配设置,滚动到“查看器双向身份验证(mTLS)”部分,选择您的连接函数,然后保存更改。
高级吊销策略
将 OCSP 与连接函数逻辑相结合
您可以启用 OCSP 来进行 CA 授权吊销检查,并在顶部叠加连接函数以实现自定义策略。连接函数接收 OCSP 结果并可以应用其它逻辑:
-
宽限期:在证书轮换期间,允许在规定的时间内从内部网络吊销的证书。
-
紧急访问:即使 OCSP 报告已吊销状态,也允许来自特定 IP 的连接。
-
自定义拒绝逻辑:根据您自己在 KeyValueStore 中的吊销数据屏蔽 OCSP 报告为“良好”的证书。
例 OCSP + KVS 组合吊销
import cf from 'cloudfront'; async function connectionHandler(connection) { var kvsHandle = cf.kvs(); var revocationStatus = connection.clientCertificate.revocationStatus; var serialNumber = connection.clientCertificate.certificates.leaf.serialNumber; // Check your own revocation list first (immediate revocation, no cache delay) var inKvs = await kvsHandle.exists(serialNumber.replaceAll(":", "")); if (inKvs) { connection.logCustomData("KVS_REVOKED:" + serialNumber); return connection.deny(); } // Then check OCSP result if (revocationStatus.chainValidity === 'Valid' && revocationStatus.certificates.leaf.status === "Revoked") { // OCSP says revoked — allow grace period from trusted IPs if (connection.clientIp.startsWith("10.0.")) { connection.logCustomData("GRACE_PERIOD:" + serialNumber + ":" + connection.clientIp); return connection.allow(); } connection.logCustomData("OCSP_REVOKED:" + serialNumber); return connection.deny(); } connection.allow(); }
此模式可让您通过 KVS(无缓存延迟)立即吊销,以及通过 OCSP 进行 CA 授权吊销,中间有自定义异常处理。