传输层安全:TLS/SSL协议详解
传输层安全,说白了就是给数据通信加一把锁。我做了这么多年支付系统,最深的体会就是:没有TLS的支付系统,就像没穿衣服走在街上——随时可能被劫持。
先说说TLS和SSL的关系。SSL是前辈,TLS是接班人。现在大家说的TLS,其实已经是1.2和1.3版本的天下了。我个人习惯直接跳过SSL,因为SSL 3.0已经被证明不安全了。你想想看,2014年的POODLE攻击,就是专门针对SSL 3.0的。我在项目中遇到过一家银行,还在用SSL 3.0,我当场就让他们升级了。
TLS握手过程
TLS握手,就是客户端和服务器互相确认身份、协商加密参数的过程。我画了一张图,帮你理清这个流程:
嗯,这里要注意:TLS 1.3 简化了握手过程,从2-RTT降到了1-RTT。我建议新项目直接上TLS 1.3,性能提升很明显。
密码套件选择
密码套件就是一组加密算法的组合。我见过太多人随便选,结果踩坑。下面这张表是我常用的推荐:
| 套件名称 | 密钥交换 | 认证 | 加密 | MAC | 推荐等级 |
|---|---|---|---|---|---|
| TLS_AES_128_GCM_SHA256 | ECDHE | RSA/ECDSA | AES-128-GCM | AEAD | ⭐⭐⭐⭐⭐ |
| TLS_AES_256_GCM_SHA384 | ECDHE | RSA/ECDSA | AES-256-GCM | AEAD | ⭐⭐⭐⭐⭐ |
| TLS_CHACHA20_POLY1305_SHA256 | ECDHE | RSA/ECDSA | CHACHA20-POLY1305 | AEAD | ⭐⭐⭐⭐ |
| TLS_RSA_WITH_AES_128_CBC_SHA | RSA | RSA | AES-128-CBC | HMAC-SHA1 | ❌ 不推荐 |
核心原则:优先选择ECDHE密钥交换 + AEAD加密模式。不要用RSA密钥交换,它不支持前向安全性。
双向证书认证
双向认证,就是客户端也要出示证书。这在支付系统中很常见,尤其是银行间的交易。我做过一个跨境支付项目,对方银行要求必须双向认证,折腾了好几天才调通。
为什么会需要双向认证?说白了,就是服务器也要确认客户端的身份。你想想看,如果只有服务器出示证书,那谁都可以假装是合法客户端。在支付场景里,这风险太大了。
配置示例
以Nginx为例,双向认证的配置是这样的:
server {
listen 443 ssl;
server_name api.payment.com;
# 服务器证书
ssl_certificate /etc/ssl/certs/server.crt;
ssl_certificate_key /etc/ssl/private/server.key;
# 客户端证书验证
ssl_client_certificate /etc/ssl/certs/ca.crt;
ssl_verify_client on;
ssl_verify_depth 2;
# 验证失败时的处理
ssl_client_certificate /etc/ssl/certs/ca.crt;
ssl_verify_client optional;
location / {
# 获取客户端证书信息
proxy_set_header X-SSL-Client-Cert $ssl_client_cert;
proxy_set_header X-SSL-Client-Verify $ssl_client_verify;
proxy_pass http://backend;
}
}
我的经验:双向认证的证书链验证一定要做完整。我曾经遇到一个案例,对方只验证了客户端证书本身,没验证CA链,结果被中间人攻击了。
HTTPS强制策略
HTTPS强制策略,就是确保所有流量都走HTTPS,不留任何HTTP的入口。我见过太多系统,只对登录页面做了HTTPS,其他页面还是HTTP。结果呢?cookie被截获,session被劫持。
强制HTTPS有几种方式:
- 服务器端重定向:所有HTTP请求301/302到HTTPS
- HSTS:告诉浏览器以后只走HTTPS
- CSP:内容安全策略,限制资源加载
- Secure Cookie:cookie只通过HTTPS传输
Nginx强制HTTPS配置
server {
listen 80;
server_name api.payment.com;
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name api.payment.com;
# SSL配置
ssl_certificate /etc/ssl/certs/server.crt;
ssl_certificate_key /etc/ssl/private/server.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
# 安全头
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Content-Type-Options nosniff;
add_header X-Frame-Options DENY;
location / {
proxy_pass http://backend;
}
}
注意:301重定向会暴露URL参数。如果URL中包含敏感信息,建议用307重定向。
HSTS部署
HSTS(HTTP Strict Transport Security)是强制HTTPS的终极方案。它告诉浏览器:以后访问这个网站,只走HTTPS,不要尝试HTTP。
我记得有一次,一个客户说他们的HSTS部署后,部分用户访问不了。排查了半天,发现是HSTS的max-age设得太短,浏览器还没来得及缓存。嗯,这里要注意:max-age至少要设半年以上。
HSTS头格式
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
各参数含义:
- max-age:缓存时间,单位秒。建议31536000(1年)
- includeSubDomains:是否包含子域名
- preload:申请加入浏览器预加载列表
HSTS预加载
HSTS预加载是更狠的一招。你把域名提交到浏览器的预加载列表,浏览器在第一次访问前就知道这个域名必须走HTTPS。我建议所有支付系统都申请预加载。
申请条件:
- 有效的HTTPS证书
- 所有子域名都支持HTTPS
- HSTS头包含preload指令
- max-age至少10886400秒(18周)
部署检查清单:
- ✅ 所有页面都支持HTTPS
- ✅ 没有混合内容(Mixed Content)
- ✅ HSTS头正确配置
- ✅ 证书链完整
- ✅ 密码套件安全
- ✅ 定期SSL Labs测试
常见问题与避坑
我总结几个常见问题:
- 证书过期:我建议用自动续签工具,比如certbot。手动续签太容易忘了。
- 中间证书缺失:有些服务器只配了叶子证书,没配中间证书。这会导致某些客户端验证失败。
- HSTS部署后回退:一旦部署HSTS,想回退就难了。浏览器会记住HSTS策略,直到max-age过期。
- 性能问题:TLS握手确实有开销。我建议用会话复用和OCSP Stapling来优化。
我的建议:先用低max-age测试,比如5分钟。确认没问题后,再逐步增加到1年。这样即使出问题,影响范围也小。
好了,传输层安全就讲到这里。记住一句话:没有TLS的支付系统,就是在裸奔。该配的配齐,该测的测好,别给自己留隐患。