10 个最关键的网络应用程序安全风险以及如何降低这些风险。
OWASP Top 10 是最广为人知的网络应用安全意识文件。2025 年版反映了最新的威胁形势,引入了软件供应链故障和异常情况处理不当等新类别。
访问控制执行政策,使用户不能在其预期权限之外行事。失败通常会导致未经授权的信息泄露、数据修改或破坏,或在用户权限之外执行业务功能。
攻击者可以利用访问控制缺陷访问其他用户的账户、查看敏感文件、修改其他用户的数据或更改访问权限。
# 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())
@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())
当安全设置的定义、实施或维护不正确时,就会出现安全配置错误。这包括缺少安全加固、启用不必要的功能、密码未变的默认账户、过于冗长的错误信息以及错误配置的 HTTP 安全标头。
配置错误的服务器、框架或云服务可能会暴露敏感数据,导致未经授权的访问,或为攻击者提供信息以策划进一步的攻击。
# 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!
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
2025 年新增内容。重点关注与第三方组件、依赖关系和 CI/CD 管道相关的风险。攻击者通过破坏软件包、在开源库中注入恶意代码或利用构建管道漏洞来攻击软件供应链。
一个受攻击的依赖项可能会影响数千个应用程序。供应链攻击可导致数据失窃、后门安装或系统完全崩溃,而检测却微乎其微。
// package.json with unpinned dependencies { "dependencies": { "express": "*", // Any version! "lodash": "^4.0.0", // Wide range "unknown-pkg": "^1.0.0" // Unvetted package } }
// 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
与加密相关的故障往往会导致敏感数据泄露。这包括使用过时的算法、密钥生成不力、传输或静态数据加密缺失以及证书验证不当。
加密薄弱或缺失会暴露密码、信用卡号、健康记录、个人信息和商业机密,可能导致违反法规(GDPR、PCI DSS)。
import hashlib # Storing passwords with weak hashing def store_password(password: str) -> str: return hashlib.md5(password.encode()).hexdigest() # MD5 is broken!
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)
当不受信任的数据作为命令或查询的一部分发送到解释器时,就会出现注入缺陷。SQL 注入、NoSQL 注入、操作系统命令注入和跨站脚本 (XSS) 是最常见的形式。敌对数据会诱使解释器执行非预期命令。
注入可导致数据丢失、损坏、未授权访问、主机完全接管或拒绝服务。仅 SQL 注入就仍然是最危险、最普遍的攻击载体之一。
# 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"
# 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>"
不安全设计是指与设计和架构缺陷有关的风险。它要求使用威胁建模、安全设计模式和参考架构。不安全的设计无法通过完美的实现来修复;所需的安全控制措施从来都不是为了抵御特定攻击而创建的。
设计缺陷会导致业务逻辑漏洞,而这些漏洞很难通过自动化工具检测到。敏感操作的速率限制缺失、关键操作缺乏多因素身份验证或欺诈检测不足都是设计层面的失误。
# 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)
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])
用户身份确认、身份验证和会话管理至关重要。如果应用程序允许插入凭证、暴力破解、弱密码,或会话管理存在缺陷(如登录后会话 ID 不轮换),就会出现身份验证失败。
攻击者可以通过自动凭据填充、暴力或会话劫持来访问用户账户。账户受损可能导致身份盗窃、欺诈和数据泄露。
# 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')
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')
软件和数据完整性故障与代码和基础架构无法防止完整性违规有关。这包括不安全的反序列化、使用不受信任的 CDN 或未经完整性验证的插件,以及没有签名更新的自动更新机制。
不安全的反序列化可导致远程代码执行。从不具子资源完整性 (SRI) 的不受信任的 CDN 加载脚本,会让攻击者将恶意代码注入应用程序。
<!-- 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!
<!-- 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
如果没有足够的日志记录、监控和警报,就无法及时发现漏洞。日志记录不足、与事件响应系统集成不力以及缺乏实时警报,都会让攻击者进一步攻击系统、保持持久性并篡改或提取数据。
如果没有适当的日志记录,攻击者就可以长期潜伏而不被发现。大多数漏洞研究表明,发现漏洞的平均时间超过 200 天,而且通常是由外部而非内部监控发现的。
# 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')
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')
2025 年新增功能。错误、异常和边缘情况处理不当的应用程序会暴露敏感信息、进入不一致状态或产生可利用的条件。这包括生产中的冗长错误信息、绕过安全控制的未捕获异常以及竞赛条件。
错误处理不当会暴露堆栈跟踪、数据库细节或内部路径。安全检查中的竞赛条件可允许从检查时间到使用时间(TOCTOU)攻击,绕过授权或支付验证。
# 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!
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 | 身份验证失败 | High | MFA、暴力保护、会话轮换 |
| A08 | 软件或数据完整性故障 | High | SRI、签名更新、安全反序列化 |
| A09 | 安全日志和警报故障 | Medium | 集中记录、实时警报、事件响应 |
| A10 | 特殊情况处理不当 | Medium | 全局错误处理程序、通用信息、TOCTOU 预防 |