前言

最近 .NET 社区炸开了锅,一个 CVSS 评分高达 9.9 的漏洞 —— CVE-2025-55315 正式披露。对于初级开发者来说,9.9 分意味着“最高安全警报”。这个漏洞的核心问题在于 HTTP 请求走私(HTTP Request Smuggling)

简单来说,攻击者可以像潜入特洛伊木马一样,将一个恶意的请求“走私”到另一个合法的请求中,从而绕过你的身份验证、读取别人的隐私数据,甚至在服务器上执行未授权的操作。今天,我们就来扒开它的外衣,看看这背后的原理以及我们该如何应对。

核心概念:什么是“请求走私”?

在现代 Web 架构中,我们的后端 ASP.NET Core 程序通常不会直接暴露给外网,而是躲在 反向代理服务器(如 Nginx, YARP 或 F5)后面。

当反向代理将用户的请求转发给后端的 Kestrel 服务器时,两者必须对“一个请求从哪里开始,到哪里结束”达成共识。

CVE-2025-55315 的本质是 Kestrel 服务器与代理服务器在处理 HTTP/1.1 块传输(Chunked Encoding)时的逻辑不一致

  • 攻击者:发送一个精心构造的、带有“奇怪”换行符或块扩展(Chunk Extensions)的请求。
  • 代理服务器:认为这是一个完整的请求,放行。
  • Kestrel:在解析时产生了歧义,把请求的后半部分当成了“下一个独立请求”。

结果就是:攻击者的第二个请求(恶意请求)在 Kestrel 看来就像是代理服务器发出的“内部合法指令”,从而轻松绕过了最外层的安全防线。

检查与修复

1. 紧急避险:版本“生命线”

这是最直接、也是最有效的修复方式。微软已经紧急发布了针对该漏洞的补丁。请对照下表,确保你的服务器环境高于以下版本:

.NET 版本 受影响版本 安全补丁版本 (修复后)
.NET 9.0 9.0.9 及更低 9.0.10+
.NET 8.0 (LTS) 8.0.20 及更低 8.0.21+
.NET 10.0 (RC) 10.0-rc1 之前 10.0-rc.1.25451.107+
Kestrel Core 包 2.3.0 及更低 2.3.6+

操作指引

  • 开发环境:更新 Visual Studio 2022 到最新版(17.10/17.12/17.14 的安全更新版)。
  • 生产环境:如果使用 Docker,请确保基础镜像标签(如 8.0-bookworm-slim)已拉取最新构建,不要锁死在旧的小版本。

2. 代码级加固:关闭“宽容”模式

在补丁版本中,Kestrel 引入了一个隐藏的“开关”:InsecureChunkedParsing默认情况下它是关闭的(即安全的),但如果你发现老代码在更新后报错,请千万不要盲目开启它。

正确的做法是在 Program.cs 中显式约束 Kestrel 的行为:

using Microsoft.AspNetCore.Server.Kestrel.Core;

var builder = WebApplication.CreateBuilder(args);

builder.WebHost.ConfigureKestrel(options =>
{
    // [重点] 限制请求头的大小和超时,增加走私攻击的报文构造难度
    options.Limits.MaxRequestHeadersTotalSize = 32768; // 32KB 足够绝大多数场景
    options.Limits.RequestHeadersTimeout = TimeSpan.FromSeconds(30);

    // [进阶] 强制要求 Kestrel 仅接受符合 RFC 标准的换行符
    // 虽然补丁已经处理,但在配置中保持“严谨”是架构师的职业习惯
});

架构师提示:如果你看到错误日志中出现 BadHttpRequestException: Bad chunk extension,说明补丁正在生效,它成功拦截了一次非法的、可能包含走私内容的请求。

3. 架构级防御:统一“语言”

请求走私发生的根本原因是代理服务器(Nginx/YARP)和后端(Kestrel)对 HTTP/1.1 报文边界的理解不一致

  • 启用 HTTP 规范化: 如果你使用 Nginx,确保开启了 ignore_invalid_headers on;,并且尽量不要手动修改 Transfer-Encoding 头部。
  • 拥抱现代协议: 这是终极方案。HTTP/2 和 HTTP/3 是基于“帧(Frame)”的二进制协议,不再依赖 \r\nContent-Length 来判断结束。它们在底层逻辑上就对这种“文本走私”免疫。

总结

为什么这次漏洞在源码里的修复只是改了几个 if 判断?

因为在 Http1MessageParser.cs 中,原本的逻辑允许了分块扩展中出现孤立的 \r\n

  • 代理服务器 可能认为 \n 就是结束。
  • Kestrel (旧版) 却在等 \r\n,于是把 \n 之后的内容当成了数据。

这种微小的理解偏差,就是攻击者通过缓冲区溢出或权限绕过的跳板。作为初级开发者,一定要记住:在处理底层网络协议时,对输入数据的“仁慈”,就是对系统安全的“残忍”。

你可能会问:“只是解析个字符串不一致,至于给 9.9 分吗?”

这个漏洞之所以致命,是因为它改变了信任边界。在很多微服务架构中,后端服务默认“信任”来自代理服务器的所有请求(因为鉴权已经在网关做过了)。

避坑指南: 永远不要假设进入后端服务器的请求是绝对安全的。即便有了框架级别的修复,我们也应遵循 “零信任(Zero Trust)” 原则:

  1. 严格校验 Header:不要盲目信任 X-Forwarded-For 等头部,除非你确定它们来自受信任的代理。
  2. 规范化:确保你的代理服务器(如 Nginx)开启了请求规范化功能,能够自动过滤掉畸形的块传输请求。
  3. 及时跟进 LTS:.NET 6 已经停止维护,这次漏洞对老版本是致命的。尽快升级到 .NET 8 或更高版本。

参考资料