HTTP 协议详解教程 / 第 16 章:HTTP 安全
第 16 章:HTTP 安全
安全是 Web 应用的生命线。本章全面介绍 HTTPS/TLS、各种安全头部以及常见攻击的防御手段。
16.1 HTTPS 与 TLS
为什么需要 HTTPS
| 威胁 |
HTTP |
HTTPS |
| 窃听 |
✗ 明文传输 |
✓ 加密传输 |
| 篡改 |
✗ 可被修改 |
✓ 完整性校验 |
| 冒充 |
✗ 无法验证身份 |
✓ 证书验证 |
TLS 握手流程(TLS 1.3)
客户端 服务器
│ │
│── ClientHello ──────────────────→│
│ 支持的密码套件、密钥共享 │
│ │
│←── ServerHello + Certificate ────│
│ 选定密码套件、证书、密钥共享 │
│ │
│── Finished ─────────────────────→│
│ 密钥确认 │
│ │
│←══ 加密通信 ════════════════════│
TLS 1.2 vs TLS 1.3
| 特性 |
TLS 1.2 |
TLS 1.3 |
| 握手延迟 |
2 RTT |
1 RTT |
| 0-RTT |
不支持 |
支持 |
| 密码套件 |
多种 |
精简(仅 5 种) |
| 前向保密 |
可选 |
强制 |
| 废弃算法 |
SHA-1、RC4 等 |
移除 |
16.2 Let’s Encrypt 证书
使用 certbot 获取证书
# 安装 certbot
sudo apt install certbot python3-certbot-nginx
# 获取证书
sudo certbot --nginx -d example.com -d www.example.com
# 自动续期
sudo certbot renew --dry-run
# 设置自动续期 cron
0 0 1 * * certbot renew --quiet
证书验证类型
| 类型 |
验证内容 |
签发时间 |
适用场景 |
| DV |
域名所有权 |
分钟级 |
个人网站、API |
| OV |
组织信息 |
1-3 天 |
企业网站 |
| EV |
严格验证 |
1-2 周 |
金融、电商 |
16.3 HSTS(HTTP Strict Transport Security)
原理
# 服务器告诉浏览器:未来只能用 HTTPS 访问
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
攻击场景与防护
# 没有 HSTS:
1. 用户输入 example.com
2. 浏览器访问 http://example.com
3. 攻击者在 HTTP 请求时进行中间人攻击
4. 即使服务器重定向到 HTTPS,攻击已经发生
# 有 HSTS:
1. 首次访问后,浏览器记住 HSTS 策略
2. 之后输入 example.com,浏览器直接访问 HTTPS
3. 根本不发送 HTTP 请求,消除攻击窗口
Nginx 配置
server {
listen 443 ssl;
server_name example.com;
# HSTS 头部
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
}
# HTTP 重定向到 HTTPS
server {
listen 80;
server_name example.com;
return 301 https://$host$request_uri;
}
HSTS 参数
| 参数 |
说明 |
max-age |
策略有效期(秒) |
includeSubDomains |
包含子域名 |
preload |
加入浏览器预加载列表 |
16.4 CSP(Content Security Policy)
原理
CSP 限制页面可以加载的资源来源,防止 XSS 攻击。
配置示例
# 基础 CSP
Content-Security-Policy: default-src 'self'
# 完整 CSP
Content-Security-Policy:
default-src 'self';
script-src 'self' https://cdn.example.com 'nonce-abc123';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
font-src 'self' https://fonts.gstatic.com;
connect-src 'self' https://api.example.com;
frame-src 'none';
object-src 'none';
base-uri 'self';
form-action 'self';
upgrade-insecure-requests;
CSP 指令
| 指令 |
说明 |
示例 |
default-src |
默认资源策略 |
'self' |
script-src |
JavaScript |
'self' 'nonce-xxx' |
style-src |
CSS |
'self' 'unsafe-inline' |
img-src |
图片 |
'self' data: https: |
connect-src |
AJAX/WebSocket |
'self' https://api.* |
frame-src |
iframe |
'none' |
防止 XSS
<!-- CSP 阻止内联脚本 -->
<script>alert('XSS')</script> <!-- 被 CSP 阻止 -->
<!-- 使用 nonce 的安全脚本 -->
<script nonce="abc123">
// 只有带正确 nonce 的脚本才能执行
console.log('安全的脚本');
</script>
CSP 报告
# 仅报告模式(不阻止)
Content-Security-Policy-Report-Only: default-src 'self'; report-uri /csp-report
# 报告模式 + 执行
Content-Security-Policy: default-src 'self'; report-uri /csp-report
16.5 其他安全头部
安全头部一览
| 头部 |
说明 |
推荐值 |
| X-Content-Type-Options |
禁止 MIME 嗅探 |
nosniff |
| X-Frame-Options |
防止点击劫持 |
DENY 或 SAMEORIGIN |
| X-XSS-Protection |
XSS 过滤器 |
1; mode=block |
| Referrer-Policy |
控制 Referer |
strict-origin-when-cross-origin |
| Permissions-Policy |
功能权限 |
camera=(), microphone=() |
Nginx 完整安全配置
server {
listen 443 ssl http2;
server_name example.com;
# TLS 配置
ssl_certificate /etc/ssl/certs/example.pem;
ssl_certificate_key /etc/ssl/private/example.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
ssl_prefer_server_ciphers off;
# 安全头部
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://cdn.example.com" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
}
16.6 中间人攻击防护
常见攻击类型
| 攻击 |
说明 |
防御 |
| SSL 剥离 |
强制降级到 HTTP |
HSTS |
| 证书伪造 |
使用伪造证书 |
证书固定(Certificate Pinning) |
| DNS 劫持 |
DNS 返回错误 IP |
DNSSEC / DoH |
| ARP 欺骗 |
局域网 ARP 表污染 |
HTTPS |
Certificate Transparency
# 证书透明度日志
# 所有公开信任的证书都必须记录到 CT 日志
# 浏览器验证 SCT(Signed Certificate Timestamp)
Expect-CT: max-age=86400, enforce, report-uri="/ct-report"
16.7 Cookie 安全
# 安全的 Cookie 配置
Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Lax; Path=/; Max-Age=3600
| 属性 |
作用 |
| HttpOnly |
防止 JavaScript 读取 |
| Secure |
仅通过 HTTPS 传输 |
| SameSite=Lax |
防止 CSRF |
| Domain |
限制 Cookie 域名 |
| Path |
限制 Cookie 路径 |
| Max-Age |
设置过期时间 |
16.8 业务场景:电商网站安全配置
# 完整的电商网站安全配置
server {
listen 443 ssl http2;
server_name shop.example.com;
# TLS 1.2 + 1.3
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
# HSTS(预加载)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# CSP(严格)
add_header Content-Security-Policy "default-src 'self'; script-src 'self' https://cdn.shop.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; connect-src 'self' https://api.shop.com wss://ws.shop.com" always;
# 防止点击劫持
add_header X-Frame-Options "DENY" always;
# 防止 MIME 嗅探
add_header X-Content-Type-Options "nosniff" always;
# Referrer 策略
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# 功能权限
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(self)" always;
# 支付页面特别配置
location /checkout {
# 更严格的 CSP
add_header Content-Security-Policy "default-src 'none'; script-src 'self'; style-src 'self'; img-src 'self'; form-action 'self'; frame-ancestors 'none'" always;
}
}
⚠️ 注意事项
- 始终使用 HTTPS:Let’s Encrypt 免费证书,没有理由不用
- HSTS 慎用 preload:一旦加入预加载列表,移除非常困难
- CSP 测试先行:先用 Report-Only 模式测试,确认无问题再执行
- TLS 1.0/1.1 已废弃:确保服务器禁用旧版本 TLS
- 定期更新证书:设置自动续期,防止证书过期
- 安全头部顺序:确保在所有响应中添加安全头部
🔗 扩展阅读
下一章:第 17 章:调试与测试工具 — curl、Wireshark、httpie、浏览器 DevTools