Freemarker中的默认转义

时间:2009-08-12 10:59:46

标签: java xss escaping freemarker

在Freemarker模板中,我们可以使用escape指令自动将转义应用于包含块内的所有插值:

<#escape x as x?html>
  <#-- name is escaped as html -->
  Hallo, ${name}
</#escape>

有没有办法以编程方式实现类似的效果,定义应用于模板中所有插值的默认转义,包括那些转义外转义?

感谢。

5 个答案:

答案 0 :(得分:5)

详细说明Attila的答案:您可以使用像this one这样的类,然后像这样包装模板加载器:

final TemplateLoader templateLoader = new ClassTemplateLoader(this.getClass(), templatePath) {
  /**
   * Replaces the normal template reader with something that changes the default
   * escaping to HTML as to avoid XSS attacks.
   */
  @Override
  public Reader getReader(Object templateSource, String encoding) throws IOException {
     return new WrappingReader(super.getReader(templateSource, encoding), "<#escape x as x?html>", "</#escape>");
  }
};

如果您在添加的部分中不包含换行符,则不会出现行编号问题。但是,您无法使用此方法使用&lt; #ftl&gt; / [#ftl]。

答案 1 :(得分:4)

自2.3.24起,每个模板都有一个关联的freemarker.core.OutputFormat对象,该对象指定是否以及如何转义${...}(和#{...})。 HTML,XML和RTF的OuputFormat开箱即用,但您也可以定义自己的格式。默认情况下,当选定的OutputFormat转义时,您可以像${foo?no_esc}一样明确防止转义。

有多种方法可以将模板与您想要的OutputFormat相关联。对于HTML和XML转义,建议的方法是将recognize_standard_file_extensions配置设置设置为true,然后使用HTML的ftlh文件扩展名和XML模板的ftlx文件扩展名。您还可以使用OutputFormat设置,根据任意模板名称(模板路径)模式将template_configurers - s与模板相关联。最后,您可以像configuration.setOutputFormat(HTMLOutputFormat.INSTANCE)一样全局设置默认输出格式。您也可以将模板顶部的输出格式覆盖为<#ftl output_format='HTML'>,但应该很少使用它。

相关文档页面:http://freemarker.org/docs/dgui_misc_autoescaping.htmlhttp://freemarker.org/docs/pgui_config_outputformatsautoesc.html

答案 2 :(得分:2)

有一个解决方案,虽然它并非完全无足轻重。您可以创建一个包装其他模板加载器的特殊TemplateLoader,并将&lt; #escape x注入x?html&gt;在模板源文本的序言中,并添加为它的结尾。

明显的缺点: - 第一行中的列号将被丢弃 - 如果您的模板以&lt; #ftl&gt;开头声明,你需要插入&lt; #excape&gt;在它之后。

答案 3 :(得分:2)

如果您使用&lt; #include parse = false ... /&gt;,链接中建议的TemplateLoaders需要稍微调整一下。在模板中包含例如HTML。

此外,您需要复制spring.ftl并使用您自己的副本与&lt; #ftl ..&gt;如汤姆所说,最先删除指令。

以下效果很好,虽然有点粗糙(使用guava over commons-io)

@Override  
public Reader getReader(Object pTemplateSource, String pEncoding) throws IOException {  
   Reader tReader = delegate.getReader(pTemplateSource, pEncoding);  
   try {  
       String tTemplateText = CharStreams.toString(tReader);

       //only include files ending with "ftl", as we may have some parse=false on included html files
       if (pTemplateSource.toString().endsWith("ftl")) {
           return new StringReader(ESCAPE_PREFIX + tTemplateText + ESCAPE_SUFFIX);
       }
       return new StringReader(tTemplateText);
   } finally {  
       Closeables.closeQuietly(tReader);  
   }  
} 

答案 4 :(得分:1)

您实际上并不需要WrappingReader来添加转义。您可以在任何TemplateLoader周围创建一个装饰器,在模板中读入一个String,在escapes中包装模板文本,然后返回一个读取结果String的StringReader。要了解这是如何完成的,请查看here。我发现的唯一问题是,如果你使用这种方法并从类路径中包含spring.ftl宏,它们会爆炸,因为它们有一个&lt; #ftl&gt;在最顶层的宣言。但是,您可以简单地将spring.ftl复制到模板路径中并删除声明(以及所有转义指令,因为默认情况下您将转义)。