nginx1240漏洞
Nginx 1.24.0 这个版本,去年就爆过一个挺要命的问题。不是配置写错那种低级失误,是它处理 HTTP/2 请求时的一个内存越界读取——CVE-2023-44487,圈里人更习惯叫它“HTTP/2 Rapid Reset”攻击。攻击者不用认证,不用复杂构造,只要不停地发 HEADERS 帧再立刻发 RST_STREAM 帧,就能把 Nginx 的 worker 进程内存打穿。实测在 1.24.0 上,几十个并发连接就能让 CPU 飙到 100%,内存涨得比正常流量快十倍不止。 我当时在测试环境复现过一次。用 h2load 模拟 200 个流,每个流发完请求头就立刻重置,Nginx 日志里开始出现“worker process 12345 exited on signal 11”。信号 11 就是段错误,说白了就是读到了不该读的内存地址。那台机器配置不差,16 核 32G,正常跑几千 QPS 稳如老狗,结果被这个漏洞搞得每三分钟崩一次 worker。 问题的根子出在 ngx_http_v2_module 这个模块。它维护了一个 stream 的哈希表,每个 stream 有独立的 state 和 buffer。Rapid Reset 攻击的精髓在于:客户端可以在服务端还没来得及分配完整 stream 上下文的时候,就发 RST_STREAM 把流关掉。Nginx 1.24.0 的代码里,关闭流的逻辑和清理内存的逻辑存在竞态——一个线程正在读 stream->buffer,另一个线程已经把这块内存 free 掉了。读到的就是野指针。 官方在 1.25.3 里修了。补丁不算大,核心是加了一把引用计数锁,并且把 stream 的销毁延迟到所有 pending 操作完成之后。但 1.24.0 作为当时的 stable 版本,很多生产环境还在跑。我见过一个电商后台,Nginx 前面挂了 CDN,以为能扛住,结果攻击者直接打源站 IP,十分钟就把 1.24.0 打挂了。CDN 只缓存静态资源,动态 API 请求全回源,照样中招。 临时缓解手段有几个。最直接的是在 nginx.conf 里加 真正靠谱的还是升级。1.25.3 之后的版本,或者直接上 1.26.x 主线。如果实在动不了,那就把 HTTP/2 关了,退回 HTTP/1.1。虽然性能差点,但至少不会崩。我自己的博客就是 1.24.0,去年十月收到监控告警说 worker 频繁重启,查了半天才发现是被人扫到了这个漏洞。后来升到 1.25.4,同样的攻击流量打过来,CPU 只涨了 5%,内存纹丝不动。 说句实在话,Nginx 的 HTTP/2 实现一直不算特别健壮。1.24.0 这个漏洞暴露出来的问题,不只是代码缺陷,更是默认配置太宽松。官方文档里对 http2_max_concurrent_streams 的解释轻描淡写,很多人根本不知道这个值设大了会出什么事。技术圈有个坏习惯:看到“stable”就以为是“secure”,其实两码事。1.24.0 是 stable,但它不安全。 最后提一嘴,如果你现在还在用 1.24.0,别犹豫,http2_max_concurrent_streams 128; 把并发流数量压下来。默认是 128,但有些调优教程会建议改成 256 甚至 512,那就等于给攻击者递刀子。另一个办法是上 limit_conn 模块,对单个 IP 的并发连接数做限制。不过这些治标不治本——攻击者换 IP 跟换衣服一样快。nginx -v 看一眼版本号,然后去官网下个 1.26.1 的包。升级过程不到五分钟,重启一下的事。但要是被打了,恢复起来可就不止五分钟了。我见过最惨的一个案例,某金融公司的网关 Nginx 崩了之后,上游的 Java 服务因为连接池耗尽跟着雪崩,整个交易系统停了四十分钟。一个内存越界读,搞出几百万的损失。这账怎么算都不划算。
-
上一篇
-
下一篇