
本文介绍在 .net 环境下使用 xslt 生成 html 时,如何确保 xml 节点内容(包括属性值和文本节点)均被正确 html 实体转义,防止 xss 风险并保证输出合法性。
在使用 System.Xml.Xsl.XslCompiledTransform(XSLT 1.0)进行 XML→HTML 转换时,一个常见且危险的问题是:xsl:value-of 对文本节点会自动转义(如 ——这会导致原始 XML 中已编码的 <script> 在属性中被二次解析为 <script>,从而破坏 HTML 结构,甚至引入跨站脚本(XSS)漏洞。</script>
例如,输入 XML 中的
<input type="text" value="hello <script>alert('!')</script>">浏览器将执行该脚本,而预期应为安全转义后的:
<input type="text" value="hello <script>alert('!')</script>">✅ 根本原因与限制
.NET 原生 XslCompiledTransform 仅支持 XSLT 1.0,其 xsl:output method="html" 的行为遵循旧版 HTML 输出规则:对属性值不强制 HTML 转义(尤其在模板字符串插值中),仅对 xsl:value-of 和 xsl:copy-of 的文本内容做最小化转义。这意味着你无法通过纯 XSLT 1.0 + .NET 原生引擎实现「所有上下文统一转义」的目标。
立即学习“前端免费学习笔记(深入)”;
✅ 推荐解决方案:升级至 XSLT 3.0 + XHTML 输出
唯一可靠、标准兼容的解决路径是采用 XSLT 3.0 引擎并设置 method="xhtml"。XHTML 模式要求所有属性值和文本内容均按 XML 规则严格转义,天然满足 HTML 安全输出需求。
以下是在 .NET 6/7+ 中使用 SaxonCS(Saxon 12.x 商业版)或 IKVM 跨编译的 Saxon HE 11.4(免费) 的完整示例:
using net.sf.saxon.s9api;
using System.IO;
using System.Xml;
// 初始化处理器(无验证模式,轻量)
var processor = new Processor(false);
string xml = @"<Contact><Name>hello <script>alert('!')</script></Name></Contact>";
string xslt = @"
<xsl:stylesheet version='3.0' xmlns:xsl='http://www.w3.org/1999/XSL/Transform'>
<xsl:output method='xhtml' indent='yes' html-version='5.0'
doctype-system='about:legacy-compat' omit-xml-declaration='yes'/>
<xsl:template match='/'>
<span data-title=\"{ concat('{ ''title'': ''', /Contact/Name, ''' }') }\">
Name: <xsl:value-of select='/Contact/Name'/>
Input: <input type='text' value='{/Contact/Name}'/>
</span>
</xsl:template>
</xsl:stylesheet>";
// 编译 XSLT 3.0 样式表
var compiler = processor.NewXsltCompiler();
var executable = compiler.Compile(xslt.AsSource()).Load30();
// 构建输入文档
var docBuilder = processor.NewDocumentBuilder();
var inputDoc = docBuilder.Build(xml.AsSource());
// 执行转换
using var resultWriter = new StringWriter();
executable.ApplyTemplates(inputDoc, processor.NewSerializer(resultWriter));
Console.WriteLine(resultWriter.ToString());✅ 输出结果(完全符合预期):
<span data-title="{ 'title': 'hello <script>alert('!')</script>' }">
Name: hello <script>alert('!')</script>
Input: <input type="text" value="hello <script>alert('!')</script>" />
</span>? 注意:data-title 中单引号被转义为 ' 是 XHTML 的合法行为(比 ' 更兼容),而 均被转义为 </>,确保属性值在 HTML 中始终作为纯文本存在。
⚠️ 重要注意事项
- 不要尝试手动拼接转义逻辑(如用 replace() 替换
- 避免 method="html":即使在 XSLT 3.0 下,method="html" 仍可能启用“宽松输出模式”,放弃部分转义保障;务必使用 method="xhtml"。
-
Saxon 版本选择:
- 免费方案:IKVM + Saxon HE 11.4(需额外配置类路径,详见 GitHub 示例);
- 生产推荐:SaxonCS 12.x(.NET 原生,商业授权,稳定性与性能更优)。
- xsl:output 必须显式声明:omit-xml-declaration="yes" 可避免 HTML5 文档开头出现 声明,提升兼容性。
✅ 总结
要实现 XML→HTML 转换中属性与文本节点的统一、安全、标准化转义,唯一健壮路径是:
? 放弃 .NET 原生 XslCompiledTransform(XSLT 1.0);
? 迁移至支持 XSLT 3.0 的现代处理器(如 Saxon);
? 显式指定 ;
? 依赖其内置的 XML 序列化规则,而非自定义转义逻辑。
此举不仅解决当前转义问题,更为未来支持 JSON 输出、流式处理、高阶函数等 XSLT 3.0 特性奠定基础。











