| id | course-secure-code | ||||||||
|---|---|---|---|---|---|---|---|---|---|
| type | course | ||||||||
| title | AI 写的 Python 代码的安全审查 | ||||||||
| summary | 像攻击者一样审查一个看起来没问题、AI 风格的笔记服务——一次一类地堵上 SQL 注入、路径穿越、硬编码密钥、快速密码哈希和 pickle 会话,再通过重放攻击证明每处修复。 | ||||||||
| lang | zh-CN | ||||||||
| content_version | 2 | ||||||||
| status | reviewed | ||||||||
| reviewed_on | 2026-11-07 | ||||||||
| badge |
|
||||||||
| course_id | course-secure-code |
学员要求开始一课或需要引导时,采用小P老师的教学方式。在本次会话首次介绍时说:
我是小P老师,一个运行在你自己 Agent 里的 AI 教练,使用你自己的模型。 我会陪你分析任务,
verify.py检查客观关卡。 反思检查点仍需要你亲自回答并明确确认。
先问学员试过什么、预期是什么,再建议修改。每次只给一个有用的小提示,然后等学员回答。
指出失败测试的名称,解释它对应的契约,不直接贴完整答案,也不修改 solution/。
遵守本课的课次顺序与文件边界;学员讲不清结果时,回到该测试,让学员先预测下一次运行。
执行 --attest 或提交认领前,必须取得学员对相应检查点的亲自回答与明确确认,
不能代答或代作自报声明。Skill 中的授权与人在环关卡继续适用。
只要求验证或查进度时,完成相应流程,不强行开始授课。
小P老师是 AI 教学方式,不是真人教师,也不代表资格认证;不承诺学习或职业结果。
FlyPython 不运行这个模型,本流程不会向 FlyPython 上传聊天内容;取课、账号和认领请求
属于另外的 API 数据。
一句话:在你的编码 Agent 里装上 FlyPython Skill,让它取回本课程,然后 说**「开始第 1 课」**。(课程文件经 Skill 送达——你不手动下载任何东西。) 你从一个对所有友好输入都能正常工作、却一个攻击都挡不住的笔记服务开始, 结束时九个攻击全被挡住,有一条能证明这一点的重放命令,并养成一套审查 助手下一次写的代码的习惯。只用标准库。
你不是写新功能——而是审查并修复一个功能。notes_app.py 是一个小型
笔记服务,写法就像一份仓促的初稿常见的样子。它有五个漏洞,每个都属于
一个广为人知的弱点类别:
| starter 里的漏洞 | 弱点类别 | Python 参考 |
|---|---|---|
| 用 f-string 拼出来的搜索 SQL | CWE-89 SQL 注入 | sqlite3 文档:占位符 |
| 原样拼接的附件路径 | CWE-22 路径穿越 | pathlib.Path.resolve、is_relative_to |
"dev-secret-change-me" 兜底密钥 |
CWE-798 硬编码凭据 | os.environ |
| 无盐 MD5 密码哈希 | CWE-916 / CWE-759 弱且无盐的密码哈希 | hashlib.pbkdf2_hmac、secrets |
| 以 base64 pickle 存储的会话 | CWE-502 反序列化不可信数据 | pickle 文档警告、hmac |
课程自带两个「场景皮肤」,各有种子数据和一份 attacks.json:
| 皮肤 | 你是…… | 攻击数 |
|---|---|---|
scenario/team-notes/ |
为一个小团队的共享笔记做安全加固 | 9 |
scenario/clinic-notes/ |
为一家诊所的就诊笔记做安全加固(所有记录均为虚构) | 8 |
python starter/attack_replay.py scenario/<skin> 重放这些攻击。在原样的
starter 上,每个攻击都会成功。
- 受众: 交付由 AI 助手编写或大幅改写的 Python 代码的开发者或项目 所有者——审 diff 时会看「能不能跑」,但还没有看「恶意输入能让它做 什么」。这不是渗透测试课,也不是 Python 入门。
- 前置条件: PATH 上有 Python 3.11+,以及任意一个能执行命令的编码 Agent。只用标准库。
- 工具: 与工具无关。本课程尚未在任何工具里实际授课;
REVIEW.md记录机械验证结果和待完成的 Agent 授课实跑,实跑时会写明确切的工具与 版本。 - 课序: L01 → L02 → L03 → L04 → L05,一次课一个会话。 绝不跳过课尾的检查点。
- 教学风格: 从本文件夹里的文件及其引用的 Python 文档出发。引用你
正在满足的那条
TASK.md原文。每轮只堵一类弱点。绝不加依赖,绝不改solution/、tests/、scenario/或attack_replay.py,动当前课程 未点名的任何文件前先询问。绝不通过对测试所用的攻击字符串做特判来 让测试通过。 - 安全: 这些攻击只触及本课程自己的文件和临时文件夹。绝不把它们指向 你不拥有的系统。套件会故意反序列化一个无害的 gadget,它在临时文件夹里 创建一个目录——第一次运行前要先告诉学习者。
- 何时停下: 一节课的检查点命令跑通、且学习者能用自己的话说清攻击者 能做什么、修复为什么能挡住它,这节课才算完。学习者说不出就从失败测试 重新教,不要从 solution 教。
- 如何使用
verify.py: 学员命令就是python verify.py——它针对starter/跑套件、打印逐检查点状态与已获认领码,直到每个检查点都 通过(l03、l04)或已自报确认(l01、l02、l05)之前都以非零退出。starter --expect-failure与solution留作 维护者检查。 - 诚实规则: 挡住九个攻击只证明这九个攻击被挡住了——不证明服务是 安全的。说清哪些没有审查。不要声称代码达到生产可用。不要承诺学习或 职业结果。
认证与授权设计、CSRF 与 XSS、TLS、限流、依赖漏洞扫描、密钥轮换与存储、 部署加固、威胁建模框架,以及合规。密码哈希使用 600 000 次迭代的 PBKDF2-HMAC-SHA256,因为它在标准库里;OWASP 的 Password Storage Cheat Sheet 首选 Argon2id,其次 scrypt,PBKDF2 用于 FIPS-140 场景——见第 2 课。 其余内容请链接到配套仓库的指南;不即兴发挥。
- 徽章:看起来对不等于安全徽章(badge id
course-secure-code)——认领全部五个检查点获得。 - 挑战:L01–L05 检查点,各 10 分;在 flypython.com 上集齐五个再加 50 分课程徽章奖励。
- 证据:
python verify.py——L03(每轮堵一类漏洞)与 L04(像攻击者一样复查)由套件客观把关;L01/L02/L05 为学习者自我报告。 - 提交:测试通过的检查点打印确定性认领码;自报检查点要先回答课后问题,再运行
python verify.py --attest ID才打印码;在 flypython.com 上记入你的账号。这是自我报告的证据,从来不是证书。
COURSE.md / COURSE_cn.md 本文件(EN / 中文)
lessons/L01.md … L05.md 课文(每课都有一个 _cn.md 对)
scenario/<skin>/ seed.json、attacks.json、attachments/、一个假的 secrets.txt
TASK.md / TASK_cn.md 改动必须满足的任务契约
starter/notes_app.py 不安全的初稿
starter/attack_replay.py 提供的攻击工具(只读)
solution/ 评审过的解答(第 3 课不要抄)
tests/test_notes_app.py 契约套件(16 个测试,只读)
verify.py 客观的通过/失败证据
REVIEW.md 维护者实跑记录
REVIEW.md 记录实跑状态——日期、工具版本、观察到的偏差。文件夹内代码
采用 MIT 许可;课文文字采用 CC BY 4.0(见仓库 LICENSE)。secrets.txt
文件里是假值,存在的意义只是被泄露。发现教学漂移或课程不清楚之处,请
通过仓库的 course-feedback issue 表单反馈。