HTTP 与 HTTPS:从原理到实践的完整科普

2024-07-03 编程开发

HTTP(超文本传输协议)是互联网的基石,而 HTTPS 是其安全增强版。作为前端工程师,深入理解两者的区别和工作原理,对排查问题、性能优化和安全防护都至关重要。

一、HTTP 基础

1.1 请求 - 响应模型

HTTP 遵循简单的请求 - 响应模式:客户端发送请求,服务器返回响应。

客户端(浏览器)                    服务器
     |                              |
     |  --- GET /api/users HTTP/1.1 --->  |
     |                              |
     |  <--- HTTP/1.1 200 OK -------  |
     |       Content-Type: application/json |
     |       [{"id":1, "name":"Alice"}]     |

1.2 HTTP 请求方法

方法 语义 幂等性 安全性 常见场景
GET 获取资源 ✅ 幂等 ✅ 安全 查询数据、页面访问
POST 创建资源 ❌ 非幂等 ❌ 不安全 表单提交、登录
PUT 全量更新 ✅ 幂等 ❌ 不安全 替换整个资源
PATCH 部分更新 ❌ 非幂等 ❌ 不安全 修改部分字段
DELETE 删除资源 ✅ 幂等 ❌ 不安全 删除数据
HEAD 获取响应头 ✅ 幂等 ✅ 安全 检查资源是否存在
OPTIONS 查询支持的方法 ✅ 幂等 ✅ 安全 CORS 预检请求
// 前端使用 Fetch API 示例
// GET
const users = await fetch('/api/users').then(r => r.json());

// POST
const newUser = await fetch('/api/users', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ name: 'Bob', email: 'bob@example.com' }),
}).then(r => r.json());

// PUT
await fetch('/api/users/1', {
  method: 'PUT',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ name: 'Bob Updated', email: 'bob@example.com' }),
});

// DELETE
await fetch('/api/users/1', { method: 'DELETE' });

1.3 HTTP 状态码

状态码范围 类别 常见状态码
1xx 信息响应 100 Continue、101 Switching Protocols
2xx 成功 200 OK、201 Created、204 No Content
3xx 重定向 301 Moved Permanently、302 Found、304 Not Modified
4xx 客户端错误 400 Bad Request、401 Unauthorized、403 Forbidden、404 Not Found、429 Too Many Requests
5xx 服务器错误 500 Internal Server Error、502 Bad Gateway、503 Service Unavailable、504 Gateway Timeout
// 前端状态码处理最佳实践
async function apiRequest(url, options = {}) {
  const response = await fetch(url, options);

  if (!response.ok) {
    switch (response.status) {
      case 401:
        // 跳转登录
        window.location.href = '/login';
        throw new Error('未授权,请重新登录');
      case 403:
        throw new Error('没有访问权限');
      case 404:
        throw new Error('请求的资源不存在');
      case 429:
        // 等待后重试
        const retryAfter = response.headers.get('Retry-After') || 5;
        await new Promise(r => setTimeout(r, retryAfter * 1000));
        return apiRequest(url, options);
      case 500:
        throw new Error('服务器内部错误,请稍后重试');
      default:
        throw new Error(`请求失败: ${response.status}`);
    }
  }

  return response.json();
}

1.4 HTTP 的致命缺陷:明文传输

HTTP 最大的问题是所有数据都以明文传输。这意味着:

  • 窃听风险:同一 Wi-Fi 下的任何人都能抓包看到你的密码、银行卡号
  • 篡改风险:中间人可以修改传输内容(中间人攻击),插入恶意广告或脚本
  • 冒充风险:无法验证对方是否是真的服务器
HTTP 明文传输示例(抓包可见):

GET /login HTTP/1.1
Host: bank.example.com

HTTP/1.1 200 OK
...
<form action="/login" method="POST">
  <input name="username" value="alice">
  <input name="password" value="mysecret123">  ← 密码明文可见!
</form>

二、HTTPS = HTTP + TLS

HTTPS 在 HTTP 和 TCP 之间插入 TLS(Transport Layer Security) 层,提供:

  • 加密(Encryption):数据加密传输,无法窃听
  • 完整性(Integrity):数据无法被篡改
  • 身份验证(Authentication):验证服务器身份
HTTP:   [HTTP]  →  [TCP]  →  [IP]
HTTPS:  [HTTP]  →  [TLS]  →  [TCP]  →  [IP]
                      ↑
                 加密/认证层

2.1 TLS 握手过程(简化版)

客户端 (浏览器)                         服务器 (website.com)
    |                                        |
    |  ① ClientHello                        |
    |  (支持的 TLS 版本、加密套件列表)          |
    |  ---------------------------------->   |
    |                                        |
    |  ② ServerHello                        |
    |  (选定的 TLS 版本、加密套件)              |
    |  ③ Certificate                        |
    |  (服务器 SSL 证书,含公钥)               |
    |  ④ ServerHelloDone                    |
    |  <----------------------------------   |
    |                                        |
    |  ⑤ 验证证书链                          |
    |  ⑥ 生成 Premaster Secret              |
    |  ⑦ ClientKeyExchange                  |
    |  (用服务器公钥加密的 Premaster Secret)   |
    |  ---------------------------------->   |
    |                                        |
    |  ⑧ 双方用 Premaster Secret            |
    |     生成对称加密的 Session Key           |
    |                                        |
    |  ←======= 后续通信 ========→           |
    |    (使用 Session Key 对称加密)           |

关键点:

  • 非对称加密(RSA/ECDHE):仅在握手阶段使用,用于安全交换对称密钥。公钥加密、私钥解密,计算开销大。
  • 对称加密(AES/ChaCha20):握手完成后用于加密数据传输。速度快,适合大量数据。
  • 为什么不全程用非对称加密? —— 太慢了,性能不可接受。对称加密速度快数百倍。

2.2 证书链验证

根 CA 证书(Root CA)
  └── 中间 CA 证书(Intermediate CA)
        └── 网站证书(example.com)

浏览器验证流程:
  1. 检查网站证书是否由受信任的中间 CA 签发
  2. 检查中间 CA 证书是否由受信任的根 CA 签发
  3. 检查证书是否在有效期内
  4. 检查证书的域名是否匹配
  5. 检查证书是否被吊销(OCSP / CRL)

2.3 中间人攻击(MITM)与 HTTPS 的防护

HTTP 下的 MITM 攻击(可被攻击):

用户 ←→ 攻击者(咖啡店 Wi-Fi 热点)←→ 真实服务器
       ↑ 攻击者可以看到、修改所有数据

HTTPS 下的防护:

用户 ←→ 攻击者 ←→ 真实服务器
       ↑ 攻击者无法解密(无服务器私钥)
       ↑ 攻击者无法伪造证书(无受信任 CA 签名)

三、为什么要全站 HTTPS?

3.1 浏览器强制策略

  • Chrome/Firefox/Safari 对 HTTP 页面标记"不安全"
  • HTTP/2 浏览器实现只支持 HTTPS
  • 许多新 Web API 仅在安全上下文(HTTPS 或 localhost)可用:
    • navigator.geolocation(地理位置)
    • navigator.mediaDevices.getUserMedia()(摄像头/麦克风)
    • Service Workers
    • Push API
    • Payment Request API

3.2 SEO 优势

Google 明确将 HTTPS 作为排名信号。HTTPS 网站的搜索排名优于同等情况下的 HTTP 网站。

3.3 Referrer 信息保留

从 HTTPS 页面跳转到 HTTP 页面时,浏览器不会发送 Referrer 头(防止信息泄露),可能影响统计分析。

四、HTTPS 性能与优化

优化项 说明 效果
TLS 1.3 握手减少为 1-RTT(往返一次) 比 TLS 1.2 快 30-50%
HTTP/2 / HTTP/3 多路复用、头部压缩 显著减少延迟
OCSP Stapling 服务器提前获取 OCSP 响应 减少客户端验证延迟
Session Resumption 复用之前的 TLS 会话 0-RTT 恢复连接
HSTS 强制浏览器只用 HTTPS 消除 301 重定向开销
# Nginx HTTPS 优化配置示例
server {
    listen 443 ssl http2;
    server_name example.com;

    # 证书路径
    ssl_certificate     /etc/nginx/ssl/fullchain.pem;
    ssl_certificate_key /etc/nginx/ssl/privkey.pem;

    # 只用 TLS 1.2 和 1.3
    ssl_protocols TLSv1.2 TLSv1.3;

    # 优先服务器端加密套件
    ssl_prefer_server_ciphers on;

    # OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;

    # Session 缓存
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    # HSTS(一年,包含子域名)
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}

五、免费获取 HTTPS 证书

Let's Encrypt + Certbot

# 1. 安装 Certbot(Ubuntu/Debian)
sudo apt update && sudo apt install certbot python3-certbot-nginx

# 2. 自动获取并配置证书(Nginx 示例)
sudo certbot --nginx -d example.com -d www.example.com

# 3. 设置自动续期
sudo certbot renew --dry-run   # 测试
# Certbot 默认添加 crontab 自动续期

# 4. 手动续期(证书有效期 90 天)
sudo certbot renew

其他免费证书选择:

提供商 特点 有效期
Let's Encrypt 最流行,自动化 90 天
ZeroSSL 支持 REST API 90 天
Cloudflare CDN 自带免费证书 自动续期
BuyPass 180 天,仅欧洲 180 天

六、HTTP vs HTTPS 全面对比

维度 HTTP HTTPS
端口 80 443
加密 ❌ 明文 ✅ TLS 加密
身份验证 ❌ 无 ✅ SSL 证书
数据完整性 ❌ 无保证 ✅ 有保证
速度 略快(无加密开销) TLS 1.3 下几乎无差别
SEO ❌ 降权 ✅ 提权
浏览器标签 "不安全" 🔒 安全锁
Web API 可用性 受限 完整
证书成本 免费 免费(Let's Encrypt)
HTTP 版本支持 HTTP/1.1 HTTP/1.1, HTTP/2, HTTP/3
推荐 ❌ 不推荐 ✅ 必须

七、总结

  1. HTTP 是明文传输,存在窃听、篡改、冒充三大风险
  2. HTTPS = HTTP + TLS,通过非对称加密交换密钥、对称加密传输数据
  3. TLS 1.3 已将握手简化为 1-RTT,性能几乎无影响
  4. 全站 HTTPS 是 Web 标准:浏览器强制、SEO 加分、新 API 硬性要求
  5. Let's Encrypt 免费证书让 HTTPS 零成本部署

作为前端开发,无论你是自己部署服务还是对接后端 API,请务必优先使用 HTTPS。在本地开发时,可以使用 mkcert 生成自签名证书来模拟 HTTPS 环境。

# 本地开发 HTTPS 环境
brew install mkcert    # macOS
mkcert -install
mkcert localhost 127.0.0.1
# 生成 localhost+1.pem 和 localhost+1-key.pem