24 · 实战与挑战项目
⏱ 项目 1–2 天 / 挑战 4–6 天 | ⭐ 密码学必须靠"打破它"来学
🎯 一句话
这一章的项目和别的教程不同:它们大多不是"实现一个算法",而是"攻破一个错误实现"。 因为密码学的直觉,几乎只能从看着一个"看起来没问题"的方案倒下来建立。
⚠️ 开工前必读
✅ 这些项目全部在【你自己搭的靶子】上做
✅ 目的是理解防御,不是攻击真实系统
❌ 不要对任何不属于你的系统做这些事
🔑 还有一条贯穿全章的铁律:
【学完这些,仍然不要自己实现生产密码学】
你学的是"为什么要用成熟库",不是"怎么自己写一个"
环境:
pip install pycryptodome cryptography
🥉 项目一:亲手打破五个错误实现(1 天)
目标:把教程里讲过的攻击,一个一个真的跑出来。
- [ ] T1 (45min) ⭐ 重用 Nonce 的流密码(第 5 章)
- 用同一个 keystream 加密两条消息 → 密文异或 → 明文异或
- 用"空格猜测法"逐字节还原两条明文
-
✅ 通关:完整还原出两句话
-
[ ] T2 (60min) ⭐⭐ CBC 填充预言攻击(第 7 章)
- 写一个服务端:解密失败时区分"填充错误"和"其他错误"
- 客户端只靠这一位信息,逐字节解出整个明文
-
✅ 通关:不知道密钥的情况下解出明文
-
[ ] T3 (45min) 长度扩展攻击(第 10 章)
- 用
H(secret ‖ message)做"认证" - 在不知道 secret 的情况下,伪造
H(secret ‖ message ‖ 你的内容) -
✅ 通关:伪造出一个服务端认可的新消息
-
[ ] T4 (45min) 教科书 RSA 的两个洞(第 14 章)
- 同态伪造:由
σ₁, σ₂造出m₁m₂的签名 - 小 e 攻击:e=3 且消息短时,直接开三次方恢复明文
-
✅ 通关:两个都成功
-
[ ] T5 (60min) 💀 ECDSA 的 k 重复(第 17 章)
- 故意用同一个 k 签两条消息
- 按公式解出 k,再解出私钥
- ✅ 通关:打印出的私钥和真实私钥完全一致
🔑 这个项目的核心收获
每一个攻击的前提,都是一个"看起来无害"的实现选择: 复用一个 nonce、返回一个更详细的错误、少做一次填充、图省事用同一个随机数。 密码学的脆弱性几乎全部集中在这些地方,而不是数学本身。
🥈 项目二:实现一个"能用"的加密文件格式(1–2 天)
目标:不是发明算法,而是正确地组装现成的算法。
- [ ] T1 (45min) 设计文件格式,先写下来再写代码:
| 字段 | 长度 | 说明 |
|---|---|---|
| magic | 4B | 文件魔数,认出这是你的格式 |
| version | 1B | 版本号,为以后换算法留路 |
| 算法标识 | 1B | 用的是哪套 AEAD / KDF |
| salt | 16B | KDF 的盐,每个文件都不同 |
| nonce | 12B | AEAD 的 nonce,⚠️ 绝不能复用 |
| KDF 参数 | 变长 | 内存 / 迭代次数 / 并行度 |
| ciphertext | 变长 | 密文本体 |
| tag | 16B | 认证标签,解密前先验它 |
- [ ] T2 (45min) 口令 → 密钥:用 Argon2id(第 19 章), 参数写进文件头(将来才能调)
- [ ] T3 (45min) 加密:AES-256-GCM 或 ChaCha20-Poly1305
- [ ] T4 (30min) ⭐ 把文件头放进 AAD —— 否则攻击者能改版本号/算法标识做降级攻击
- [ ] T5 (45min) 解密路径:认证失败必须立刻整体失败,不输出任何明文
- [ ] T6 (45min) 写测试:改一个字节 → 必须失败;改文件头 → 必须失败;口令错 → 必须失败
✅ 通关标准
| 检查 | 要求 |
|---|---|
| 篡改密文任意一字节 | 抛认证错误,不输出部分明文 ⭐ |
| 篡改文件头任意一字节 | 抛认证错误(证明 AAD 生效) |
| 同一口令加密两次 | 密文完全不同(salt/nonce 每次新生成) |
| nonce 复用 | 代码里结构上不可能发生 |
🎁 加分
- 加密钥轮换:用主密钥包裹数据密钥(第 19 章信封加密)
- 加流式处理:大文件分块,⭐每块的 AAD 里带上块序号和"是否最后一块" —— 否则攻击者能重排或截断分块
⚠️ 最后这条是真实系统里反复出错的地方: 每块单独认证 ≠ 整个文件被认证。必须把顺序和结束标记也纳入认证范围。
🥇 挑战项目:写一个"密码学代码审计器"(4–6 天)
难度 ★★★★★ | 产出是一个能扫出真实问题的工具。
目标:给定一个 Python 项目,自动找出密码学误用
这是安全工程师真实在做的事
阶段一:静态规则(Day 1–2)
- [ ] T1 用
ast模块解析代码,识别密码学 API 调用 - [ ] T2 实现前 8 条规则:
| # | 规则 | 依据 |
|---|---|---|
| 1 | 用了 MD5/SHA-1 做安全用途 | 第 9 章 |
| 2 | 用了 ECB 模式 | 第 7 章 |
| 3 | 硬编码密钥/IV | 第 19 章 |
| 4 | 用 random 而非 secrets 生成密钥 |
第 4 章 ⭐ |
| 5 | 固定 nonce/IV | 第 5 章 |
| 6 | 只加密不认证(无 GCM/Poly1305/HMAC) | 第 11 章 |
| 7 | 口令直接当密钥用(没走 KDF) | 第 19 章 |
| 8 | 用 == 比较 MAC/token |
⭐ 时序攻击 |
- [ ] T3 输出:文件、行号、规则、为什么危险、怎么改
阶段二:数据流分析(Day 3–4)
- [ ] T4 追踪密钥的来源:是常量吗?是环境变量吗?
- [ ] T5 追踪 nonce:它在循环里是每次新生成,还是循环外只生成一次? ⭐
- [ ] T6 检测"先解密再验证"的顺序错误
- [ ] T7 处理误报:加
# noqa: crypto白名单机制
阶段三:验证与打磨(Day 5–6)
- [ ] T8 ⭐ 造一个含 20 个已知问题的测试项目,测量召回率和误报率
- [ ] T9 在 3 个真实开源项目上跑,人工确认每个告警
- [ ] T10 写报告:哪些规则误报最多?为什么静态分析在这里天然受限?
✅ 通关标准
- 测试项目上召回 ≥ 80%
- 真实项目上误报率 < 30%(并说明每个误报的成因)
- 报告里有一段你自己的关于"静态分析的边界在哪"的分析
🏆 加分
- 支持 JavaScript / Go
- 输出 SARIF 格式,能接进 GitHub Code Scanning
- 检测依赖库版本里的已知密码学 CVE
💡 这个项目的价值: 你会发现大多数真实漏洞不是"用错了算法",而是"用对了算法但组装错了" —— 这正是整本教程反复强调的那件事。
📝 报告模板
# 攻击/实现:____
## 前提条件(攻击者需要什么能力)
## 原理(为什么这个实现选择会致命)
## 复现步骤 + 关键代码
## 结果(截图/输出)
## 正确做法是什么
## 我原本以为 ___,实际上 ___ ← 最有价值的一节
🔗 这一章连到哪里
| 可以顺手对照的 | 为什么去那里 |
|---|---|
| 智能体教程 · 挑战项目 C:提示注入攻防 | 同构的红队/蓝队项目:搭靶场 → 攻破基线 → 分层防住 → 写报告,它的阶段划分和通关标准可以直接借过来用 ⭐ |
| Claude 资料库 · AI 原生 SDLC 安全 | 审计器只有接进 CI 才会真被用起来;那边讲的正是「测试与 CI 阶段」怎么改造 |
| 机器学习基础 · 评估与过拟合 | ⚠️ 「20 个已知问题的测试项目」是个极度不平衡的评测集——别用准确率汇报,去那一节挑对指标再写通关结论 |
✅ 检查点
- 为什么这一章以"攻破"而不是"实现"为主?
- 五个攻击的共同前提是什么?
- 加密文件格式里,为什么必须把文件头放进 AAD?
- 认证失败时为什么不能输出部分明文?
- 分块加密时,每块单独认证为什么不够?
- 为什么用
==比较 MAC 是漏洞? - 静态分析检测 nonce 复用时,关键要看什么?
- 学完这一章,你应该去自己实现生产密码学吗?
👀 答案
- 因为密码学的直觉几乎只能从看着一个"看起来没问题"的方案倒下来建立。实现一遍很难让你记住为什么某个选择是致命的。
- 每一个的前提都是一个"看起来无害"的实现选择:复用 nonce、返回更详细的错误、少做一次填充、图省事用同一个随机数。脆弱性集中在组装处,不是数学本身。
- 否则攻击者能篡改版本号/算法标识做降级攻击,而认证不会发现。
- 因为输出未认证的明文等于给攻击者一个预言机——填充预言攻击(T2)正是这么工作的。必须整体失败。
- 因为攻击者可以重排或截断分块。必须把块序号和"是否最后一块"也放进每块的 AAD,让顺序和结束本身也被认证。
==通常短路比较,比较时间随匹配的字节数变化,攻击者可据此逐字节猜出正确 MAC(时序攻击)。要用hmac.compare_digest。- 看它是在循环内每次新生成,还是在循环外只生成一次——后者意味着整个循环共用同一个 nonce。
- 不应该。这一章教的是"为什么要用成熟库",不是"怎么自己写一个"。
🛑 可以停在这里
⚡ 走神救援
这章以"攻破"而非"实现"为主 —— 密码学直觉几乎只能从看着一个"看起来没问题"的方案倒下来建立。⚠️全部在自己搭的靶子上做;⭐铁律:学完仍然不要自己实现生产密码学,你学的是"为什么要用成熟库"。项目一(五个攻击):Nonce 重用→密文异或=明文异或→空格猜测还原;⭐⭐CBC 填充预言(只靠"填充错/其他错"一位信息逐字节解出明文);长度扩展伪造 H(secret‖msg‖你的内容);教科书 RSA 同态伪造 + e=3 直接开三次方;💀ECDSA k 重复解出私钥。🔑五个攻击的共同前提都是一个"看起来无害"的实现选择 —— 复用 nonce、返回更详细的错误、图省事用同一个随机数,脆弱性集中在组装处不是数学本身。项目二(加密文件格式):Argon2id 派生 + AES-GCM/ChaCha20-Poly1305,⭐文件头必须放进 AAD(否则可降级攻击),⭐认证失败必须整体失败不输出部分明文(否则就成了填充预言那种预言机),分块时⭐每块 AAD 要带块序号和"是否最后一块"(否则可重排/截断——每块单独认证≠整个文件被认证,真实系统反复在此出错)。挑战(密码学代码审计器):ast 静态规则(MD5/ECB/硬编码密钥/用 random 而非 secrets/固定 nonce/只加密不认证/口令直接当密钥/用 == 比较 MAC 的时序攻击)→ 数据流分析(⭐nonce 是在循环内还是循环外生成)→ 用 20 个已知问题的测试项目量召回率和误报率。💡 你会发现大多数真实漏洞不是"用错了算法",而是"用对了算法但组装错了"。
下一节 👉 附录A-速查.md