HTTPS握手攻击是什么?加密流量DDoS防护实战

加密流量不等于安全流量,DDoS 攻击者同样会盯着 TLS 握手打。本文用 9 个问答讲清 HTTPS 握手攻击的原理、怎么区分「握手耗尽」与「连接耗尽」、Nginx 与证书侧的加固配置,以及用 CDN 终结 TLS 的实战方案。

Q:什么是 HTTPS 握手攻击?它和普通 DDoS 有什么不同?

  • TLS 握手是客户端与服务器建立加密连接的协商过程,其中包含非对称加密运算(RSA 私钥运算、DH 密钥交换),这是服务器 CPU 上最昂贵的操作之一。
  • 握手攻击就是让服务器把 CPU 全花在握手计算上:大量建立连接但不完成握手,或在单条连接里反复发起完整握手。
  • 与流量型 DDoS 最大的不同在于——攻击流量可能很小,带宽曲线几乎看不出异常,但 CPU 被打满、正常用户连握手都完不成。

Q:握手攻击到底在消耗服务器的什么资源?

  • CPU:非对称加密运算,单次握手往往比一次普通请求贵几十倍。
  • 连接与内存:半截连接长期挂起会占用连接槽与缓冲区。
  • 证书链与状态校验:未开启 OCSP Stapling 时,服务器可能要去外部查询证书状态,额外增加延迟与失败点。
  • 这就是为什么「加了带宽、换了更大的服务器还是被打挂」——瓶颈从来不是带宽,而是每秒能完成多少次握手。
  • 需要与另一类攻击区分:连接耗尽型攻击打的是连接表(TCP 半连接与 conntrack),连接数会异常膨胀;握手攻击耗的是 CPU,连接数可能并不高。

Q:怎么判断网站卡在握手层,而不是后端?

# 1) 单次握手耗时(多次取样看波动)
for i in 1 2 3 4 5; do
  curl -o /dev/null -s -w "handshake: %{time_appconnect}s total: %{time_total}s\n" https://www.example.com/
done

# 2) Nginx 访问日志里的 SSL 类错误码,495/496/400 大量出现是典型信号
grep -E ' (400|495|496|497) ' /var/log/nginx/access.log | wc -l
tail -f /var/log/nginx/error.log | grep -i ssl

# 3) 连接状态与 443 并发总览
ss -s
ss -ant state established '( sport = :443 )' | wc -l
  • 判据:握手耗时明显拉长、SSL 错误码激增、nginx worker CPU 高但后端应用进程 CPU 不高,基本可以确认压力卡在 TLS 层。
  • 反过来,如果握手正常而后端 CPU 高,那是应用层 CC,不属于握手攻击。

Q:Nginx 有哪些配置能降低握手成本、限制握手滥用?

# 开启会话复用,让老客户端不必每次都做完整握手
ssl_session_cache   shared:SSL:50m;   # 1MB 约存 4000 个会话
ssl_session_timeout 1d;
ssl_session_tickets on;

# 握手超时收敛:不让半截连接长期挂着
ssl_handshake_timeout 10s;

# 优先启用 TLS 1.3(1-RTT、不支持重协商、默认前向保密)
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
  • 同时在 443 的 server 块里对并发与频率做限制,把单个来源卡住:
limit_conn_zone $binary_remote_addr zone=tlsconn:10m;
limit_req_zone  $binary_remote_addr zone=tlsreq:10m rate=20r/s;

server {
    listen 443 ssl;
    limit_conn tlsconn 30;      # 单 IP 最多 30 个并发连接
    limit_req  zone=tlsreq burst=40 nodelay;
    limit_conn_status 429;
}
  • 关键参数是 ssl_session_cache 与 ssl_handshake_timeout:前者把重复握手变成复用,后者把半截连接尽早清掉,两者配合能明显压低 CPU 峰值。
  • 系统层可适当调大握手队列(net.core.somaxconn、net.ipv4.tcp_max_syn_backlog),但要清楚内核参数只能缓解排队,解决不了 CPU 被加密运算吃满的问题。

Q:证书类型也会影响握手开销吗?

  • 会,而且差异不小。RSA 私钥运算比 ECDSA 重得多,同等硬件下 ECDSA 证书能承载的握手次数通常高出一个数量级。
  • 证书链越长,客户端需要校验的中间证书越多,握手数据量与耗时都会增加,尽量把链裁到最短。
  • 开启 OCSP Stapling,把证书状态响应缓存到服务器本地,省掉外部查询:
ssl_stapling        on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/nginx/ssl/chain.pem;
resolver 223.5.5.5 8.8.8.8 valid=300s;
resolver_timeout 5s;

# 检查证书类型与链长度
openssl s_client -connect www.example.com:443 -servername www.example.com /dev/null \
  | openssl x509 -noout -subject -issuer

Q:重协商(Renegotiation)是什么,怎么关掉?

  • 重协商原本用于在已有连接里更换加密参数,但攻击者可以在单条连接里反复发起重协商,用极小的流量换来巨量 CPU 消耗——这就是经典的 SSL 重协商拒绝服务攻击。
  • 处理方式很直接:停用不安全重协商,并优先使用 TLS 1.3(该版本本身不支持重协商)。
ssl_protocols TLSv1.2 TLSv1.3;
# OpenSSL 1.1.1+ 已默认禁用不安全重协商,无需额外开关
# 老发行版请确认安全重协商已生效:
openssl s_client -connect www.example.com:443 -servername www.example.com -reconnect /dev/null \
  | grep -i reneg
  • 在 Nginx 中不要把 ssl_protocols 留着 TLSv1.0/TLSv1.1:它们不仅安全性差,握手效率也更低。

Q:TLS 1.3 和会话复用能解决多少问题?

  • 能解决相当一部分,但不能全指望它。真实情况是:新客户端(无会话票据)每次都要做完整握手,攻击者正是靠不断伪装成「新客户端」来规避会话复用。
  • TLS 1.3 把完整握手从 2-RTT 降到 1-RTT,且默认使用 ECDHE,CPU 成本低于老版本;但它不会让握手变成零成本。
  • 关于 0-RTT(ssl_early_data):默认关闭更稳妥,它存在重放风险,下单、支付、提交这类非幂等请求绝对不要开。
  • 结论:会话复用与 TLS 1.3 属于「降本」,不是「免疫」;真正扛住大流量握手攻击,最终还是要靠 CDN 分流。

Q:用 CDN 或高防终结 TLS 是不是最省事的做法?

  • 是。让 CDN / 高防节点负责与客户端完成握手,把加密流量解密并清洗后再回源,源站只处理干净的请求,握手压力基本转移出去。
  • 配合三件事效果最好:源站只放行 CDN 回源网段、回源启用长连接与会话复用、源站同样开启 TLS 1.3。
# 源站只允许 CDN 回源 IP 访问(网段为示例,请替换为实际回源段)
location / {
    allow 203.0.113.0/24;
    allow 198.51.100.0/24;
    deny  all;
    proxy_pass http://backend;
}

# 回源启用 keepalive,减少重复握手
upstream backend {
    server 127.0.0.1:8080;
    keepalive 64;
}
proxy_http_version 1.1;
proxy_set_header Connection "";
  • 常见坑:只在前端开了 CDN,源站域名仍可被直接解析访问,攻击者绕过 CDN 直打源站,前面所有防护等于白做。源站要么只放行回源段,要么干脆只监听内网地址。

Q:还有什么补充建议?

  • 别把加密当成防护:TLS 保护的是传输内容,不区分流量善恶,握手本身就可以被当成攻击面。
  • 监控盯四个量:握手耗时、SSL 错误码数量、443 并发连接数、nginx worker CPU 占比。
  • 优先做低成本改造:会话复用、TLS 1.3、ECDSA 证书、OCSP Stapling,四项都属于改配置就能生效的降本手段。
  • 大流量攻击交给 CDN:单机再优化也扛不住分布式握手洪水,分层卸载才是正解。
  • 一句话总结:握手攻击打的是 CPU 而不是带宽——会话复用把重复握手省掉、TLS 1.3 与 ECDSA 把单次成本压低、CDN 终结 TLS 把压力转移出去,三件事做完,源站才有资格谈「扛住」。