如何在 XSLT 中安全转义 XML 内容以生成符合 HTML 规范的输出

本文介绍在 .net 环境下使用 xslt 生成 html 时,如何确保 xml 节点内容(包括属性值和文本节点)均被正确 html 实体转义,防止 xss 风险并保证输出合法性。

在使用 System.Xml.Xsl.XslCompiledTransform(XSLT 1.0)进行 XML→HTML 转换时,一个常见且危险的问题是:xsl:value-of 对文本节点会自动转义(如 ——这会导致原始 XML 中已编码的 zuojiankuohaophpcnscriptyoujiankuohaophpcn 在属性中被二次解析为

例如,输入 XML 中的 hello zuojiankuohaophpcnscriptyoujiankuohaophpcnalert('!')zuojiankuohaophpcn/scriptyoujiankuohaophpcn,在 value="{/Contact/Name}" 中会被原样插入,生成:

浏览器将执行该脚本,而预期应为安全转义后的:

✅ 根本原因与限制

.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 = @"hello zuojiankuohaophpcnscriptyoujiankuohaophpcnalert('!')zuojiankuohaophpcn/scriptyoujiankuohaophpcn";
string xslt = @"

  
  
    
      Name: 
      Input: 
    
  
";

// 编译 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());

✅ 输出结果(完全符合预期):


  Name: hello zuojiankuohaophpcnscriptyoujiankuohaophpcnalert('!')zuojiankuohaophpcn/scriptyoujiankuohaophpcn
  Input: 
? 注意:data-title 中单引号被转义为 ' 是 XHTML 的合法行为(比 ' 更兼容),而 均被转义为 zuojiankuohaophpcn/youjiankuohaophpcn,确保属性值在 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 特性奠定基础。