200字
HSTS 详解 🔒
2025-12-02
2025-12-02

🎯 什么是 HSTS?

HSTS = HTTP Strict Transport Security(HTTP 严格传输安全)

核心作用: 强制浏览器只能通过 HTTPS 访问网站,彻底杜绝 HTTP 连接。

🤔 为什么需要 HSTS?

问题场景

即使网站支持 HTTPS,用户仍可能遇到这些问题:

场景 1:用户输入习惯 👤

用户输入:example.com
浏览器首次访问:http://example.com
然后重定向到:https://example.com

问题: 第一次请求是 HTTP(明文),可能被劫持!

场景 2:链接问题 🔗

网页中的链接:<a href="http://example.com/login">登录</a>
点击后:走 HTTP 协议

问题: 敏感数据可能泄露!

场景 3:中间人攻击 😈

用户 → http://bank.com → 黑客 → 钓鱼网站
      (明文请求被拦截)

问题: SSL Strip 攻击,降级到 HTTP

HSTS 如何解决?

第一次访问(HTTPS):
服务器响应头:Strict-Transport-Security: max-age=31536000

浏览器记住:
"未来一年内,example.com 只能用 HTTPS!"

后续访问:
用户输入 http://example.com
浏览器自动改为 https://example.com(不发送HTTP请求!)

📝 HSTS 响应头详解

基本语法

Strict-Transport-Security: max-age=<秒数>; includeSubDomains; preload

参数说明

1️⃣ max-age(必需)

Strict-Transport-Security: max-age=31536000
  • 单位:秒

  • 31536000 = 1 年

  • 63072000 = 2 年(推荐)

  • 在此期间,浏览器强制使用 HTTPS

生命周期:

第一次访问 → 记录HSTS策略 → 倒计时开始
每次访问 → 刷新倒计时 → 重新开始
超过max-age → HSTS失效 → 可以访问HTTP

2️⃣ includeSubDomains(可选)

Strict-Transport-Security: max-age=31536000; includeSubDomains

作用: HSTS 策略也应用到所有子域名

示例:

设置在:example.com
同时保护:
  - example.com
  - www.example.com
  - api.example.com
  - *.example.com

⚠️ 注意: 确保所有子域名都支持 HTTPS,否则会无法访问!

3️⃣ preload(可选)

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload

作用: 允许网站加入浏览器的 HSTS 预加载列表

这是最强防护!下面详细讲解。

🎪 HSTS Preload List(预加载列表)

什么是预加载?

传统 HSTS 的问题:

首次访问仍需 HTTP → 存在风险窗口

Preload 的解决方案:

网站提交到预加载列表
     ↓
浏览器内置该列表
     ↓
用户从未访问过该网站
     ↓
浏览器已知必须用 HTTPS!

预加载列表特点

优点:

  • 首次访问就受保护

  • 无风险窗口

  • Chrome、Firefox、Safari、Edge 等主流浏览器共享

⚠️ 代价:

  • 一旦提交,很难移除(至少数月)

  • 必须满足严格条件

  • 所有子域名必须支持 HTTPS

提交要求

要加入预加载列表,网站必须:

  1. ✅ 使用有效的 HTTPS 证书

  2. ✅ 所有 HTTP 流量重定向到 HTTPS

  3. ✅ 所有子域名支持 HTTPS

  4. ✅ 响应头设置:

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
  1. ✅ max-age 至少 1 年(推荐 2 年)

提交地址: https://hstspreload.org/

🔧 配置示例

Nginx 配置

server {
    listen 443 ssl http2;
    server_name example.com;

    # SSL 证书配置
    ssl_certificate /path/to/cert.pem;
    ssl_certificate_key /path/to/key.pem;

    # HSTS 配置
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
    
    # 其他配置...
}

# HTTP 重定向到 HTTPS
server {
    listen 80;
    server_name example.com;
    return 301 https://$server_name$request_uri;
}

Apache 配置

<VirtualHost *:443>
    ServerName example.com
    
    # SSL 配置
    SSLEngine on
    SSLCertificateFile /path/to/cert.pem
    SSLCertificateKeyFile /path/to/key.pem
    
    # HSTS 配置
    Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
</VirtualHost>

# HTTP 重定向
<VirtualHost *:80>
    ServerName example.com
    Redirect permanent / https://example.com/
</VirtualHost>

Node.js (Express) 配置

const express = require('express');
const helmet = require('helmet');

const app = express();

// 使用 helmet 中间件设置 HSTS
app.use(helmet.hsts({
    maxAge: 63072000,        // 2年
    includeSubDomains: true,
    preload: true
}));

// 或手动设置
app.use((req, res, next) => {
    res.setHeader(
        'Strict-Transport-Security',
        'max-age=63072000; includeSubDomains; preload'
    );
    next();
});

🔍 浏览器如何处理 HSTS

Chrome 查看 HSTS 状态

访问:chrome://net-internals/#hsts

可以进行的操作:

  1. 🔍 Query(查询):查看某个域名的 HSTS 状态

  2. Add(添加):手动添加 HSTS 域名(测试用)

  3. Delete(删除):删除 HSTS 记录(清除缓存)

Firefox 查看

  1. 打开开发者工具(F12)

  2. 切换到 "网络" 标签

  3. 查看响应头中的 Strict-Transport-Security

🎭 实际运作流程

首次访问(未启用 Preload)

步骤1: 用户输入 example.com
     ↓
步骤2: 浏览器访问 http://example.com
     ↓
步骤3: 服务器返回 301 重定向到 https://example.com
     ↓
步骤4: 浏览器访问 https://example.com
     ↓
步骤5: 服务器返回响应 + HSTS 头
     ↓
步骤6: 浏览器记录 HSTS 策略

⚠️ 风险窗口: 步骤 2 的 HTTP 请求可能被劫持

后续访问(HSTS 生效)

步骤1: 用户输入 http://example.com
     ↓
步骤2: 浏览器检查 HSTS 缓存 ✓
     ↓
步骤3: 浏览器内部升级为 https://example.com
     ↓
步骤4: 直接发送 HTTPS 请求(跳过HTTP!)

无风险: 根本不发送 HTTP 请求

启用 Preload 后

步骤1: 用户首次访问 example.com
     ↓
步骤2: 浏览器检查内置 Preload 列表 ✓
     ↓
步骤3: 直接使用 HTTPS

🎯 完美: 从来没有 HTTP 请求!

⚠️ 常见问题与注意事项

1. 部署前的准备工作

检查清单:
☑ 所有资源都支持 HTTPS(图片、CSS、JS)
☑ 所有子域名都配置了证书
☑ 确认没有硬编码的 HTTP 链接
☑ 测试环境验证无误

2. 渐进式部署策略

阶段1: 测试期(1周)
  max-age=604800  // 7天

阶段2: 短期(1个月)
  max-age=2592000  // 30天

阶段3: 长期(1年)
  max-age=31536000  // 1年

阶段4: 加入 Preload(2年)
  max-age=63072000; includeSubDomains; preload

3. 错误配置的后果

问题场景:

# 设置了 includeSubDomains
Strict-Transport-Security: max-age=31536000; includeSubDomains

# 但 sub.example.com 没有配置 HTTPS

结果: sub.example.com 完全无法访问!

解决方法:

  • 浏览器端:chrome://net-internals/#hsts 删除记录

  • 服务器端:设置 max-age=0 取消 HSTS

Strict-Transport-Security: max-age=0

4. 从 Preload 列表移除

如果需要移除(极少情况):

  1. hstspreload.org 提交移除请求

  2. 等待数月(浏览器更新周期)

  3. 在此期间必须维护 HTTPS

时间成本: 3-12 个月

🛡️ HSTS 与其他安全措施

安全组合拳

HSTS          → 强制 HTTPS
  +
CSP           → 内容安全策略
  +
HPKP (已废弃)  → 公钥固定
  +
Cookie Secure → Cookie 仅 HTTPS 传输
  +
SameSite      → 防止 CSRF

完整的安全响应头示例

# HSTS
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload

# 防止点击劫持
X-Frame-Options: DENY

# 防止 MIME 类型嗅探
X-Content-Type-Options: nosniff

# XSS 保护
X-XSS-Protection: 1; mode=block

# CSP
Content-Security-Policy: default-src https:; script-src 'self'

# Cookie 安全
Set-Cookie: session=xxx; Secure; HttpOnly; SameSite=Strict

📊 HSTS 统计

当前使用情况:

  • 🌐 超过 20% 的 Alexa Top 100 万网站启用 HSTS

  • 📈 Preload 列表包含 10 万 + 域名

  • 🏆 Google、Facebook、Twitter 等大厂全部启用

🎓 总结

HSTS 的三个等级

级别1: 基础 HSTS
  max-age=31536000

级别2: 包含子域名
  max-age=31536000; includeSubDomains

级别3: 预加载(最安全)
  max-age=63072000; includeSubDomains; preload

一句话记忆

HSTS 让浏览器记住 "这个网站只能用 HTTPS",从此用户再也不会走不安全的 HTTP,即使他自己手动输入 http://

🚀 最佳实践建议

  1. ✅ 从小的 max-age 开始测试

  2. ✅ 确认所有子域名都支持 HTTPS 再加 includeSubDomains

  3. ✅ 充分测试后再考虑 preload

  4. ✅ 监控证书过期时间

  5. ✅ 配合 301/302 重定向使用

评论