什么是 OWASP 网络应用程序 10 强?

OWASP Top 10 是最广为人知的网络应用安全意识文件。2025 年版反映了最新的威胁形势,引入了软件供应链故障和异常情况处理不当等新类别。

1️⃣ A01 - 损坏的访问控制

Critical

概述

访问控制执行政策,使用户不能在其预期权限之外行事。失败通常会导致未经授权的信息泄露、数据修改或破坏,或在用户权限之外执行业务功能。

风险

攻击者可以利用访问控制缺陷访问其他用户的账户、查看敏感文件、修改其他用户的数据或更改访问权限。

漏洞代码示例

Python ❌ Bad
# User ID taken directly from request without authorization check
@app.route('/api/user/<user_id>/profile')
def get_profile(user_id):
    user = db.get_user(user_id)  # No ownership check!
    return jsonify(user.to_dict())

安全代码示例

Python ✅ Good
@app.route('/api/user/<user_id>/profile')
@login_required
def get_profile(user_id):
    # Verify the requesting user owns this resource
    if current_user.id != user_id and not current_user.is_admin:
        abort(403)

    user = db.get_user(user_id)
    if not user:
        abort(404)
    return jsonify(user.to_dict())

缓解措施清单

2️⃣ A02 - 安全配置错误

High

概述

当安全设置的定义、实施或维护不正确时,就会出现安全配置错误。这包括缺少安全加固、启用不必要的功能、密码未变的默认账户、过于冗长的错误信息以及错误配置的 HTTP 安全标头。

风险

配置错误的服务器、框架或云服务可能会暴露敏感数据,导致未经授权的访问,或为攻击者提供信息以策划进一步的攻击。

漏洞代码示例

Python ❌ Bad
# Debug mode enabled in production, verbose errors exposed
app = Flask(__name__)
app.config['DEBUG'] = True  # Exposes stack traces!
app.config['SECRET_KEY'] = 'default-secret'  # Default key!

安全代码示例

Python ✅ Good
import os

app = Flask(__name__)
app.config['DEBUG'] = False
app.config['SECRET_KEY'] = os.environ['SECRET_KEY']

@app.after_request
def set_security_headers(response):
    response.headers['X-Content-Type-Options'] = 'nosniff'
    response.headers['X-Frame-Options'] = 'DENY'
    response.headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains'
    response.headers['Content-Security-Policy'] = "default-src 'self'"
    return response

缓解措施清单

3️⃣ A03 - 软件供应链故障

High

概述

2025 年新增内容。重点关注与第三方组件、依赖关系和 CI/CD 管道相关的风险。攻击者通过破坏软件包、在开源库中注入恶意代码或利用构建管道漏洞来攻击软件供应链。

风险

一个受攻击的依赖项可能会影响数千个应用程序。供应链攻击可导致数据失窃、后门安装或系统完全崩溃,而检测却微乎其微。

漏洞代码示例

JavaScript ❌ Bad
// package.json with unpinned dependencies
{
  "dependencies": {
    "express": "*",           // Any version!
    "lodash": "^4.0.0",       // Wide range
    "unknown-pkg": "^1.0.0"   // Unvetted package
  }
}

安全代码示例

JavaScript ✅ Good
// package.json with pinned versions + lockfile + audit
{
  "dependencies": {
    "express": "4.21.2",
    "lodash": "4.17.21"
  },
  "scripts": {
    "preinstall": "npx npm-audit-resolver",
    "integrity-check": "npm audit signatures"
  }
}
// Also: use package-lock.json, enable Dependabot/Renovate,
// generate SBOM, verify package provenance

缓解措施清单

4️⃣ A04 - 加密失败

High

概述

与加密相关的故障往往会导致敏感数据泄露。这包括使用过时的算法、密钥生成不力、传输或静态数据加密缺失以及证书验证不当。

风险

加密薄弱或缺失会暴露密码、信用卡号、健康记录、个人信息和商业机密,可能导致违反法规(GDPR、PCI DSS)。

漏洞代码示例

Python ❌ Bad
import hashlib

# Storing passwords with weak hashing
def store_password(password: str) -> str:
    return hashlib.md5(password.encode()).hexdigest()  # MD5 is broken!

安全代码示例

Python ✅ Good
import bcrypt

def store_password(password: str) -> bytes:
    # Use bcrypt with automatic salting
    salt = bcrypt.gensalt(rounds=12)
    return bcrypt.hashpw(password.encode(), salt)

def verify_password(password: str, hashed: bytes) -> bool:
    return bcrypt.checkpw(password.encode(), hashed)

缓解措施清单

5️⃣A05-注入

Critical

概述

当不受信任的数据作为命令或查询的一部分发送到解释器时,就会出现注入缺陷。SQL 注入、NoSQL 注入、操作系统命令注入和跨站脚本 (XSS) 是最常见的形式。敌对数据会诱使解释器执行非预期命令。

风险

注入可导致数据丢失、损坏、未授权访问、主机完全接管或拒绝服务。仅 SQL 注入就仍然是最危险、最普遍的攻击载体之一。

漏洞代码示例

Python ❌ Bad
# SQL injection via string concatenation
def get_user(username: str):
    query = f"SELECT * FROM users WHERE name = '{username}'"
    return db.execute(query)  # username = "' OR '1'='1"

安全代码示例

Python ✅ Good
# Parameterized queries prevent SQL injection
def get_user(username: str):
    query = "SELECT * FROM users WHERE name = %s"
    return db.execute(query, (username,))

# For XSS prevention, escape output
from markupsafe import escape

def render_comment(comment: str) -> str:
    return f"<p>{escape(comment)}</p>"

缓解措施清单

6️⃣ A06 - 不安全设计

High

概述

不安全设计是指与设计和架构缺陷有关的风险。它要求使用威胁建模、安全设计模式和参考架构。不安全的设计无法通过完美的实现来修复;所需的安全控制措施从来都不是为了抵御特定攻击而创建的。

风险

设计缺陷会导致业务逻辑漏洞,而这些漏洞很难通过自动化工具检测到。敏感操作的速率限制缺失、关键操作缺乏多因素身份验证或欺诈检测不足都是设计层面的失误。

漏洞代码示例

Python ❌ Bad
# Password reset with no rate limit or verification
@app.route('/reset-password', methods=['POST'])
def reset_password():
    email = request.form['email']
    new_pass = request.form['new_password']
    user = db.find_by_email(email)
    user.password = hash_password(new_pass)  # No token verification!
    db.save(user)

安全代码示例

Python ✅ Good
from datetime import datetime, timedelta

@app.route('/reset-password', methods=['POST'])
@rate_limit("3/hour")
def reset_password():
    token = request.form['token']
    new_pass = request.form['new_password']

    # Verify time-limited, single-use token
    reset_req = db.find_reset_token(token)
    if not reset_req or reset_req.used or reset_req.expires < datetime.utcnow():
        abort(400, "Invalid or expired token")

    # Enforce password complexity
    if not meets_password_policy(new_pass):
        abort(400, "Password does not meet requirements")

    reset_req.user.password = hash_password(new_pass)
    reset_req.used = True
    db.save_all([reset_req.user, reset_req])

缓解措施清单

7️⃣ A07 - 验证失败

High

概述

用户身份确认、身份验证和会话管理至关重要。如果应用程序允许插入凭证、暴力破解、弱密码,或会话管理存在缺陷(如登录后会话 ID 不轮换),就会出现身份验证失败。

风险

攻击者可以通过自动凭据填充、暴力或会话劫持来访问用户账户。账户受损可能导致身份盗窃、欺诈和数据泄露。

漏洞代码示例

Python ❌ Bad
# No brute force protection, weak session handling
@app.route('/login', methods=['POST'])
def login():
    user = db.find_by_email(request.form['email'])
    if user and user.password == request.form['password']:  # Plain comparison!
        session['user'] = user.id  # Session ID not rotated
        return redirect('/dashboard')

安全代码示例

Python ✅ Good
from flask_limiter import Limiter

limiter = Limiter(app, default_limits=["100/hour"])

@app.route('/login', methods=['POST'])
@limiter.limit("5/minute")
def login():
    user = db.find_by_email(request.form['email'])
    if not user or not bcrypt.checkpw(
        request.form['password'].encode(), user.password_hash
    ):
        # Generic error to prevent user enumeration
        return "Invalid credentials", 401

    # Rotate session ID after authentication
    session.regenerate()
    session['user'] = user.id

    # Check for MFA requirement
    if user.mfa_enabled:
        return redirect('/mfa-verify')
    return redirect('/dashboard')

缓解措施清单

8️⃣ A08 - 软件或数据完整性故障

High

概述

软件和数据完整性故障与代码和基础架构无法防止完整性违规有关。这包括不安全的反序列化、使用不受信任的 CDN 或未经完整性验证的插件,以及没有签名更新的自动更新机制。

风险

不安全的反序列化可导致远程代码执行。从不具子资源完整性 (SRI) 的不受信任的 CDN 加载脚本,会让攻击者将恶意代码注入应用程序。

漏洞代码示例

HTML ❌ Bad
<!-- Loading external scripts without integrity checks -->
<script src="https://cdn.example.com/lib.js"></script>

<!-- Insecure deserialization in Python -->
import pickle
data = pickle.loads(user_input)  # Arbitrary code execution!

安全代码示例

HTML ✅ Good
<!-- Use Subresource Integrity (SRI) for external scripts -->
<script src="https://cdn.example.com/lib.js"
  integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQ8K..."
  crossorigin="anonymous"></script>

# Use safe deserialization in Python
import json
data = json.loads(user_input)  # Safe: only parses JSON data

缓解措施清单

9️⃣ A09 - 安全日志和警报故障

Medium

概述

如果没有足够的日志记录、监控和警报,就无法及时发现漏洞。日志记录不足、与事件响应系统集成不力以及缺乏实时警报,都会让攻击者进一步攻击系统、保持持久性并篡改或提取数据。

风险

如果没有适当的日志记录,攻击者就可以长期潜伏而不被发现。大多数漏洞研究表明,发现漏洞的平均时间超过 200 天,而且通常是由外部而非内部监控发现的。

漏洞代码示例

Python ❌ Bad
# No logging of security-relevant events
@app.route('/login', methods=['POST'])
def login():
    user = authenticate(request.form)
    if not user:
        return "Login failed", 401  # No record of failure
    return redirect('/dashboard')

安全代码示例

Python ✅ Good
import logging
from datetime import datetime

security_log = logging.getLogger('security')

@app.route('/login', methods=['POST'])
def login():
    user = authenticate(request.form)
    if not user:
        security_log.warning(
            "LOGIN_FAILED | ip=%s | email=%s | time=%s",
            request.remote_addr,
            request.form.get('email', 'unknown'),
            datetime.utcnow().isoformat(),
        )
        check_brute_force(request.remote_addr)
        return "Invalid credentials", 401

    security_log.info(
        "LOGIN_SUCCESS | user_id=%s | ip=%s",
        user.id, request.remote_addr,
    )
    return redirect('/dashboard')

缓解措施清单

🔟 A10 - 特殊情况处理不当

Medium

概述

2025 年新增功能。错误、异常和边缘情况处理不当的应用程序会暴露敏感信息、进入不一致状态或产生可利用的条件。这包括生产中的冗长错误信息、绕过安全控制的未捕获异常以及竞赛条件。

风险

错误处理不当会暴露堆栈跟踪、数据库细节或内部路径。安全检查中的竞赛条件可允许从检查时间到使用时间(TOCTOU)攻击,绕过授权或支付验证。

漏洞代码示例

Python ❌ Bad
# Exposing internal details in error messages
@app.route('/api/data')
def get_data():
    try:
        result = db.query(request.args['q'])
        return jsonify(result)
    except Exception as e:
        return jsonify({"error": str(e)}), 500  # Leaks DB details!

安全代码示例

Python ✅ Good
import uuid, logging

logger = logging.getLogger(__name__)

@app.errorhandler(Exception)
def handle_exception(e):
    error_id = str(uuid.uuid4())
    logger.error("Unhandled exception [%s]: %s", error_id, e, exc_info=True)

    # Return generic error with reference ID for support
    return jsonify({
        "error": "An internal error occurred",
        "reference": error_id,
    }), 500

@app.route('/api/data')
def get_data():
    q = request.args.get('q')
    if not q or not is_valid_query(q):
        return jsonify({"error": "Invalid query parameter"}), 400

    result = db.query(q)
    return jsonify(result)

缓解措施清单

📊 汇总表

身份证 脆弱性 严重性 关键缓解措施
A01损坏的门禁系统Critical服务器端访问检查、默认拒绝、记录所有权
A02安全配置错误High加固程序、安全标头、禁用默认设置
A03软件供应链失败High钉住的依赖关系、SBOM、依赖关系扫描
A04加密失败High强算法、TLS 1.2+、密钥管理
A05注射Critical参数化查询、输出编码、输入验证
A06不安全的设计High威胁建模、安全模式、滥用案例测试
A07身份验证失败HighMFA、暴力保护、会话轮换
A08软件或数据完整性故障HighSRI、签名更新、安全反序列化
A09安全日志和警报故障Medium集中记录、实时警报、事件响应
A10特殊情况处理不当Medium全局错误处理程序、通用信息、TOCTOU 预防