谈谈 HTTPS 的工作原理,SSL / TLS 握手流程是什么?

发布于:2025-03-04 ⋅ 阅读:(16) ⋅ 点赞:(0)
一、HTTPS 核心机制:非对称加密 + 对称加密

HTTPS = HTTP over TLS/SSL,通过 ​混合加密体系​ 解决三大问题:

  1. 防窃听​ - 对称加密传输内容(如 AES)
  2. 防篡改​ - 数字签名验证数据完整性
  3. 防冒充​ - 数字证书验证服务器身份
// 前端感知的典型场景:混合内容拦截
// 在 HTTPS 页面加载 HTTP 资源会被浏览器拦截
<img src="http://example.com/image.jpg"> 
// 控制台报错:Mixed Content: The page was loaded over HTTPS...

二、TLS 握手流程详解(三次握手后)
1. Client Hello

客户端发送:

  • 支持的 TLS 版本(如 TLS 1.3)
  • 客户端随机数(Client Random)
  • 加密套件列表(如 ECDHE-RSA-AES128-GCM-SHA256)
# 开发者工具查看加密套件(Chrome)
chrome://flags/#tls13-variant
2. Server Hello

服务端回应:

  • 选定的 TLS 版本和加密套件
  • 服务器随机数(Server Random)
  • 数字证书(包含公钥)
// 前端可通过 JS 获取证书信息(需要用户授权)
navigator.mediaDevices.getUserMedia({ video: true })
  .then(() => {
    const cert = document.querySelector('video').getCertificate();
    console.log(cert.issuer); // 颁发机构
  });
3. 证书验证

客户端验证证书:

  • 证书链是否可信(CA 机构签发)
  • 域名是否匹配
  • 是否过期
// 开发环境常见错误:自签名证书报错
// 解决方案1:浏览器手动信任(危险)
// 解决方案2:配置本地CA(推荐使用 mkcert)
// 生成本地证书
$ mkcert -install
$ mkcert localhost 127.0.0.1 ::1
4. 密钥交换(以 ECDHE 为例)
  • 服务端发送 ​Server Params​(椭圆曲线公钥)
  • 客户端生成 ​Client Params​ 并发送
  • 双方通过 ECDHE 算法生成 ​Pre-Master Secret
# 简化版密钥计算逻辑(实际为二进制操作)
client_random = 0x1234
server_random = 0x5678
pre_master = ecdhe(client_params, server_params)
master_secret = PRF(pre_master, client_random + server_random)
5. 切换加密协议

双方用 Master Secret 生成对称密钥,后续通信使用对称加密。


三、前端开发重点场景
场景1:强制全站 HTTPS

nginx

# Nginx 配置自动跳转(301 永久重定向)
server {
  listen 80;
  server_name example.com;
  return 301 https://$host$request_uri;
}

html

<!-- 前端兜底方案(慎用) -->
<script>
if (location.protocol !== 'https:') {
  location.replace(`https://${location.host}${location.pathname}`);
}
</script>
场景2:安全 Cookie 传输

javascript

// 设置 Secure + HttpOnly + SameSite
document.cookie = `session=xxx; Secure; HttpOnly; SameSite=Lax`;
场景3:HSTS 预加载

nginx

# 添加 Strict-Transport-Security 头
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

提交预加载列表


四、性能优化实践
技巧1:Session Resumption

复用 TLS 会话减少握手耗时:


nginx

# Nginx 配置会话票证
ssl_session_tickets on;
ssl_session_timeout 1d;
技巧2:OCSP Stapling

由服务端缓存证书状态,减少客户端验证耗时:


nginx

ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8;
技巧3:TLS 1.3 升级

比 TLS 1.2 减少一次 RTT:


nginx

ssl_protocols TLSv1.3 TLSv1.2;

五、常见坑点排查指南
问题1:证书链不完整

现象:Android 低版本报错,iOS 正常
解决:使用 openssl 补全证书链


bash

$ openssl s_client -showcerts -connect example.com:443
$ cat fullchain.pem > chained.crt  # 合并根证书和中间证书
问题2:混合内容阻塞

定位:使用 CSP 报告收集非 HTTPS 请求


html

<meta http-equiv="Content-Security-Policy" 
      content="default-src https: 'unsafe-inline' 'unsafe-eval'; 
               report-uri /csp-report">
问题3:CDN 证书配置错误

检测工具


bash

$ curl -I https://example.com        # 检查 Server 头
$ nscurl --ats-diagnostics https://example.com  # iOS 特性检测

六、开发环境 HTTPS 最佳实践
方案1:Webpack DevServer 配置

javascript

// webpack.config.js
const fs = require('fs');
const https = require('https');
module.exports = {
  devServer: {
    https: {
      key: fs.readFileSync('localhost-key.pem'),
      cert: fs.readFileSync('localhost.pem')
    },
    public: 'https://localhost:8080' // 避免浏览器警告
  }
};
方案2:Service Worker 调试

javascript

// sw.js 中捕获证书错误
self.addEventListener('fetch', event => {
  if (event.request.url.startsWith('https://')) {
    event.respondWith(
      fetch(event.request).catch(err => {
        console.error('证书错误:', err);
        return new Response('HTTPS故障');
      })
    );
  }
});

七、终极检测清单
  1. 所有子域名启用 HTTPS(包括 CDN)
  2. 配置 HSTS 头部并提交预加载
  3. 定期更新 TLS 证书(监控到期时间)
  4. 禁用不安全协议(SSLv3、TLS 1.0)
  5. 使用 Qualys SSL Labs 评分达到 A+

bash

# 一键检测(需安装 testssl.sh)
$ testssl.sh --color 0 example.com

通过理解 TLS 握手流程,前端开发者能更好地处理证书错误、优化资源加载策略,并推动全站安全升级。记住:HTTPS 不是终点,而是现代 Web 应用的起跑线。