几天前,同事浪子在用VS2010创建MVC站点时,发现里面的页面模板中大量使用了一种新的模板语法:<%: %> ,由于之前他比较少使用ASP.NET MVC,还以为是MVC里面新增的一种模板语法,而我很早就开始使用MVC工程,但也从来没有见过此种语法。于我们共同断定这可能是ASP.NET4.0中新出的一种模板语法。果然,我们在ASP.NET 4.0白皮书上找到了答案:这是用于替代<%= %>而推出的一种新的页面文件输出语法。
在白皮书中指出,一些站点,特别是ASP.NET MVC推出以后,越来越多的开发人员,选择在页面中使用<%= %>来输出字符串文本,而不是使用Server Control。这样做本身没有任何问题,也会让页面模板更为简洁,但是这样做却有可能带来安全性的问题:如果使用<%= %> 输出来自用户的输入的内容,由于<%= %> 语法没有进行HTML Encode处理,而大多数开发人员一般都不太注意自己对输出文本进行HTML Encode(我就是其中一个),这样就有可能就会带来XSS的安全性问题。因此,在一些需要显示用户输入内容时,不管使用Response.Write,还是使用<%= %>(事实上<%= %>的效果等同于Response.Write,关于更多的模板语法请参考:http://quickstarts.asp.net/QuickStartv20/aspnet/doc/pages/syntax.aspx),我们的最佳实践应该都是需要先调用一下HttpUtility.HtmlEncode对文本进行一次Html编码,以保证输出安全。这样做还是稍微有一点点的麻烦,于是<%: %> 就出现了。使用<%: %> 的效果等同于我们调用了HtmlEncode以后,再使用<%=%>,实际上就是在保证安全的同时提高生产力。
我们在VS2010中创建的MVC站点,在LogOnUserControl.ascx文件中有这么一段代码:
<%: Html.ActionLink("Log On", "LogOn", "Account") %>
它编译的结果如下:
__w.Write(HttpUtility.HtmlEncode(base.Html.ActionLink("Log On", "LogOn", "Account")));
这个新的语法原理本身就是这么简单。<%:%>会帮我们进行一次HtmlEncode,但是在一些情况下,我们不希望输出的文本被自动HtmlEncode;或者我们要输出的内容本身已经做过一次HtmlEncode,而且在项目我们希望统一使用<%:%>,那么再使用这个语法将会对文本进行二次编码。在这种情况下,ASP.NET 4.0中,还增加了IHtmlString接口,只要你要输出的实例实现了这个接口,HttpUtility.HtmlEncode这个方法将不会对IHtmlString的接口进行Encode,对应的HtmlEncode方法的实现如下:
public static string HtmlEncode(object value) { if (value == null) { return null; } IHtmlString str = value as IHtmlString; if (str != null) { return str.ToHtmlString(); } return HtmlEncode(Convert.ToString(value, CultureInfo.CurrentCulture)); }
在ASP.NET 4.0,IHtmlString的默认实现是:HtmlString,如下输出代码将不会进行HtmlEncode:
<%: new HtmlString("<strong>HTML that is not encoded</strong>") %>
在MVC 2中,我们发现HtmlHelper原来返回的字符串,改为返回MvcHtmlString(由于MVC2的程序集并不直接使用ASP.NET4.0,因此它通过动态类的方式来实现IHtmlString接口),就是这个原因,不希望<%:%>对MVC输出的Html标签进行Html Encode,否则在ASP.NET 4.0可使用<%:%>将无法正常工作。
这里还要提一点,使用这个语法,不完全保证页面的没有XSS漏洞,例如当用户提交的内容中,使用了不带引号的属性值引用Html标签,一样可能会受到影响。另外,这个语法不进行javascript编码。