关于 reviewing-cryptography
用于代码审查的密码学技能,聚焦加密、认证、密钥、令牌、密码、随机数和 TLS 的误用。它提供具体检查步骤,并要求审查结论说明可导致的攻击及明确修正方案。
主要功能
- 01认证加密与随机数检查
- 02密钥管理和轮换审查
- 03密码哈希与随机性检查
- 04恒定时间比较审查
- 05JWT、签名和 TLS 验证
- 06协议层误用检查清单
适用场景
- 01审查加解密代码
- 02检查令牌和 API 签名方案
- 03评估 TLS 与证书验证
- 04检查密码和秘密存储
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ trilwu/ secskills --skill reviewing-cryptography安装后在 Claude Code 中输入/reviewing-cryptography确认已加载。
详细介绍
审查密码学
几乎没有真正的系统是被密码分析攻破的。它们通常是因误用而遭到破坏: 重复使用 nonce、未认证的密文、会提前返回的比较操作、提交到 git 的密钥。 审查误用问题,并将原语设计留给密码学家。
适用场景
- 审计执行加密、解密、签名、验证或哈希的代码
- 审查密钥管理、轮换和存储
- 评估 TLS/mTLS 配置和证书验证
- 审查 JWT、会话令牌和 API 签名方案
- 检查密码和秘密的存储方式
- 评估安全相关值的随机性质量
不适用场景
- 设计新的原语或协议 — 这需要密码学家进行正式审查,而非代码审计
- 在 CTF 中破解密码学 — 这是不同的专业领域;请使用面向
solving的进攻型技能和已知攻击工具 - 常规代码审查 — 请使用
auditing-code-for-vulnerabilities - 密码破解 — 请使用
cracking-passwords
误用检查清单
按顺序逐项检查。每一项都曾发现过真实的生产环境故障。
1. 密文是否经过认证?
未进行认证的加密是最常见的严重问题。如果 CBC 或 CTR 没有 MAC,攻击者就能修改明文;如果还存在解密预言机,则能恢复明文(填充预言机)。
Good: AES-GCM, ChaCha20-Poly1305, AES-CBC + HMAC (encrypt-then-MAC)
Bad: AES-CBC alone, AES-ECB (ever), CTR without a MAC, MAC-then-encrypt
rg -n 'AES/ECB|AES\.MODE_ECB|CipherMode\.ECB|"AES"\)' -i
rg -n 'AES/CBC/PKCS5Padding|MODE_CBC|createCipheriv\(.*cbc' -i
如果看到 CBC,就查找 MAC。如果没有 MAC,无论密文如何传输,这都属于问题。
2. Nonce 和 IV 的处理
| 模式 | 规则 | 后果 |
|---|---|---|
| GCM / ChaCha20-Poly1305 | 绝不重复使用同一对(密钥,nonce) | 灾难性后果:泄露认证密钥,伪造变得轻而易举 |
| CBC | 每条消息的 IV 必须不可预测且随机 | 选择明文攻击(BEAST 类) |
| CTR | 同一密钥下绝不重复使用计数器 | 密钥流重复使用;明文异或结果 |
# The classic bug: a fixed or zero IV
rg -n 'iv\s*=\s*(b?["\x27]0|new byte\[\d+\]|bytes\(\d+\)|\[0\]\s*\*)' -i
rg -n 'IvParameterSpec\(new byte\[16\]\)|createCipheriv\([^,]+,[^,]+,\s*["\x27]'
GCM 使用随机 96 位 nonce 时,每个密钥处理约 2^32 条消息以内是安全的。 基于计数器的 nonce 更安全,但前提是计数器状态确实能在重启后保留,且不会在多个实例间重复。询问计数器存储在哪里;如果答案是“在内存中”,再加上水平扩展,就意味着会重复使用。
3. 密钥管理
- 密钥从哪里来?硬编码、环境变量、KMS/HSM,还是派生而来?
- 检查所有这些位置:硬编码的密钥、源码中的密钥、提交到仓库的配置文件中的密钥、容器镜像中的密钥、CI 日志中的密钥。
- 密钥是否只用于单一用途?在加密和签名之间,或不同租户之间重复使用密钥,都是问题。
- 是否存在密钥轮换途径?无法轮换的系统,在密钥泄露时便无从应对。
- 派生密钥:是否使用了合适的 KDF(密钥材料使用 HKDF,密码使用 Argon2id/scrypt/PBKDF2)?对密码直接计算 SHA-256 并不属于 KDF。
rg -n 'BEGIN (RSA |EC |OPENSSH )?PRIVATE KEY|-----BEGIN'
rg -n '(secret|api[_-]?key|password|token)\s*[:=]\s*["\x27][A-Za-z0-9/+=]{16,}' -i
gitleaks detect --source . --redact # history matters more than the tree
4. 密码存储
Correct: Argon2id (preferred), scrypt, bcrypt, PBKDF2-HMAC-SHA256 with a
high iteration count — each with a per-user random salt
Wrong: MD5, SHA-1, SHA-256, SHA-512 (raw or salted), any unsalted hash,
encryption instead of hashing, a "pepper" as the only defense
请依据当前指南检查工作因子,而不是沿用代码编写时认为足够的值。还要检查验证流程是否使用了库提供的恒定时间验证函数,而不是直接比较字符串。
5. 随机性
安全相关值——令牌、会话 ID、nonce、盐值、密码重置码、IV——必须由 CSPRNG 生成。
rg -n 'math/rand|Math\.random\(\)|random\.random\(|rand\(\)|mt_rand|Random\(\)'
# Correct: crypto/rand, secrets.token_bytes, window.crypto.getRandomValues,
# SecureRandom, os.urandom, RandomNumberGenerator
还要检查:使用时间戳或 PID 作为种子、将基于弱随机源生成的 UUIDv1/v4 用作秘密,以及在需要不可猜测性时使用可预测的连续 ID。
6. 计时侧信道
任何涉及秘密的比较都必须采用恒定时间操作:MAC、令牌、API 签名、密码哈希、OTP。
rg -n 'hmac.*==|token\s*==|signature\s*==|\.equals\(.*(hmac|token|sig)' -i
# Correct: hmac.compare_digest, crypto.timingSafeEqual, subtle.ConstantTimeCompare,
# MessageDigest.isEqual, hash_equals
对 HMAC 进行会提前返回的字符串比较属于实际可行的远程攻击,并非理论上的问题。
7. 签名验证
- 是否真正验证了签名,还是仅仅解析了签名?
- 算法是否取自消息?(JWT
alg混淆:none,以及 RS256→HS256,此时公钥会变成 HMAC 密钥。) - 密钥是否通过攻击者可控的标识符(
kid、jku、x5u)选取?这些字段由攻击者提供;应据此处理。 - 签名验证后是否验证了声明:
exp、nbf、iss、aud,以及——至关重要的是——主体当前的授权状态?
rg -n 'jwt\.decode\(|verify\s*[:=]\s*(False|false)|algorithms\s*=\s*\[?["\x27]?none' -i
rg -n 'InsecureSkipVerify|verify\s*=\s*False|CURLOPT_SSL_VERIFYPEER.*0|rejectUnauthorized:\s*false'
8. TLS 配置
# Server side
testssl.sh --severity MEDIUM https://target
sslyze --regular target:443
nmap --script ssl-enum-ciphers -p 443 target
# Look for: TLS < 1.2, RC4/3DES/NULL/EXPORT ciphers, no forward secrecy,
# weak DH params, expired or misissued certs, missing HSTS
客户端侧出错的情况比服务端侧更常见。请检查是否启用了证书验证、是否开启了主机名验证(在多个库中,它与证书链验证是分开的),以及自定义信任存储是否会在不知情的情况下接受所有证书。自定义 TrustManager 若返回时不抛出异常,就是 Java 中“完全没有 TLS”的惯用写法。
9. 后量子防护状况
对于机密性需要长期保障的内容,请指出“先窃取、后解密”的风险,以及当前技术栈是否支持混合密钥交换(例如 X25519 + ML-KEM)。这属于路线图发现,通常并非紧急问题——但请明确说明,而不是将其省略。
协议层面的问题
除基础原语外,还应询问:
- 重放:是否使用了 nonce、时间戳或序列号?是否确实进行了检查和存储?
- 绑定:签名是否绑定到包含接收方和上下文在内的整条消息?签名剥离和跨协议复用都源于签名范围过窄。
- 降级:客户端或服务端能否协商到较弱的模式?
- 预言机:错误处理是否会区分“填充错误”和“MAC 错误”,或区分解密失败和授权失败?任何可观察到的差异——包括时序和响应大小——都可能构成预言机。
- 规范化:如果签名覆盖的是序列化结构,同一结构是否可能以两种方式序列化?JSON 和 XML 签名方案经常因此出错。
应驳回的托词
- “它经过加密了。” 加密不等于经过身份验证,也不等于获得授权。
- “我们使用 AES-256,所以很安全。” 密钥长度几乎从来不是薄弱环节。
- “IV 足够随机。” 请说明它在哪里生成,以及使用什么方式生成。
- “通过网络进行时序攻击并不现实。” 这类攻击是现实存在的,而且已在互联网上被反复演示。
- “因为库用起来不方便,所以我们自己实现了加密。” 自定义加密本身就是一项发现。请指出应该改用哪个库。
- “密钥放在环境变量中,而不是代码里。” 这样更好,但还需检查环境变量在哪里设置、谁能读取进程环境,以及它是否会出现在日志、崩溃转储或容器映像中。
- “证书固定太麻烦了。” 可以——但应将其说明为已接受的风险,而不是禁用验证。
- “这是内部流量。” 横向移动就发生在内部网络中。
交付内容
对于每项发现,请说明:涉及的原语或协议、具体的不当用法、由此可实施的具体攻击(而不是泛泛地说“加密较弱”)、受影响的数据及其机密性期限,以及明确的正确构造方式——包括具体库、模式和参数。仅建议“使用强加密”的加密问题,无法得到修复。
阅读外部来源
以 Markdown 格式获取公开公告、规范和供应商报告:
curl -sL "https://defuddle.md/<url>" # scheme in the path is optional
这会移除页面冗余内容——对于以正文为主的页面,令牌数大约减少 78%——并返回全文而非摘要,因此你可以搜索内容,并相信搜索无结果的结论。
它不适用于三种情况。获取 JSON 和 API 响应时应保留原始格式,因为可读性提取会破坏结构化数据。获取需要身份验证或由 JavaScript 渲染的页面时,应直接获取,因为它会匿名检索。切勿通过它访问对手基础设施(钓鱼链接、C2、恶意软件托管)、客户端拥有的主机或测试任务 URL——请求会离开你的机器并发往第三方;对于活动中的对手基础设施,这还会向操作者暴露你的访问行为。
有些网站会拦截提取器并返回错误信息,而非页面内容——例如 freedesktop.org 返回的 {"error":"Failed to fetch: 418 I'm a teapot"}。这表示获取请求被拒绝,并不表示来源声称该内容不存在。得出任何结论前,请直接重新获取该 URL。
参考资料
auditing-code-for-vulnerabilities— 相关的代码审查cracking-passwords— 弱密码存储的攻击侧testing-apis— API 层的令牌和签名处理- 推荐的库:libsodium/NaCl、Google Tink、
age、平台 AEAD API testssl.sh、sslyze、cryptography(Python)审计 API、cargo-crev