关于 secure-code-review
面向 Web 应用安全审查的 OWASP Top 10(2021)检查清单,逐项提供风险说明、检查点和修复示例。适合需要按类别或完整范围审查代码的安全人员。
主要功能
- 01覆盖 OWASP Top 10(2021)
- 02逐项列出代码检查点
- 03提供易受攻击代码示例
- 04附带修复建议与代码
- 05按风险级别分类发现
适用场景
- 01检查 Web 应用中的 SQL 注入风险
- 02审查 API 端点的 SSRF 漏洞
- 03执行完整 OWASP Top 10 代码审查
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ zebbern/ claude-code-guide --skill secure-code-review安装后在 Claude Code 中输入/secure-code-review确认已加载。
附带文件LICENSE
详细介绍
OWASP Top 10 代码安全审查清单
基于 OWASP Top 10(2021)标准进行系统性安全审查。每项都包括:漏洞描述、典型的易受攻击代码、检查要点和修复示例。适用于以安全为重点的 Web 应用代码审查。
用法
提供要审查的代码文件或代码片段,并指定要检查的 OWASP 类别(或请求全面审查),即可获得逐项审计报告。
提示词示例:
- “检查这段代码是否存在 SQL 注入风险”
- “对这个项目进行全面的 OWASP Top 10 安全审查”
- “这个 API 端点是否存在 SSRF 漏洞?”
快速参考
| ID | 类别 | 关键检查项 |
|---|---|---|
| A01 | 访问控制失效 | 每个端点是否都会验证当前用户的身份?用户能否通过更改 ID 来访问他人的数据? |
| A02 | 加密失效 | 密码是否使用 bcrypt/argon2 进行哈希?是否存在硬编码的密钥? |
| A03 | 注入 | 是否存在字符串拼接 SQL?是否使用 shell=True?模板输出是否未转义? |
| A04 | 不安全设计 | 是否实施了速率限制?关键工作流能否被绕过? |
| A05 | 安全配置错误 | 是否启用了 DEBUG?错误页面是否包含堆栈跟踪?是否使用默认凭据? |
| A06 | 易受攻击的组件 | pip audit / npm audit 是否发现任何 CVE? |
| A07 | 身份验证失效 | 是否验证了 JWT 签名?令牌能否撤销?是否提供 MFA? |
| A08 | 完整性失效 | 是否使用 pickle.loads 反序列化不可信数据? |
| A09 | 日志记录和监控失效 | 日志中是否记录了明文密码?是否记录了登录失败? |
| A10 | SSRF | 是否对用户提供的 URL 进行过滤,以阻止其指向内部 IP? |
审查流程 SOP
核心原则:宁可误报,也不要漏报。
- 定义范围 — 确定要审查的文件、模块或代码片段
- 全面覆盖检查 — 按顺序扫描 A01-A10。报告中必须包含每一项(没有发现问题的项目也要标记为通过)。默认行为仅报告已发现的问题;此流程要求全面覆盖,确保没有遗漏
- 风险分类 — 为每项发现标注风险:
- RED 高:可直接利用(RCE、SQL 注入、可访问内部网络的 SSRF、明文密码存储)
- YELLOW 中:在特定条件下可利用(缺少速率限制、密码策略薄弱、静态令牌)
- GREEN 低:纵深防御存在缺口,但没有直接利用途径(缺少安全响应头、日志记录不足)
- 每项发现都必须包含可直接使用的修复代码(实际代码,而不只是描述)。注明具体的
file:line_number - 输出审查报告 — 使用下方模板,按严重性从高到低排列发现的问题,并在末尾列出按优先级排序的修复清单
A01:2021 — 访问控制失效
风险: 用户可能访问其他用户的数据或执行未经授权的操作。
检查要点:
- 每个 API 端点是否都强制执行授权?
- 是否存在 IDOR(不安全的直接对象引用)漏洞——用户能否通过修改 ID 参数来访问他人的数据?
- 管理界面是否会验证用户角色?
- 访问控制是否在服务端执行(而不只是隐藏 UI 元素)?
- CORS 策略是否过于宽松?
易受攻击的代码示例
# ❌ Vulnerable: No authorization check — any user can view others' orders by changing user_id
@app.route("/api/orders/<user_id>")
def get_orders(user_id):
orders = db.query(f"SELECT * FROM orders WHERE user_id = {user_id}")
return jsonify(orders)
修复示例
# ✅ Fixed: Verify the authenticated user can only access their own data
@app.route("/api/orders")
@login_required
def get_orders():
current_user_id = get_current_user().id
orders = db.query("SELECT * FROM orders WHERE user_id = %s", (current_user_id,))
return jsonify(orders)
A02:2021 — 加密失效
风险: 敏感数据(密码、信用卡号、个人信息)未加密,或使用了薄弱的加密算法。
检查要点:
- 密码是否使用安全的哈希算法(bcrypt/scrypt/argon2)存储,而不是 MD5/SHA1?
- 是否强制使用 HTTPS 传输敏感数据?
- 源代码中是否硬编码了加密密钥?
- 是否使用了已弃用的加密算法(DES、RC4、MD5)?
- 敏感的数据库字段是否已进行静态加密?
易受攻击的代码示例
# ❌ Vulnerable: MD5 for password storage, hardcoded secret key
import hashlib
SECRET_KEY = "my-secret-key-123"
def save_password(password):
hashed = hashlib.md5(password.encode()).hexdigest()
db.save(hashed)
修复示例
# ✅ Fixed: bcrypt for password hashing, secret key from environment variable
import bcrypt
import os
SECRET_KEY = os.environ["SECRET_KEY"]
def save_password(password):
salt = bcrypt.gensalt()
hashed = bcrypt.hashpw(password.encode(), salt)
db.save(hashed)
A03:2021 — 注入
风险: 用户输入被直接拼接到 SQL、操作系统命令、LDAP 查询等内容中,使攻击者能够执行任意查询或命令。
检查项:
- SQL 查询是否使用参数化查询/ORM(而不是字符串拼接)?
- 是否存在拼接用户输入的
os.system()或subprocess.call(shell=True)调用? - 模板渲染是否正确转义用户输入(防止 XSS)?
- LDAP/XPath/NoSQL 查询中的特殊字符是否经过过滤?
- 是否将未经筛选的用户输入直接写入日志(日志注入)?
SQL 注入 — 易受攻击的代码
# ❌ Vulnerable: String-concatenated SQL — attacker can input ' OR 1=1 --
@app.route("/api/user")
def get_user():
username = request.args.get("username")
query = f"SELECT * FROM users WHERE username = '{username}'"
result = db.execute(query)
return jsonify(result)
SQL 注入 — 修复方案
# ✅ Fixed: Parameterized query
@app.route("/api/user")
def get_user():
username = request.args.get("username")
result = db.execute(
"SELECT * FROM users WHERE username = %s",
(username,)
)
return jsonify(result)
命令注入 — 易受攻击的代码
# ❌ Vulnerable: User input concatenated directly into shell command
import os
def ping_host(host):
os.system(f"ping -c 4 {host}")
命令注入 — 修复方案
# ✅ Fixed: Use subprocess with list arguments, shell disabled
import subprocess
import re
def ping_host(host):
if not re.match(r'^[a-zA-Z0-9.\-]+$', host):
raise ValueError("Invalid hostname")
subprocess.run(["ping", "-c", "4", host], check=True)
A04:2021 — 不安全的设计
风险: 业务逻辑设计存在缺陷,即使实现完美也无法修复。
检查项:
- 关键操作是否设置了速率限制?
- 密码重置流程是否可能被滥用(枚举用户名、暴力破解验证码)?
- 敏感操作(付款、转账)是否需要二次确认?
- 是否存在没有上限的批量操作端点?
- 业务工作流是否可能被乱序执行(例如,跳过付款就完成订单)?
易受攻击的代码示例
# ❌ Vulnerable: No attempt limit on verification code — can be brute-forced
@app.route("/api/verify-code", methods=["POST"])
def verify_code():
code = request.json["code"]
stored_code = session.get("verification_code")
if code == stored_code:
return jsonify({"status": "verified"})
return jsonify({"status": "invalid"}), 400
修复示例
# ✅ Fixed: Added attempt limit and expiration
@app.route("/api/verify-code", methods=["POST"])
def verify_code():
attempts = session.get("verify_attempts", 0)
if attempts >= 5:
return jsonify({"error": "Too many attempts, please request a new code"}), 429
code = request.json["code"]
stored = session.get("verification_code")
expire_at = session.get("code_expire_at", 0)
if time.time() > expire_at:
return jsonify({"error": "Verification code has expired"}), 400
session["verify_attempts"] = attempts + 1
if code == stored:
session.pop("verify_attempts", None)
return jsonify({"status": "verified"})
return jsonify({"status": "invalid"}), 400
A05:2021 — 安全配置错误
风险: 应用程序或服务使用默认配置、启用不必要的功能,或在错误消息中泄露敏感信息。
检查项:
- 生产环境中是否已禁用 DEBUG 模式?
- 错误页面是否泄露堆栈跟踪、数据库版本等信息?
- 是否仍在使用默认凭据?
- HTTP 响应是否包含安全标头(X-Frame-Options、Content-Security-Policy 等)?
- 是否已禁用不必要的 HTTP 方法(PUT、DELETE、TRACE)?
- 是否已禁用目录列表?
易受攻击的代码示例
# ❌ Vulnerable: DEBUG enabled in production, leaking sensitive information
app = Flask(__name__)
app.config["DEBUG"] = True
app.config["SECRET_KEY"] = "default-secret"
@app.errorhandler(500)
def error_handler(e):
return jsonify({"error": str(e), "traceback": traceback.format_exc()}), 500
修复示例
# ✅ Fixed: Configuration from environment variables, DEBUG off in production
import os
app = Flask(__name__)
app.config["DEBUG"] = os.environ.get("FLASK_DEBUG", "false").lower() == "true"
app.config["SECRET_KEY"] = os.environ["FLASK_SECRET_KEY"]
@app.errorhandler(500)
def error_handler(e):
app.logger.error(f"Internal error: {e}")
return jsonify({"error": "Internal server error, please try again later"}), 500
A06:2021 — 易受攻击和过时的组件
风险: 使用存在已知漏洞的第三方库或框架版本。
检查项:
- 依赖项是否存在已知 CVE(使用
pip audit、npm audit、snyk等工具扫描)? - 是否存在很久没有更新的依赖项?
- 是否在使用已停止维护的库?
- 锁定文件(package-lock.json / requirements.txt)是否纳入版本控制?
- 是否有自动更新依赖项的机制(Dependabot 等)?
扫描命令
# Python projects
pip audit
# Node.js projects
npm audit
# General scanning
# Use open-source tools like trivy or grype to scan container/project dependencies
修复建议
# Update vulnerable packages
pip install --upgrade package_name
# Auto-fix npm vulnerabilities
npm audit fix
# Pin dependency versions to prevent implicit upgrades
pip freeze > requirements.txt
A07:2021 — 身份识别和身份验证失败
风险: 身份验证机制存在缺陷,可能导致暴力破解、凭据填充或会话劫持攻击。
检查项:
- 登录失败是否设置了速率限制(账户锁定/延迟)?
- 密码策略是否合理(最短长度、复杂度要求)?
- 注销时是否会使会话令牌失效?
- 会话 ID 是否足够随机且不可预测?
- 是否支持对敏感操作进行 MFA?
- JWT 令牌是否经过签名和有效期验证?
易受攻击的代码示例
# ❌ Vulnerable: JWT signature not verified, accepts alg=none
import jwt
def verify_token(token):
payload = jwt.decode(token, options={"verify_signature": False})
return payload
修复示例
# ✅ Fixed: Enforce signature and expiration verification, specify algorithm
import jwt
import os
JWT_SECRET = os.environ["JWT_SECRET"]
def verify_token(token):
try:
payload = jwt.decode(
token,
JWT_SECRET,
algorithms=["HS256"],
options={"require": ["exp", "iat", "sub"]}
)
return payload
except jwt.ExpiredSignatureError:
raise AuthError("Token has expired")
except jwt.InvalidTokenError:
raise AuthError("Invalid token")
A08:2021 — 软件和数据完整性故障
风险: 未能验证软件更新、关键数据或 CI/CD 流水线的完整性,可能导致供应链攻击或数据篡改。
检查项:
- 反序列化是否使用了不安全的方法(例如,对不可信数据使用 Python 的
pickle.loads)? - CI/CD 流水线是否进行完整性验证?
- 第三方 CDN 资源是否使用 SRI(子资源完整性)?
- 是否对关键配置文件的修改进行审计日志记录?
- 自动更新机制是否会验证签名?
易受攻击的代码示例
# ❌ Vulnerable: Deserializing untrusted data — can lead to remote code execution
import pickle
@app.route("/api/import", methods=["POST"])
def import_data():
data = pickle.loads(request.data)
process(data)
return "OK"
修复示例
# ✅ Fixed: Use a safe data format (JSON), refuse to deserialize arbitrary objects
import json
@app.route("/api/import", methods=["POST"])
def import_data():
try:
data = json.loads(request.data)
except json.JSONDecodeError:
return jsonify({"error": "Invalid JSON format"}), 400
process(data)
return "OK"
A09:2021 — 安全日志记录与监控失效
风险: 缺乏安全事件日志记录和监控,导致无法及时检测和响应攻击。
检查点:
- 是否记录了登录成功和失败事件?
- 是否对敏感操作(权限变更、数据删除)进行审计日志记录?
- 密码、令牌或其他敏感数据是否被意外记录在日志中?
- 日志是否防篡改(集中存储、仅追加写入)?
- 是否为异常行为配置了警报(例如,大量登录失败)?
易受攻击的代码示例
# ❌ Vulnerable: No logging on login failure, and plaintext password in logs
def login(username, password):
user = db.get_user(username)
if not user or not check_password(password, user.password_hash):
print(f"Login failed for {username} with password {password}")
return None
return create_session(user)
修复示例
# ✅ Fixed: Log security events without logging sensitive data
import logging
security_logger = logging.getLogger("security")
def login(username, password):
user = db.get_user(username)
if not user or not check_password(password, user.password_hash):
security_logger.warning(
"Login failed",
extra={"username": username, "ip": request.remote_addr}
)
return None
security_logger.info(
"Login successful",
extra={"username": username, "ip": request.remote_addr}
)
return create_session(user)
A10:2021 — 服务器端请求伪造(SSRF)
风险: 应用接受用户提供的 URL 并在服务器端发起请求,使攻击者能够访问内部网络资源或云元数据。
检查点:
- 是否存在接受用户提供的 URL 并在服务器端发起请求的功能(获取图片、URL 预览、Webhook 回调)?
- 是否限制了 URL 协议(仅允许 http/https)?
- 是否过滤了内部 IP 范围(127.0.0.0/8、10.0.0.0/8、172.16.0.0/12、192.168.0.0/16)?
- 是否屏蔽了云元数据地址(169.254.169.254)?
- 是否具备防范 DNS 重绑定攻击的保护措施?
易受攻击的代码示例
# ❌ Vulnerable: Directly requesting user-supplied URL — can access internal network and cloud metadata
import requests
@app.route("/api/fetch-url")
def fetch_url():
url = request.args.get("url")
response = requests.get(url)
return response.text
修复示例
# ✅ Fixed: Validate URL protocol and target address, block internal network access
import requests
import ipaddress
from urllib.parse import urlparse
import socket
BLOCKED_NETWORKS = [
ipaddress.ip_network("127.0.0.0/8"),
ipaddress.ip_network("10.0.0.0/8"),
ipaddress.ip_network("172.16.0.0/12"),
ipaddress.ip_network("192.168.0.0/16"),
ipaddress.ip_network("169.254.0.0/16"),
]
def is_safe_url(url):
parsed = urlparse(url)
if parsed.scheme not in ("http", "https"):
return False
try:
ip = ipaddress.ip_address(socket.gethostbyname(parsed.hostname))
for network in BLOCKED_NETWORKS:
if ip in network:
return False
except (socket.gaierror, ValueError):
return False
return True
@app.route("/api/fetch-url")
def fetch_url():
url = request.args.get("url")
if not is_safe_url(url):
return jsonify({"error": "Access to this address is not allowed"}), 403
response = requests.get(url, timeout=10, allow_redirects=False)
return response.text
审查报告模板
完成审查后,请按以下格式输出报告。必须包含全部 10 项——无发现的问题标记为通过:
# OWASP Top 10 Security Review Report
## Review Summary
- Scope: [list of files/modules]
- Date: [date]
- Risk summary: RED High x | YELLOW Medium x | GREEN Low x | PASS No findings x
## Findings (sorted by severity, descending)
### [Severity] [OWASP ID] — [Issue Title]
- **Location:** [file:line_number]
- **Description:** [issue description]
- **Impact:** [potential consequences]
- **Fix:** (ready-to-use code, not just a description)
### PASS A0X — [Category] — No issues found
## Remediation Priority
1. [Most urgent fix — rationale]
2. [Next priority — rationale]
3. ...
推荐的开源安全工具
| 工具 | 语言 | 用途 |
|---|---|---|
bandit | Python | Python 代码安全扫描 |
semgrep | 多语言 | 基于规则的代码扫描 |
eslint-plugin-security | JavaScript | JS 安全规则 |
npm audit / pip audit | JS / Python | 依赖项漏洞扫描 |
trivy | 多语言 | 容器和依赖项扫描 |
sqlmap | — | SQL 注入检测 |
OWASP ZAP | — | Web 应用动态扫描 |
注意: 此检查清单是辅助审查工具,不能替代专业渗透测试。对于高安全性系统,应结合自动化扫描 + 手动代码审计 + 渗透测试。