Python Web 开发进阶实战:安全加固实战 —— 基于 OWASP Top 10 的全栈防御体系

第一章:为什么安全不能"事后补"?

1.1 真实代价

事件 后果
XSS 窃取会话 用户账号被接管
SQL 注入 全库数据泄露(如 2017 Equifax)
IDOR(越权) A 用户读取 B 用户的医疗记录
无速率限制 暴力破解管理员密码

原则安全是功能,不是附加项。

1.2 OWASP Top 10(2021)概览

风险 本篇覆盖方案
A01: Broken Access Control RBAC + 对象级权限校验
A02: Cryptographic Failures HTTPS + 安全 JWT + 敏感字段脱敏
A03: Injection 参数化查询 + 输入验证
A04: Insecure Design 安全开发生命周期(SDL)
A05: Security Misconfiguration 安全头 + 最小权限原则
A06: Vulnerable Components 依赖扫描(safety / npm audit)
A07: Identification Failures 多因素认证 + 强密码策略
A08: Software Data Integrity 未涉及(侧重供应链)
A09: Security Logging 日志脱敏 + 异常行为告警
A10: SSRF URL 白名单 + 禁用内部协议

本篇重点解决 A01--A07、A09、A10


第二章:前端安全加固

2.1 防御 XSS(跨站脚本)

风险场景
复制代码
<!-- 危险!直接渲染用户输入 -->
<div v-html="userComment"></div>
安全方案:DOMPurify

安装:

复制代码
npm install dompurify

使用:

复制代码
// utils/sanitize.ts
import DOMPurify from 'dompurify'

export const sanitizeHTML = (html: string): string => {
  return DOMPurify.sanitize(html, {
    ALLOWED_TAGS: ['b', 'i', 'em', 'strong'], // 仅允许安全标签
    FORBID_ATTR: ['style', 'onerror']        // 禁用危险属性
  })
}

在组件中:

复制代码
<template>
  <div v-html="sanitizedComment"></div>
</template>

<script setup lang="ts">
import { sanitizeHTML } from '@/utils/sanitize'
const props = defineProps<{ comment: string }>()
const sanitizedComment = sanitizeHTML(props.comment)
</script>

注意 :即使使用 Vue 的 {``{ }} 插值,若后端返回 HTML 片段仍需净化。

2.2 内容安全策略(CSP)

通过 HTTP 头限制资源加载:

复制代码
# nginx.conf
add_header Content-Security-Policy "
  default-src 'self';
  script-src 'self' 'unsafe-inline' https://cdn.example.com;
  style-src 'self' 'unsafe-inline';
  img-src 'self' data: https:;
  font-src 'self';
  connect-src 'self' https://api.yourdomain.com;
  frame-ancestors 'none';  # 防点击劫持
  object-src 'none';
" always;

关键指令

  • script-src:禁止内联脚本(但 Vue 开发模式需 'unsafe-inline',生产应移除)
  • connect-src:限制 AJAX 请求目标
  • frame-ancestors 'none':防止页面被嵌入 iframe

虽前端不直接操作 Cookie,但需确保后端设置正确:

复制代码
Set-Cookie: session_id=abc123; 
  HttpOnly;     ← 禁止 JS 访问
  Secure;       ← 仅 HTTPS 传输
  SameSite=Lax; ← 防 CSRF(宽松模式)
  Path=/;

注意 :JWT 通常存于 localStorage,但更推荐 HttpOnly Cookie(防 XSS 窃取)。


第三章:后端安全核心实践(Flask)

3.1 防 SQL 注入

错误做法
复制代码
# 危险!拼接 SQL
query = f"SELECT * FROM users WHERE id = {user_id}"
正确做法:参数化查询
复制代码
# SQLAlchemy ORM(自动参数化)
user = User.query.filter(User.id == user_id).first()

# 原生 SQL(显式参数)
db.session.execute(
    text("SELECT * FROM users WHERE id = :user_id"),
    {"user_id": user_id}
)

验证 :用 ' OR '1'='1 测试,应返回空或报错。

3.2 越权访问控制(Broken Access Control)

场景:用户 A 尝试访问 /api/users/123/profile(123 是用户 B)

错误实现

复制代码
@app.route('/api/users/<int:user_id>/profile')
@jwt_required()
def get_profile(user_id):
    # 直接返回,未校验当前用户是否为 owner
    return User.query.get(user_id).to_dict()

安全实现

复制代码
@app.route('/api/users/<int:user_id>/profile')
@jwt_required()
def get_profile(user_id):
    current_user_id = get_jwt_identity()
    
    # 关键:校验资源归属
    if current_user_id != user_id:
        # 或检查是否为管理员
        if not current_user.is_admin:
            abort(403, "Forbidden")
    
    return User.query.get(user_id).to_dict()

进阶 :使用 对象级权限库 (如 Flask-Principal)。

3.3 CSRF 防护(针对传统表单)

注意:纯 API + JWT 通常无需 CSRF(因无 Cookie 自动携带),但若使用 Session 则需防护。

启用 Flask-WTF:

复制代码
from flask_wtf.csrf import CSRFProtect
csrf = CSRFProtect(app)

前端发送 Token:

复制代码
// 从 meta 标签获取
const csrfToken = document.querySelector('meta[name="csrf-token"]').content;
axios.defaults.headers.common['X-CSRFToken'] = csrfToken;

3.4 速率限制(防暴力破解)

安装 flask-limiter

复制代码
pip install flask-limiter

配置:

复制代码
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address

limiter = Limiter(
    app,
    key_func=get_remote_address,
    default_limits=["200 per day", "50 per hour"]
)

@app.route("/auth/login", methods=["POST"])
@limiter.limit("5 per minute")  # 登录接口严格限制
def login():
    # ...

策略

  • 登录:5 次/分钟
  • API:100 次/小时/IP
  • 管理员接口:更严格

第四章:API 与认证安全

4.1 JWT 安全最佳实践

风险 防御措施
Token 泄露 短有效期(access_token=15min)+ refresh_token 安全存储
重放攻击 使用 jti(JWT ID) + 黑名单(Redis 存储已注销 token)
算法混淆 强制指定算法(如 HS256),拒绝 alg: none

Token 注销示例

复制代码
@app.route('/auth/logout', methods=['POST'])
@jwt_required()
def logout():
    jti = get_jwt()['jti']
    # 加入黑名单,有效期 = 原 token 剩余时间
    redis.setex(f"blacklist:{jti}", expires_in, "true")
    return {"msg": "Logged out"}

验证时检查黑名单

复制代码
@jwt.token_in_blocklist_loader
def check_if_token_revoked(jwt_header, jwt_payload):
    jti = jwt_payload["jti"]
    return redis.exists(f"blacklist:{jti}")

4.2 敏感数据脱敏

响应中隐藏密码、身份证等:

复制代码
class UserSchema(Schema):
    id = fields.Int()
    username = fields.Str()
    email = fields.Email()
    # 不输出 password_hash
    # 身份证部分打码
    id_card = fields.Method("mask_id_card")

    def mask_id_card(self, obj):
        if obj.id_card:
            return obj.id_card[:6] + "****" + obj.id_card[-4:]
        return None

原则永远不要在日志、响应、前端存储中明文出现敏感字段。


第五章:安全配置与依赖管理

5.1 安全 HTTP 头

使用 flask-talisman 自动设置:

复制代码
from flask_talisman import Talisman

Talisman(app,
    force_https=True,
    strict_transport_security=True,
    content_security_policy=csp_policy,  # 同前端 CSP
    referrer_policy="strict-origin-when-cross-origin",
    x_frame_options="DENY"
)

5.2 依赖漏洞扫描

Python

复制代码
pip install safety
safety check -r requirements.txt

Node.js

复制代码
npm audit --audit-level high

CI 集成(GitHub Actions):

复制代码
- name: Check Python dependencies
  run: |
    pip install safety
    safety check -r requirements.txt --exit-code

策略:CI 中发现高危漏洞则失败。


第六章:自动化安全测试

6.1 静态代码分析

Python(Bandit)

复制代码
pip install bandit
bandit -r ./app -f json -o bandit-report.json

常见检测项:

  • 硬编码密码
  • 不安全的 pickle 使用
  • 未验证的重定向

前端(ESLint 安全插件)

复制代码
npm install -D eslint-plugin-security

.eslintrc.js

复制代码
plugins: ['security'],
rules: {
  'security/detect-object-injection': 'error',
  'security/detect-non-literal-fs-filename': 'error'
}

6.2 动态扫描:OWASP ZAP

启动 ZAP 扫描

复制代码
docker run -t owasp/zap2docker-stable zap-baseline.py \
  -t https://your-staging-domain.com \
  -r zap-report.html

CI 集成

复制代码
- name: Run ZAP Baseline Scan
  run: |
    docker run --network host -v $(pwd):/zap/wrk:z \
      owasp/zap2docker-stable zap-baseline.py \
      -t http://localhost:5000 \
      -x zap-report.xml

注意:ZAP 可能触发大量请求,仅用于测试环境。


第七章:渗透测试实战(手动验证)

7.1 测试越权(IDOR)

  1. 用用户 A 登录,获取其 user_id=100
  2. 修改请求为 GET /api/users/101/profile
  3. 预期:返回 403,而非用户 101 的数据

7.2 测试 XSS

  1. 在用户昵称输入 <script>alert(1)</script>
  2. 保存后刷新页面
  3. 预期:脚本不执行,仅显示文本

7.3 测试 SSRF(服务器端请求伪造)

若应用支持 webhook 或图片抓取:

复制代码
POST /api/fetch-image
{ "url": "http://169.254.169.254/latest/meta-data" }  # AWS 元数据

预期:返回错误,禁止访问内网 IP。

防御代码

复制代码
from urllib.parse import urlparse
import ipaddress

def is_safe_url(url):
    try:
        result = urlparse(url)
        # 检查是否为内网 IP
        addr = ipaddress.ip_address(socket.gethostbyname(result.hostname))
        return not addr.is_private and not addr.is_loopback
    except:
        return False

第八章:CI/CD 安全门禁

8.1 GitHub Actions 安全工作流

复制代码
name: Security Scan

on: [push, pull_request]

jobs:
  security:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      - name: Setup Python
        uses: actions/setup-python@v4
        with: { python-version: '3.11' }
      
      - name: Install deps
        run: pip install -r requirements.txt -r requirements-dev.txt
      
      - name: Bandit Scan
        run: bandit -r ./app --exit-zero -f json | tee bandit.json
        
      - name: Safety Check
        run: safety check -r requirements.txt --exit-code
        
      - name: ESLint Security
        run: npm run lint:security
        
      - name: Fail on High Risk
        run: |
          if grep -q '"severity": "HIGH"' bandit.json; then
            echo "High risk found!"; exit 1
          fi

效果:PR 中若含高危漏洞,无法合并。


第九章:安全开发流程(SDL)

将安全融入开发全周期:

阶段 活动
需求 威胁建模(STRIDE)
设计 安全架构评审
编码 安全编码规范 + SAST
测试 DAST + 渗透测试
发布 依赖扫描 + 配置审计
运维 日志监控 + 应急响应

工具链

  • 威胁建模:Microsoft Threat Modeling Tool
  • SAST:Bandit, Semgrep
  • DAST:OWASP ZAP, Burp Suite

总结:构建纵深防御体系

相关推荐
崔庆才丨静觅13 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby606113 小时前
完成前端时间处理的另一块版图
前端·github·web components
Hello.Reader13 小时前
Flink ZooKeeper HA 实战原理、必配项、Kerberos、安全与稳定性调优
安全·zookeeper·flink
掘了13 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅14 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
ValhallaCoder14 小时前
hot100-二叉树I
数据结构·python·算法·二叉树
崔庆才丨静觅14 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
智驱力人工智能14 小时前
小区高空抛物AI实时预警方案 筑牢社区头顶安全的实践 高空抛物检测 高空抛物监控安装教程 高空抛物误报率优化方案 高空抛物监控案例分享
人工智能·深度学习·opencv·算法·安全·yolo·边缘计算
猫头虎14 小时前
如何排查并解决项目启动时报错Error encountered while processing: java.io.IOException: closed 的问题
java·开发语言·jvm·spring boot·python·开源·maven
崔庆才丨静觅14 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端