让ASP.NET接受有“潜在危险”的提交
public class HomeController : Controller { public ActionResult Index() { return View(); } [HttpPost] public ActionResult Index(string p1) { ViewBag.P1 = p1; return View(); } }
再改一下View:
<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Index</title> </head> <body> <div> <p>@ViewBag.P1</p> <form method="post"> <input type="text" name="p1" /> <input type="submit" /> </form> </div> </body> </html>
代码很简单,我们尝试给p1写点值,然后回显,输入个什么“123”或者“abc”是没问题的,但如果尝试输入“<a>”或者“<script>”之类的,就会出现:
“从客户端中检测到有潜在危险的Request.Form值”,和之前的出错提示有些类似,但也有显著不同,注意看,现在的Exception变成了HttpRequestValidationException,而不是之前的HttpException了。
public class HomeController : Controller { public ActionResult Index() { return View(); } [HttpPost] public ActionResult Index(string p1) { string p2 = Request.QueryString["p2"]; ViewBag.P1 = p1; ViewBag.P2 = p2; return View(); } }
View也改一下:
<!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Index</title> </head> <body> <div> <p>@ViewBag.P1</p> <p>@ViewBag.P2</p> <form method="post"> <input type="text" name="p1" /> <input type="submit" /> </form> </div> </body> </html>
<system.web> <httpRuntime requestPathInvalidCharacters="" requestValidationMode="2.0" /> <!--避免了URL路径的检查--> <pages validateRequest="false"></pages> <!--避免了aspx页面对URL参数及表单数据的检查--> </system.web>
但我们如今一般都很少用Web Form了,大家都MVC了对吧?对于ASP.NET MVC,还需要加一个全局过滤器,来避免其对URL参数及表单数据的检查,在Application_Start()中加入:
GlobalFilters.Filters.Add(new ValidateInputAttribute(false));
ALL DONE!
没有了“潜在危险”检查,假如危险真的来临了,那可怎么办?你是说XSS吗?一般情况下,如果你不需要像论坛那样让允许用户提交“富文本”的话,直接用HTML Encode来呈现数据就肯定不会有XSS问题啊,用户尝试提交一段JavaScript,你用HTML Encode了之后,提交啥,就直接在页面上显示啥,也没啥好担心的,使用到@Html.Raw的时候就要格外小心一些,差不多就OK了。但如果你真的需要允许客户提交富文本的话,情况就变得有些复杂了,有以下解决方案来避免XSS:
<a href="javascript: danger();">danger</a> <p onclick="danger();">danger</p> <div style="width: expression(danger());">danger</div>
看吧,防不胜防,还有各种不同的标签哦,各种onXXX事件,一些更高明的嵌套手法,唉,想做好是很难的了,这种方法不推荐!
第二种方法可以考虑使用HtmlAgilityPack这个库,用它来解释客户端提交上来的内容,一个个Tag去遍历。我建议使用“白名单”机制,只允许有限的tag,比如<a>,<p>,<div>,<ul>,<ol>,<li>等,遇到不认识的一律移除,这些标签里,也只允许有限的属性,遇到诸如“onXXX”这种不在白名单里的属性一律移除,这样就差不多了,还剩下一个比较麻烦的就是<a>标签的href属性,这个得做点特殊处理,自己判断一下这里边是否有潜在的危险,我的做法是:
static readonly Regex _regexIsSafe = new Regex("^([a-z][a-z,0-9]*):"); static bool IsLegalLink(string link) { link = link.Trim().ToLower(); Match match = _regexIsSafe.Match(link); if (match.Success) { string schema = match.Groups[1].Value; if (!"http".Equals(schema) && !"https".Equals(schema)) { return false; } } return true; }