mcpField
C
crypto-audit

crypto-audit

github.com/briiirussell安全478367 次安装1 天前更新
代码仓库

关于 crypto-audit

crypto-audit 是用于审查应用代码与配置中密码学实现的安全技能,关注算法、参数、密钥与 nonce 处理、签名、随机数、TLS 及密钥生命周期中的细微问题。

主要功能

  1. 01检查算法与加密模式
  2. 02核查密钥派生和 nonce 处理
  3. 03检查认证加密与签名
  4. 04评估随机数和 TLS 设置
  5. 05审查密钥生命周期及框架模式
  6. 06记录已验证问题与加固建议

适用场景

  1. 01审查应用的加密方案
  2. 02检查密码哈希与密钥派生
  3. 03复核 JWT 和 webhook 验证
  4. 04评估 TLS 与密钥管理配置

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/briiirussell/cybersecurity-skills --skill crypto-audit

安装后在 Claude Code 中输入/crypto-audit确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

references/
findings-format.md
findings.schema.json
severity-calibration.md
to-sarif.mjs
validate-findings.mjs
verification.md

详细介绍

Crypto Audit — 密码学实现审查

审查应用中的密码学实现方式,包括算法选择、参数、模式,以及可能让优秀密码学原语变成脆弱系统的实现模式。审查深度超过 owasp-audit A04 Cryptographic Failures(它会捕获明显的“MD5 密码”和“VERIFY_NONE”问题)。此技能用于审查更隐蔽的实现问题。

大多数密码学故障并不是“用了 MD5”。更多时候,问题在于:原语正确,但模式错误(用了 ECB 而不是 GCM);算法正确,但参数错误(2026 年仍用 1,000 次迭代的 PBKDF2);库正确,但调用顺序错误(数据加载后才初始化密码);等等。

交叉参考:owasp-audit A04(基线)+ A07(时序安全比较)、secrets-audit(密钥存储)、iam-audit(KMS / HSM 模式)。

授权

审查用户拥有或获准审计的代码、配置和密钥管理设置。TLS 探测(testssl.sh、openssl s_client)属于实时检查:仅可对用户获准测试的端点运行;此操作未预先批准,因此每次都会提示。绝不要尝试恢复密钥、破解哈希,或解密你无权访问的数据。

不要自行开发

对于任何自定义加密方案,默认审计结论都是“改用 libsodium / Tink / WebCrypto”。全世界只有不到 50 人能够安全地设计新的密码学方案,而他们不会在你的公司工作。除非明确的威胁模型另有说明,否则自定义密码学就是一项发现。

审计检查清单

算法和模式
  • 对称加密: AES-256-GCM 或 ChaCha20-Poly1305(认证加密——在一个原语中同时实现机密性和完整性)
  • 拒绝使用: AES-ECB(泄露块模式——相同明文 → 相同密文)、不带 HMAC 的 AES-CBC(未经认证;存在填充预言攻击)、不带 HMAC 的 AES-CTR(可篡改;位翻转 = 明文翻转)、DES / 3DES、RC4、Blowfish(改用 Twofish,或者完全弃用)
  • 非对称加密: 签名和密钥交换使用 Ed25519 / X25519;若兼容性要求使用 RSA,则使用 3072 位及以上的 RSA-OAEP / RSA-PSS;加密时绝不使用带 PKCS#1 v1.5 填充的 RSA(Bleichenbacher);绝不使用原始 RSA
  • 哈希(通用): SHA-256、SHA-3、BLAKE2 / BLAKE3
  • 哈希(密码)——截然不同的问题: Argon2id、scrypt、bcrypt(bcrypt 的成本 ≥ 12;OWASP 2024 下限)
  • MAC: 至少使用 HMAC-SHA256;绝不使用 CBC-MAC;绝不自制 hash(key + message)
  • 搜索:MD5、SHA1(旧版兼容场景中的 HMAC-SHA1 除外)、DES、RC4、Blowfish、AES.*ECB、pkcs1_v1_5(Python)、未使用 OAEP 的 RSA.encrypt
密钥派生
  • 从密码派生: Argon2id(抗内存优化攻击),或迭代次数 ≥ 600,000 的 PBKDF2-HMAC-SHA256(OWASP 2024),或参数为 N=2^17、r=8、p=1 的 scrypt
  • 从高熵密钥材料派生: HKDF-SHA256——已有密钥材料且需要派生子密钥时应使用的原语
  • 从低熵秘密派生加密密钥: PBKDF2 / Argon2(按密码处理)
  • 搜索:PBKDF2(检查迭代次数)、HKDF、Argon2、scrypt、pbkdf2_hmac(Python;检查 iterations 参数)
IV / nonce 处理

IV / nonce 处理不当,是“加密看起来没问题,实际上却泄露了明文”的三大常见原因之一。

  • AES-GCM: 使用同一密钥加密时,每次都必须使用唯一的 nonce。绝不能重复使用。 如果使用同一密钥重复使用 GCM nonce,攻击者就能得到两段明文的异或结果,并能够伪造消息。使用 96 位随机 nonce(RFC 5116)。对于高吞吐量系统,请改用 AES-GCM-SIV(可抵御 nonce 误用)
  • AES-CBC: IV 必须不可预测且唯一。每次加密使用随机的 16 字节 IV
  • AES-CTR: 在密钥的整个生命周期内,(key, counter) 对中的计数器绝不能重复
  • ChaCha20-Poly1305: 每条消息使用唯一的 nonce;XChaCha20-Poly1305 的 nonce 为 192 位,因此大规模使用时随机 nonce 也是安全的
  • 搜索:硬编码的 IV(iv = "0000000000000000"、iv = bytes(16))、零 IV 构造函数、计数器重置
  • 特别搜索:Cipher.getInstance("AES")(Java 默认使用 ECB)、AES.new(key)(PyCryptodome 默认使用 ECB)、crypto.createCipher(Node,已弃用,会从密钥派生 IV——不要使用)
认证加密
  • 始终使用认证加密模式——AES-GCM、ChaCha20-Poly1305 或先加密后认证(对密文执行 HMAC-SHA256)
  • 绝不要先解密再检查 MAC;始终先检查 MAC,再解密(否则会产生填充预言攻击)
  • 如果使用先加密后认证,请为加密和认证使用独立的密钥(或者使用 HKDF 从一个主密钥派生出两个密钥)
  • 使用恒定时间比较来验证 MAC(Node 中的 crypto.timingSafeEqual、Python 中的 hmac.compare_digest、Go 中的 subtle.ConstantTimeCompare)——参见 owasp-audit A07
签名验证
  • 最常见的签名验证错误并非算法失效,而是根本没有检查签名,或从消息本身读取算法
  • JWT: 确认 alg 与代码预期完全一致。绝不要调用 jwt.decode 后直接使用其中的声明,必须调用 jwt.verify。许多库默认接受 alg: none(未签名)——请确认所用库的版本不存在此问题
  • Webhook 签名: 在对请求体执行任何其他操作之前,先验证签名。许多实现会先解析再验证,留下 JSON 解析攻击面
  • 对签名字节串进行恒定时间比较
  • 时间戳容差窗口——只接受 ± 5 分钟内的签名(Stripe、GitHub、Slack 都采用此方式),不要永久接受(防止重放攻击)
  • 参见 owasp-audit A04,检查签名处理路径中的类型强制转换(parseInt → NaN)
  • 搜索:jwt.decode(后面没有调用 verify)、alg: 'none'、附近没有 timingSafeEqual 的 verify.*sig
随机数
  • 使用: crypto.randomBytes(Node)、secrets.token_bytes(Python ≥ 3.6)、crypto/rand(Go)、SecRandomCopyBytes(iOS)、SecureRandom(Java)
  • 绝不要使用: Math.random()(Node——梅森旋转算法,结果可预测)、random.random()(Python——同理)、rand()(C——质量很差)、将 arc4random_uniform 用于加密(BSD——这只是历史遗留名称,但仍需确认运行时的实现)
  • 对于 UUID,优先使用由 CSPRNG 生成的 UUID v4(大多数语言的标准库都能正确处理;如果情况重要,请阅读实现代码进行确认)
  • Token / 会话 ID 的最低熵值——128 位(16 个随机字节,经 base64 编码后为 22 个字符)是底线;256 位是合理的默认值
  • 搜索:Math.random、random.random、rand(、mt_rand(PHP)、Random.new(Ruby)
TLS 配置
  • **版本:**优先使用 TLS 1.3,最低使用 TLS 1.2,拒绝 TLS 1.0 / 1.1 / SSLv3 / SSLv2
  • **密码套件(TLS 1.2):**仅使用 ECDHE,仅使用 AEAD 密码(AES-GCM、ChaCha20-Poly1305)。拒绝 CBC、RC4、NULL、EXPORT、匿名密码套件
  • 证书验证:VERIFY_PEER,验证完整证书链,并启用主机名检查(托管服务的注意事项见 owasp-audit A04)
  • **证书固定:**用于高信任连接(移动应用连接到你的后端、敏感的内部服务);同时配置备用固定值(用于轮换)
  • **HSTS 预加载:**确认每个子域名都首先通过 HTTPS 提供服务;提交预加载后不易撤销(需要数月才能移除)
  • 启用 OCSP 装订,提升性能和隐私保护
  • 测试工具:testssl.sh https://target 或 sslyze --regular target
密钥生命周期
  • **密钥存放位置:**HSM / 云 KMS(AWS KMS、GCP Cloud KMS、Azure Key Vault、HashiCorp Vault Transit)——应用请求加密 / 解密,无需接触密钥材料
  • **信封加密:**每条记录的数据密钥由客户主密钥包装——如果任何单个数据密钥泄露,可限制影响范围
  • **轮换:**主密钥每年轮换(或按提供商的默认设置);每条记录使用单独的数据密钥(无需轮换——只有怀疑密钥泄露时才重新加密)。配置后,KMS 提供商会处理这些操作
  • **密钥版本控制:**每段密文都会记录用于加密的密钥 ID,因此轮换后解密时可以找到正确的密钥
  • **撤销:**如何阻止已泄露的密钥用于解密?须制定、记录并测试相应计划
特定框架模式
  • Rails:MessageVerifier / MessageEncryptor 默认使用现代密码原语;确认它们配置了强密钥(32 字节 / 256 位)
  • Django:cryptography.fernet 使用 AES-128-CBC + HMAC-SHA256(可接受,但不是 GCM);短签名值使用 django.core.signing
  • **Node/Express:**优先使用 iron-session / cookie-signature,不要自行实现
  • **iOS:**现代 Swift 代码使用 CryptoKit;CommonCrypto 也可用,但有更多容易出错之处
  • **Android:**Tink(Google)是推荐的高层库;原始 JCA 历来默认使用 AES-ECB

验证,然后记录

使用 references/findings-format.md 中的共享格式,为清单中的每个候选项给出一个判定,完成检查。在将任何项目标记为 confirmed 之前,先按照 references/verification.md 执行反证检查:打开调用点,查明密钥、IV、盐和参数的实际来源,并确认所用的库版本是否已应用安全默认值。

只有在能够明确指出谁获得了什么时,弱密码原语才构成一项发现:信任级别较低的主体(任何能看到密文的人、持有泄露哈希表的人、发送令牌的客户端、路径上的网络攻击者)、它绕过的控制(机密性、完整性、身份验证、密码保密性)以及造成的结果。根据被破坏的控制所保护的数据,参照 references/severity-calibration.md 评定严重性。诸如“攻击者已持有用户表副本”之类的条件,应写入 conditions[](data_state);这会降低可能性,而非影响。 confirmed — 代码表明存在错误用法,且结果随之而来。示例:

  • src/lib/vault.ts 中的 createCipheriv('aes-256-gcm', key, FIXED_IV) 对每条记录都使用同一个密钥:任何能读取加密列的人都能得到明文的异或结果,并伪造密文。一个会加密两个值并显示密钥流重复的单元测试会设置 reproduced: true。
  • src/lib/session.ts 中使用 Math.random() 生成会话令牌:任何用户的会话令牌都可预测。
  • jwt.sign(..., process.env.JWT_SECRET || 'dev-secret'):通过 system_configuration 条件(“未设置 JWT_SECRET 的部署”)确认,置信度为 medium。任何能读取代码仓库的人都能为这些部署伪造令牌。
  • 使用未加盐的 SHA-256 或迭代次数为 1,000 的 PBKDF2 处理密码,并通过 data_state 条件注明哈希表泄露。

needs_validation — 代码支持这一判断,但有一个事实不在代码中。示例:

  • nginx.conf 允许 TLSv1 TLSv1.1,但 CDN 或负载均衡器可能会先终止 TLS。阻碍因素:哪个配置为 api.example.com 提供服务。计划:testssl.sh --protocols https://api.example.com(实时检查,需要授权)。仅描述但尚未运行的 TLS 检查仍归于此类。
  • 数据密钥由 KMS 封装,但其密钥策略位于另一个代码仓库。阻碍因素:哪些主体持有 kms:Decrypt 权限。计划:aws kms get-key-policy --key-id <id> --policy-name default。

rejected — 指定位置上的明确控制措施排除了该问题。示例:

  • jwt.verify(token, key) 未设置 algorithms 选项,但被库排除:control 为 "jsonwebtoken 9.0.2 verify() rejects alg: none and HS/RS confusion for asymmetric keys",ruled_out_by.location 指向锁定 9.0.2 的 package-lock.json 所在行。
  • src/lib/etag.ts 中的 createHash('md5') 仅用于缓存 ETag。ruled_out_by.location 指向唯一的调用位置;该处设置响应标头,不作信任判断。

加固说明,而非记录: 安全性低于推荐水平,但没有可追踪的暴露风险。例如:bcrypt cost 为 10 而非 12、正确实现的 AES-CBC Encrypt-then-MAC(优先使用 GCM)、在提供 AEAD 套件的同时提供 TLS 1.2 CBC 套件、未启用 OCSP stapling、KMS 密钥没有轮换计划、缺少 HSTS preload。追踪出自定义加密未能保护的内容时,应将其列为问题;否则应作为“替换而非修补”的加固说明。

实时和云端证据。 TLS 扫描以及 KMS 或 HSM 设置描述的是已部署资源,而非文件。将只读输出脱敏后保存到 <audit dir>/evidence/<name>.json 或 .txt(testssl.sh --jsonfile、sslyze --json_out、aws kms describe-key),并将 location 和 evidence[] 指向该文件及行号。切勿保存密钥材料、私钥、解密所得的明文或机密值。

指纹: crypto-audit/<class>/<file or resource>/<symbol or name>,不含行号:

  • crypto-audit/gcm-nonce-reuse/src/lib/vault.ts/encryptField
  • crypto-audit/CWE-338/src/lib/session.ts/newSessionId
  • crypto-audit/tls-legacy-protocol/host/api.example.com/443
  • crypto-audit/kms-key-policy/aws/123456789012/us-east-1/alias/app-data

将 host:port 写为 host/<host>/<port>,并用斜杠分隔 ARN:验证器会拒绝包含 :<digits> 段的值。

ext 键(可选,可用于任何记录):

键值示例
primitive所用算法、模式和参数"AES-256-GCM, static 96-bit IV"、"PBKDF2-HMAC-SHA256, 1000 iterations"
library执行操作的库及其版本"node:crypto (Node 20)"、"pycryptodome 3.20.0"
standard使用方式偏离的指南"NIST SP 800-38D 8.2"、"RFC 8996"、"OWASP Password Storage"

然后按指纹排序写入 findings.json,对其进行验证,并可选择导出 SARIF:

node references/validate-findings.mjs findings.json
node references/to-sarif.mjs findings.json -o findings.sarif

在运行时验证修复

  • 每次更改后都测试加密/解密往返流程——静默数据损坏是密码学变更中最危险的故障模式
  • 更改算法时(例如,从 bcrypt 改为 Argon2id):规划在用户下次登录时迁移(在身份验证流程中从明文重新哈希);迁移完成前,旧哈希必须仍可读取
  • 更改 TLS 时:使用 testssl.sh 和 curl --tlsv1.3 --tls-max 1.3 https://target 验证(强制执行 TLS 最低版本)
  • 更改 KMS 时:验证 IAM 权限同时涵盖加密和解密(常见的上线错误:数据已加密,却无法解密)

输出格式

先编写 findings.json 并验证。根据该文件和加固说明列表生成报告。叙述内容绝不能更改 JSON 中尚未体现的判定结果、严重性或阻断项。

# Cryptography Implementation Audit
## Project: [name]   ## Date: [date]
## Scope: [components covered; full | partial — what was left out, e.g. "no TLS scan run"]
## findings.json: [path] — validated: [yes]

### Summary
- Confirmed: [N] (Critical [n] | High [n] | Medium [n] | Low [n])   Needs validation: [N]   Rejected: [N]   Hardening notes: [N]

### Confirmed findings
#### [SEVERITY] [Title]  `fingerprint`
**Location:** `path:line`   **CWE:** [CWE-n]   **Confidence:** [medium | high (reproduced)]   **Primitive:** [ext.primitive]
**What and why it matters:** [principal, control defeated, result]
**Conditions:** [e.g. data_state: attacker holds the users table]
**Vulnerable code:** [snippet]
**Fix:** [concrete change, including migration of existing ciphertext or hashes]
**Verify the fix:** [round-trip test, rehash on login, testssl output; see Verify Fixes at Runtime]

### Needs validation
| Finding | Blockers | Check for the owner to run |
|---|---|---|

### Hardening notes (not findings)
- [location] — [gap] — [why it isn't a finding] — [effort]

### Rejected (do not re-raise)
| Fingerprint | Claim | Ruled out by | Location |
|---|---|---|---|

### TLS posture (if applicable)
[Summary of saved testssl.sh / sslyze output, with the evidence file path, or "not run"]

### Key inventory
| Key | Purpose | Location | Algorithm | Rotation |
|-----|---------|----------|-----------|----------|

### Prioritized remediation plan
1. [Critical / High confirmed findings]
2. [Medium / Low confirmed findings]
3. [Owner-run checks for needs-validation items]
4. [Hardening notes worth the effort]

已确认问题的处置方式(稍后设置,通常由 finding-triage 设置):Fixed / Deferred / Accepted Risk / Mitigated (platform/config) / Requires major migration。误报应记录为 rejected,而不是处置方式。

范围限制

  • 审计用户提供的代码和配置
  • 拒绝协助破解、削弱或在密码学中植入后门
  • 对于 TLS 测试,只测试用户有权测试的端点
  • 如果审计发现设计存在根本性缺陷(自定义密码学、以 ROT13 作为保护),建议应为“替换,而非修补”——不要尝试逐步改进有缺陷的设计
  • 抗量子迁移:跟踪 NIST PQC 标准化进展,但在相关标准正式确立且获得库支持之前,不要推荐具体的 PQC 原语;这一领域仍在发展变化

参考资料

  • NIST SP 800-57(密钥管理)
  • NIST SP 800-131A(算法迁移)
  • NIST SP 800-175B(密码学标准指南)
  • NIST FIPS 140-3(密码模块标准)
  • IETF RFC 7525(TLS 建议)
  • OWASP 密码存储备忘单
  • OWASP 传输层保护备忘单
  • 《Cryptography Engineering》—— Ferguson、Schneier、Kohno(值得阅读的书)
  • 《Real-World Cryptography》—— David Wong
  • libsodium / Tink / BoringSSL 文档
  • 共享参考资料(构建时复制到 references/):findings-format.md、severity-calibration.md、verification.md、validate-findings.mjs、to-sarif.mjs、findings.schema.json

评论

支持 Markdown