mcpField
S
secure-code-review

secure-code-review

github.com/zebbern安全4.7k49 次安装2 天前更新
代码仓库

关于 secure-code-review

面向 Web 应用安全审查的 OWASP Top 10(2021)检查清单,逐项提供风险说明、检查点和修复示例。适合需要按类别或完整范围审查代码的安全人员。

主要功能

  1. 01覆盖 OWASP Top 10(2021)
  2. 02逐项列出代码检查点
  3. 03提供易受攻击代码示例
  4. 04附带修复建议与代码
  5. 05按风险级别分类发现

适用场景

  1. 01检查 Web 应用中的 SQL 注入风险
  2. 02审查 API 端点的 SSRF 漏洞
  3. 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日志记录和监控失效日志中是否记录了明文密码?是否记录了登录失败?
A10SSRF是否对用户提供的 URL 进行过滤,以阻止其指向内部 IP?

审查流程 SOP

核心原则:宁可误报,也不要漏报。

  1. 定义范围 — 确定要审查的文件、模块或代码片段
  2. 全面覆盖检查 — 按顺序扫描 A01-A10。报告中必须包含每一项(没有发现问题的项目也要标记为通过)。默认行为仅报告已发现的问题;此流程要求全面覆盖,确保没有遗漏
  3. 风险分类 — 为每项发现标注风险:
    • RED 高:可直接利用(RCE、SQL 注入、可访问内部网络的 SSRF、明文密码存储)
    • YELLOW 中:在特定条件下可利用(缺少速率限制、密码策略薄弱、静态令牌)
    • GREEN 低:纵深防御存在缺口,但没有直接利用途径(缺少安全响应头、日志记录不足)
  4. 每项发现都必须包含可直接使用的修复代码(实际代码,而不只是描述)。注明具体的 file:line_number
  5. 输出审查报告 — 使用下方模板,按严重性从高到低排列发现的问题,并在末尾列出按优先级排序的修复清单

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. ...

推荐的开源安全工具

工具语言用途
banditPythonPython 代码安全扫描
semgrep多语言基于规则的代码扫描
eslint-plugin-securityJavaScriptJS 安全规则
npm audit / pip auditJS / Python依赖项漏洞扫描
trivy多语言容器和依赖项扫描
sqlmap—SQL 注入检测
OWASP ZAP—Web 应用动态扫描

注意: 此检查清单是辅助审查工具,不能替代专业渗透测试。对于高安全性系统,应结合自动化扫描 + 手动代码审计 + 渗透测试。

评论

支持 Markdown