深度解析 ASP.NET Core 近年来最严重的漏洞 CVE-2025-55315
前言
最近 .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\n或Content-Length来判断结束。它们在底层逻辑上就对这种“文本走私”免疫。
总结
为什么这次漏洞在源码里的修复只是改了几个 if 判断?
因为在 Http1MessageParser.cs 中,原本的逻辑允许了分块扩展中出现孤立的 \r 或 \n。
- 代理服务器 可能认为
\n就是结束。 - Kestrel (旧版) 却在等
\r\n,于是把\n之后的内容当成了数据。
这种微小的理解偏差,就是攻击者通过缓冲区溢出或权限绕过的跳板。作为初级开发者,一定要记住:在处理底层网络协议时,对输入数据的“仁慈”,就是对系统安全的“残忍”。
你可能会问:“只是解析个字符串不一致,至于给 9.9 分吗?”
这个漏洞之所以致命,是因为它改变了信任边界。在很多微服务架构中,后端服务默认“信任”来自代理服务器的所有请求(因为鉴权已经在网关做过了)。
避坑指南: 永远不要假设进入后端服务器的请求是绝对安全的。即便有了框架级别的修复,我们也应遵循 “零信任(Zero Trust)” 原则:
- 严格校验 Header:不要盲目信任
X-Forwarded-For等头部,除非你确定它们来自受信任的代理。- 规范化:确保你的代理服务器(如 Nginx)开启了请求规范化功能,能够自动过滤掉畸形的块传输请求。
- 及时跟进 LTS:.NET 6 已经停止维护,这次漏洞对老版本是致命的。尽快升级到 .NET 8 或更高版本。