Skip to content

签名验证实现缺陷防御(恒定时间比对 / 严格类型校验 / Fail-Closed) ​

防御目标 ​

修补跨语言签名验证逻辑中的常见实现缺陷,包括 PHP == / strcmp() / md5(array) 类型戏法、JavaScript === 缺失与 undefined 比较、Java Signature.verify 异常吞没以及逐字节早停比对导致的时序侧信道泄露。

跨语言缺陷与加固对照表 ​

语言 / 场景易错实现攻击后果强制加固方案
通用字节/字符串比较sig == expected 或 memcmp宽松类型转换(Magic Hash 0e...)或时序盲注恢复字节Python hmac.compare_digest、PHP hash_equals、Node crypto.timingSafeEqual、Java MessageDigest.isEqual
参数类型未校验传入 sign[]=x、sign=null、{"sign": true}strcmp(Array, str) == NULL == 0 或 md5(Array) === NULL 绕过入口强制断言 isinstance(sig, str) / is_string($sig),非标准长度 hex/base64 直接拒绝
空签名 / 缺失字段if sign: verify(...) 或默认初始值相同不传 sign 参数直接跳过校验逻辑先强制存在性校验:缺少签名字段直接 return False
异常处理try { verify(); } catch (e) { log(e); } 后继续执行构造畸形 Base64/ASN.1 触发异常跳过验签Fail-Closed:verified = False 初始化,仅在所有断言成功通过后显式置为 True
大整数 / ASN.1 签名ECDSA r=0, s=0 或 RSA Bleichenbacher e=3 伪造Java/旧版库未拒绝零值签名或忽略 PKCS#1 v1.5 填充尾部垃圾使用维护期内的标准密码库(如 cryptography / BoringSSL),拒绝非规范 ASN.1 编码

防御实现与校验示例 ​

1. Fail-Closed 恒定时间验签封装(Python) ​

python
import hmac
import hashlib
import re

HEX64_RE = re.compile(r"^[0-9a-f]{64}$")

def safe_verify_hmac_sha256(secret: bytes, message: bytes, client_sig: object) -> bool:
    verified = False
    try:
        if not isinstance(secret, (bytes, bytearray)) or len(secret) < 32:
            return False
        if not isinstance(message, (bytes, bytearray)):
            return False
        # 严格类型与定长格式校验,防止 list/dict/None/空串 或 timingSafeEqual 长度抛错泄露
        if not isinstance(client_sig, str):
            return False
        normalized = client_sig.strip().lower()
        if not HEX64_RE.fullmatch(normalized):
            return False

        expected = hmac.new(secret, message, hashlib.sha256).hexdigest()
        verified = hmac.compare_digest(normalized, expected)
    except Exception:
        return False
    return verified

if __name__ == "__main__":
    sec = b"secret-key-at-least-32-bytes-long"
    msg = b"amount=100&order=42"
    good = hmac.new(sec, msg, hashlib.sha256).hexdigest()
    assert safe_verify_hmac_sha256(sec, msg, good) is True
    assert safe_verify_hmac_sha256(sec, msg, None) is False
    assert safe_verify_hmac_sha256(sec, msg, ["array_bypass"]) is False
    assert safe_verify_hmac_sha256(sec, msg, "0e123456") is False
    assert safe_verify_hmac_sha256(sec, msg, "") is False
    print("implementation_defense_check: PASS")

2. 多语言推荐比对函数清单 ​

text
- Python:     hmac.compare_digest(a_bytes, b_bytes)
- PHP:        is_string($sig) && hash_equals($expected, $sig)
- Node.js:    typeof sig === 'string' && sig.length === expected.length && crypto.timingSafeEqual(...)
- Go:         subtle.ConstantTimeCompare([]byte(a), []byte(b)) == 1
- Java:       MessageDigest.isEqual(aBytes, bBytes)

防御工作流 ​

text
1. 静态代码排查:全局搜索源码中的 ==、!=、strcmp、memcmp、String.equals 与空 catch 块
2. 强类型入口守卫:在验签函数首行强制检验签名类型(必须为非空字符串)与精确长度匹配
3. 替换恒定时间原语:所有 MAC/摘要比对统一替换为语言标准库提供的恒定时间比对函数
4. 强制 Fail-Closed:确保验签函数默认返回 False,任何解析或解码异常都终止业务推进
5. 并发与边界测试:压测数组、布尔值、null、畸形编码与单字节时间差用例

验证与判定标准 ​

  • 类型混淆免疫:向接口提交 sign[]=a、sign=true、sign=null、sign=0e215962017 均返回 False 且不触发 500 内部异常穿透
  • 时序无泄露:对首字节正确与首字节错误的签名各采样 10,000 次,耗时分布无显著统计差异
  • 异常安全:传入截断或非法字符签名时,函数稳定返回 False

MCP 工具映射 ​

防御环节MCP 工具用途
关联缺陷模式检索kb_router检索其他语言或签名场景的实现加固要点
验签单测套件生成workspace_write_text输出多边界类型混淆与 Fail-Closed 单元测试
异常请求特征规则make_sigma_stub生成探测类型污染(sign[]=)和异常编码探针的检测规则

Apache-2.0 · For authorized learning and defensive research only · Disclaimer