11.4 测试与安全最佳实践
🎯 本节目标
学习如何通过测试驯服 AI 生成的代码,以及如何避免常见的安全陷阱。
🧪 测试:比以往更重要
为什么 AI 时代测试更重要?
AI 代码的特点:
├── 通常能生成"正确"的代码
├── 但可能误解规格(做了错误的事情)
├── 有时会出现幻觉
└── 人类也会误解需求,这不是 AI 独有的问题
结论:测试是验证 AI 输出的关键手段即使我们达到了完全的人工通用智能(AGI),测试依然重要——因为人类也会误解规格!
TDD(测试驱动开发)与 AI
markdown
TDD + AI 的工作流:
1. 先写测试骨架
├── 定义期望的输入和输出
└── 明确边界条件
2. 让 AI 实现代码
├── AI 看到测试就知道目标
└── 有明确的成功标准
3. 运行测试验证
├── 让 AI 自己运行测试
└── 失败时自动修复
4. 测试通过后继续
└── 只有通过所有测试才继续下一个功能属性测试(Property-Based Testing)
属性测试特别适合 AI 生成的代码:
普通测试 vs 属性测试:
普通测试:
assert sort([3, 1, 2]) == [1, 2, 3]
# 只测试了一个特定输入
属性测试:
@given(lists(integers()))
def test_sort_preserves_length(lst):
assert len(sort(lst)) == len(lst)
# 测试了无数个随机输入优势:
- 覆盖边界情况
- 发现你没想到的 bug
- 即使 AI 后续修改代码,属性测试仍然有效
属性测试库
| 语言 | 库 | 链接 |
|---|---|---|
| Python | hypothesis | hypothesis.readthedocs.io |
| JavaScript/TypeScript | fast-check | fast-check.dev |
| 其他语言 | 搜索 "property-based testing + [语言]" | - |
测试陷阱警告
⚠️ AI 作弊行为
有时候 AI 会"作弊"来让测试通过:
├── 硬编码预期输出
├── 检测测试输入并返回固定值
└── 绕过实际逻辑
解决方案:
├── 审查测试和实现代码
├── 使用属性测试(难以作弊)
└── 测试边界情况和随机输入🔒 安全检查清单
核心原则
第一原则:永远不要信任 AI 生成的代码
AI 不会为你运行的代码负责——你自己负责!安全检查清单
1. 密钥管理
markdown
❌ 绝对不要:
- 硬编码 API 密钥
- 在前端代码中存储密钥
- 将 .env 文件提交到 Git
✅ 正确做法:
- 使用环境变量
- 使用密钥管理服务(如 Vercel 的环境变量)
- 在 .gitignore 中添加敏感文件2. 网络安全
markdown
❌ 绝对不要:
- 使用 HTTP(不加密)
- 禁用 SSL 验证
- 忽略证书错误
✅ 正确做法:
- 始终使用 HTTPS
- 验证 SSL 证书
- 使用安全的 API 端点3. 输入验证
markdown
❌ 绝对不要:
- 信任用户输入
- 直接拼接 SQL 查询
- 在 HTML 中直接插入用户内容
✅ 正确做法:
- 验证和清理所有输入
- 使用参数化查询
- 转义 HTML 输出(防 XSS)4. 客户端存储
markdown
❌ 绝对不要:
- 在 localStorage 存储敏感数据
- 在 cookies 存储密码
- 在 sessionStorage 存储令牌(某些场景)
✅ 正确做法:
- 使用 httpOnly cookies 存储令牌
- 加密敏感数据
- 最小化客户端存储的数据5. 依赖安全
bash
# 定期扫描依赖漏洞
# npm
npm audit
# Python
pip-audit
safety check
# 通用
snyk test🛡️ 安全工作流
在项目规则中添加安全约束
markdown
# .cursor/rules/security.md
## 安全规范
- 永不硬编码密钥或敏感信息
- 所有 API 调用必须使用 HTTPS
- 所有用户输入必须验证和清理
- 数据库查询必须使用参数化语句
- 前端不存储任何敏感数据
- 所有依赖必须定期审计
## 禁止清单
- 不使用 eval() 或类似函数
- 不禁用安全检查或 SSL 验证
- 不在日志中输出敏感信息
- 不使用过时的加密算法代码审查重点
markdown
审查 AI 生成代码时关注:
1. 输入处理
- 是否验证了所有外部输入?
- 是否正确处理了边界情况?
2. 数据存储
- 敏感数据如何存储?
- 是否使用了适当的加密?
3. 网络通信
- 是否使用了 HTTPS?
- API 密钥如何传递?
4. 错误处理
- 错误信息是否泄露了敏感信息?
- 是否有适当的日志记录?
5. 依赖
- 是否使用了已知有漏洞的包?
- 依赖是否是最新版本?📋 OWASP Top 10 快速参考
这些是最常见的 Web 应用安全风险:
| 排名 | 风险 | AI 代码中常见吗? |
|---|---|---|
| 1 | 注入攻击(SQL、命令注入) | ⚠️ 常见 |
| 2 | 认证失效 | ⚠️ 常见 |
| 3 | 敏感数据暴露 | ⚠️ 非常常见 |
| 4 | XML 外部实体 | 较少见 |
| 5 | 访问控制失效 | ⚠️ 常见 |
| 6 | 安全配置错误 | ⚠️ 常见 |
| 7 | XSS 跨站脚本 | ⚠️ 常见 |
| 8 | 不安全的反序列化 | 较少见 |
| 9 | 使用有漏洞的组件 | ⚠️ 常见 |
| 10 | 日志和监控不足 | 常见 |
针对 AI 代码的特别关注
AI 生成代码中最常见的安全问题:
1. 硬编码的密钥
AI 经常在示例代码中使用硬编码密钥
2. 缺少输入验证
AI 倾向于"信任"输入数据
3. 过于宽松的权限
为了"让代码工作"而开放过多权限
4. 过时的依赖
使用训练数据中的旧版本库🔍 安全扫描工具
静态分析工具
| 工具 | 语言 | 说明 |
|---|---|---|
| ESLint Security Plugin | JavaScript | 发现 JS 安全问题 |
| Bandit | Python | Python 安全分析 |
| Semgrep | 多语言 | 自定义规则扫描 |
| Snyk | 多语言 | 依赖和代码扫描 |
使用示例
bash
# JavaScript/TypeScript
npm install -D eslint-plugin-security
npx eslint --ext .js,.ts .
# Python
pip install bandit
bandit -r ./src
# 通用
npx snyk test⚡ 快速安全检查清单
在部署前确认:
markdown
□ 没有硬编码的密钥或密码
□ 所有 API 调用使用 HTTPS
□ 用户输入已验证和清理
□ 数据库使用参数化查询
□ 前端没有存储敏感数据
□ 依赖已扫描无已知漏洞
□ 错误信息不泄露敏感信息
□ 认证和授权正确实现
□ 日志不包含敏感信息
□ CORS 正确配置🔗 下一步
继续学习 11.5 工具与资源汇总 —— 获取完整的工具列表和学习资源!
📚 本节参考资源
- hypothesis 文档 - Python 属性测试
- fast-check - JavaScript 属性测试
- OWASP Top 10 - Web 安全风险
- Snyk - 安全扫描平台
- Bandit - Python 安全分析