跳到主要内容

请求安全(加密 / 签名 / 防重放)

Coco Web 在请求进入业务代码之前,提供三道可独立开关的请求安全过滤器:AES-GCM 请求解密、HMAC-SHA256 签名校验、防重放。三者都绑定 coco.web 命名空间,依赖前一章介绍的请求体缓存来重复读取原始请求体。

它们协作的典型顺序是:请求体缓存 → 解密 → 验签 → 防重放 → 业务逻辑。

AES-GCM 请求解密

功能简介

请求解密过滤器使用 AES/GCM/NoPadding 对请求体密文执行认证解密,由 AesGcmCocoRequestDecryptor 实现。GCM 是带认证的加密模式:解密时会一并校验认证标签,认证失败即视为解密失败并拒绝请求,因此密文一旦被篡改无法通过。

客户端通过请求头声明加密材料:加密标记(默认 X-Coco-Encrypted)、应用标识(X-Coco-App-Id)、密钥标识(X-Coco-Key-Id)、IV(X-Coco-IV)、算法(X-Coco-Algorithm)。框架用 appIdappId:keyId 从本地密钥映射中查找 AES 密钥,后者优先级更高。密钥、IV、密文默认按 Base64 解码。

如何启用与配置

coco.web.encryption 控制,默认启用(但需配置密钥后才会真正生效)。

coco:
web:
encryption:
enabled: true
required: false
default-algorithm: AES-GCM
gcm-tag-length-bits: 128
key-encoding: BASE64
iv-encoding: BASE64
payload-encoding: BASE64
keys:
my-app: "Base64EncodedAesKey=="
my-app:key-2024: "AnotherBase64Key=="

关键配置项

配置项默认值说明
coco.web.encryption.enabledtrue是否启用 AES 解密设施
coco.web.encryption.requiredfalse是否要求所有请求都必须加密
coco.web.encryption.default-algorithmAES-GCM请求未声明算法时的默认解密算法
coco.web.encryption.gcm-tag-length-bits128GCM 认证标签长度(bit),小于等于零时恢复默认
coco.web.encryption.key-encodingBASE64密钥文本编码
coco.web.encryption.iv-encodingBASE64IV 文本编码
coco.web.encryption.payload-encodingBASE64密文请求体文本编码
coco.web.encryption.keys本地 AES 密钥映射,键为 appIdappId:keyId
coco.web.encryption.encrypted-header-nameX-Coco-Encrypted加密标记请求头
coco.web.encryption.app-id-header-nameX-Coco-App-Id应用标识请求头
coco.web.encryption.key-id-header-nameX-Coco-Key-Id密钥标识请求头
coco.web.encryption.iv-header-nameX-Coco-IVIV 请求头
coco.web.encryption.algorithm-header-nameX-Coco-Algorithm算法请求头

HMAC-SHA256 签名校验

功能简介

签名校验过滤器用共享密钥对规范化后的请求文本计算 HMAC-SHA256,由 HmacSha256CocoSignatureVerifier 实现。它同时支持十六进制和 Base64 两种签名文本格式,并使用 MessageDigest.isEqual 做常量时间比较,避免时序侧信道泄露。

签名材料同样从请求头读取:应用标识(X-Coco-App-Id)、密钥标识(X-Coco-Key-Id)、时间戳(X-Coco-Timestamp)、随机串(X-Coco-Nonce)、签名(X-Coco-Sign,兜底 X-Coco-Signature)、算法(X-Coco-Sign-Algorithm)。密钥同样从 appIdappId:keyId 本地映射查找。

时间戳与时钟偏差

签名请求默认要求携带时间戳,并校验时间戳是否落在允许的时钟偏差窗口内(默认 300 秒)。这既能限制签名的有效期,也为防重放提供时间边界。客户端与服务端时钟差异超出 max-clock-skew-seconds 的请求会被拒绝,因此部署时应保证双方时钟同步(如 NTP)。

如何启用与配置

coco.web.signature 控制,默认启用(需配置密钥后生效)。

coco:
web:
signature:
enabled: true
required: false
timestamp-required: true
timestamp-validation-enabled: true
max-clock-skew-seconds: 300
default-algorithm: HMAC-SHA256
secrets:
my-app: "shared-secret-value"
my-app:key-2024: "another-secret"

关键配置项

配置项默认值说明
coco.web.signature.enabledtrue是否启用签名校验设施
coco.web.signature.requiredfalse是否要求所有请求必须携带签名
coco.web.signature.timestamp-requiredtrue签名请求是否必须携带时间戳
coco.web.signature.timestamp-validation-enabledtrue是否校验签名时间戳窗口
coco.web.signature.max-clock-skew-seconds300允许的客户端与服务端时间差(秒),小于等于零时恢复默认
coco.web.signature.default-algorithmHMAC-SHA256默认签名算法
coco.web.signature.secrets本地签名密钥映射,键为 appIdappId:keyId
coco.web.signature.signature-header-nameX-Coco-Sign签名请求头
coco.web.signature.signature-fallback-header-nameX-Coco-Signature签名兜底请求头
coco.web.signature.timestamp-header-nameX-Coco-Timestamp时间戳请求头
coco.web.signature.nonce-header-nameX-Coco-Nonce随机串请求头

防重放

功能简介

防重放过滤器基于请求携带的 appIdkeyId、时间戳、随机串(可选叠加 HTTP 方法和请求路径)拼装防重放键,在重放窗口内每个键只允许占用一次,从而拦截被截获后原样重放的请求。它默认保护已签名和已加密的请求。

占用状态由 CocoReplayStore 保存,框架内置两种实现:

  • 内存存储 InMemoryCocoReplayStore(默认,store-type: in-memory):进程内 ConcurrentHashMap,带后台定时清理过期键,适合单进程应用和本地开发。
  • JDBC 存储 JdbcCocoReplayStorestore-type: jdbc):使用业务项目提供的 JdbcOperations 和预建表,通过防重放键摘要(SHA-256)的唯一约束实现跨实例原子占用。框架不会创建表、迁移结构或管理数据源,表结构需业务预建(键 replay_key_hash、过期时间 expires_at_epoch_millis)。

(此外还提供基于 Spring Data Redis 的 redis 存储类型。)

集群部署边界

内存存储是进程本地的,多个实例之间互不感知,无法在集群中真正防重放。InMemoryCocoReplayStore 启动时会打印告警提示这一点。集群部署必须把 store-type 切换为 jdbc(或 redis),或自行提供共享的 CocoReplayStore 实现。

如何启用与配置

coco.web.replay 控制,默认启用。

coco:
web:
replay:
enabled: true
required: false
store-type: in-memory # in-memory / jdbc / redis
protect-signed-requests: true
protect-encrypted-requests: true
include-method: true
include-path: true
ttl-seconds: 300
cleanup-interval-seconds: 60
max-clock-skew-seconds: 300
jdbc:
table-name: coco_replay_key
redis:
key-prefix: "coco:replay:"

关键配置项

配置项默认值说明
coco.web.replay.enabledtrue是否启用防重放设施
coco.web.replay.requiredfalse是否要求所有请求都通过防重放校验
coco.web.replay.store-typein-memory存储类型:in-memory / jdbc / redis
coco.web.replay.protect-signed-requeststrue是否保护已签名请求
coco.web.replay.protect-encrypted-requeststrue是否保护已加密请求
coco.web.replay.include-methodtrue防重放键是否包含 HTTP 方法
coco.web.replay.include-pathtrue防重放键是否包含请求路径
coco.web.replay.ttl-seconds300重放窗口秒数,小于等于零时恢复默认
coco.web.replay.cleanup-interval-seconds60内存/JDBC 存储过期键清理间隔秒数
coco.web.replay.max-clock-skew-seconds300允许的客户端时间戳最大时钟偏差秒数,小于零时恢复默认
coco.web.replay.jdbc.table-namecoco_replay_keyJDBC 存储的业务预建表名(校验为合法 SQL 标识符)
coco.web.replay.redis.key-prefixcoco:replay:Redis 存储键前缀

共同的重要边界:multipart 绕过

三道过滤器都依赖请求体缓存重复读取原始请求体,而请求体缓存默认排除 multipart/(文件上传)和 application/octet-stream。这意味着文件上传请求会绕过 AES 解密、签名校验和防重放过滤器

设计原因见「Web 运行时」章:文件上传体积大、不适合整体读入内存。若业务需要对上传接口做加密、验签或防重放保护,应采用独立于请求体缓存的方案(例如对元数据而非文件体签名,或在网关侧处理)。