在.NET后端服务里,用户上传XML文件是相当普遍的需求,但如果直接信任外部传入的XML并使用默认方式解析,就可能遭遇Billion Laughs攻击。这种攻击通过定义多层嵌套的实体引用,使一个几KB的文件在解析后膨胀为几百MB甚至上GB的内容,最终拖垮服务器。

什么是Billion Laughs攻击
Billion Laughs攻击又称XML实体膨胀攻击,它借助文档类型定义(DTD)中的内部实体,进行反复嵌套引用。例如下面的XML片段:
<?xml version="1.0"?> <!DOCTYPE lolz [ <!ENTITY lol "lol"> <!ENTITY lol2 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"> <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;"> <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;"> ]> <lolz>&lol4;</lolz>
上面仅写了几层实体,实际攻击会嵌套几十层,解析器展开后会产生数十亿个字符串,迅速消耗系统资源。
XmlReaderSettings关键防护属性
在.NET中,推荐使用XmlReader配合XmlReaderSettings来读取不可信XML。下面列出最常用的防护配置:
| 属性 | 作用 |
|---|---|
| DtdProcessing | 设置为Ignore或Prohibit可禁止解析DTD,从根源阻断实体定义 |
| MaxCharactersInDocument | 限制整个文档展开后的最大字符数 |
| MaxCharactersFromEntities | 限制由实体展开所产生的最大字符数 |
| XmlResolver | 设为null可避免外部资源加载造成的SSRF或文件读取 |
安全解析上传XML的示例
以下C#代码演示了在ASP.NET接收上传文件时,如何使用XmlReaderSettings防止Billion Laughs攻击:
using System;
using System.IO;
using System.Xml;
public class XmlUploadHandler
{
public static void SafeParse(Stream xmlStream)
{
// 创建安全的读取配置
XmlReaderSettings settings = new XmlReaderSettings();
// 禁止DTD处理,防止实体定义
settings.DtdProcessing = DtdProcessing.Prohibit;
// 限制文档总字符数
settings.MaxCharactersInDocument = 1024 * 1024;
// 限制实体展开字符数
settings.MaxCharactersFromEntities = 0;
// 禁止解析外部引用
settings.XmlResolver = null;
using (XmlReader reader = XmlReader.Create(xmlStream, settings))
{
while (reader.Read())
{
// 正常业务处理节点
if (reader.NodeType == XmlNodeType.Element)
{
Console.WriteLine("元素: " + reader.Name);
}
}
}
}
}
常见错误用法
很多项目为了省事会采用下面这种方式加载XML:
// 危险:默认允许DTD,且无限制 XmlDocument doc = new XmlDocument(); doc.Load(uploadStream);
XmlDocument默认会处理DTD,且未限制展开大小,攻击者上传恶意文件就能直接打满内存。如果必须使用XmlDocument,也应当先通过安全的XmlReader读取,再传入Load方法。
小结
面对XML上传场景,开发者不应信任任何外部文档。将XmlReaderSettings的DtdProcessing设为Prohibit,配合字符数限制与关闭XmlResolver,就能在.NET中稳妥抵御Billion Laughs攻击。把这些配置封装成公共方法,可以让所有上传入口都具备基础的安全防御能力。
XmlReaderSettingsBillion_Laughs攻击XML上传修改时间:2026-07-29 10:39:27