模板引擎与XSS防御
XSS攻击是在用户的浏览器上执行的,其形成过程则是在服务器端页面渲染时,注入了恶意的HTML代码导致的。从MVC架构来说,是发生在View层,因此使用“输出编码”的防御方法更加合理,这意味着需要针对不同上下文的XSS攻击场景,使用不同的编码方式。
“输出编码”的防御方法有以下几种:
- 在HTML标签中输出变量
- 在HTML属性中输出变量
- 在script标签中输出变量
- 在事件中输出变量
- 在CSS中输出变量
- 在URL中输出变量
针对不同的情况,使用不同的编码函数。那么现在流行的MVC框架是否符合这样的设计呢?答案是否定的。
在当前流行的MVC框架中,View层常用的技术是使用模板引擎对页面进行渲染,模板引擎本身可能会提供一些编码方法,比如,Django Templetes中,使用filters中的escape作为HtmlEncode的方法:
<h1>Hello, {{name|escape}}!</h1>
最好的XSS防御方案,在不同的场景需要使用不同的编码函数,如果统一使用者5个字符的HtmlEncode,则很有可能被攻击者绕过。在模板引擎中,可以实现自定义的编码函数,应用于不同场景。在Django中是使用自定义filters,在Velocity中则可以使用“宏”(velocimacro)。
Web框架与CSRF防御
CSRF攻击的目标,一般都会产生“写数据”操作的URL,比如“增”、“删”、“改”;而“读数据”操作并不是CSRF攻击的目标,因为在CSRF的攻击过程中攻击者无法获取到服务器端返回的数据,攻击者只是借用用户之手触发服务器动作,所以读取数据对于CSRF来说并无直接的意义。
因此,在Web应用开发中,有必要对“读操作”和“写操作”予以区分,比如要求所有“写操作”都使用HTTP POST。但实际上POST本身并不足以对抗CSRF,因为POST也是可以自动提交的。但是POST的使用,对于保护token有着积极的意义,而security token的私密性(不可预测性原则),是防御CSRF攻击的基础。
对于Web框架来说,可以自动地在所有涉及POST的代码中添加token,这些地方包括所有的form表单、所有的Ajax POST请求等。
完整的CSRF防御方案,对于Web框架来说有以下几点地方需要改动。
- 在Session中绑定token。如果不能保存到服务器Session中,则可以代替为保存到Cookie中。
- 在form表单中自动填入token字段,比如 <input type=hidden name="anti_csrf_token" value="$token"/>。
- 在Ajax请求中自动添加token,这可能需要已有的Ajax封装实现的支持。
- 在服务器端对比POST提交参数的token与Session中绑定的token是否一致,以验证CSRF攻击。
HTTP Headers管理
在Web框架中,可以对HTTP头进行全局化的处理,因此一些基于HTTP头的安全方案可以很好地实施。
比如针对HTTP返回头的CRLF注入,因为HTTP实际上可以看成是key-value对,比如:
Location: http://www.a.com
Host: 127.0.0.1
因此对抗CRLF的方案只需要在”value“中编码所有的\r\n即可。这里没有提到在”key"中编码\r\n,是因为让用户能够控制“key”是极其危险的事情,在任何情况下都不应该使其发生。
类似的,针对30X返回的HTTP Response,浏览器将会跳转到Location指定的URL,攻击者往往利用此类功能实施钓鱼或诈骗。
HTTP/1.1 302 Moved Temporarily
(...)
Location: http://www.phishing.tld
因此,对于框架来说,管理好跳转目的地址时很有必要的。一般来说,可以在两个地方做这件事:
- 如果Web框架提供统一的跳转函数,则可以在跳转函数内部实现一个白名单,指定跳转地址只能在白名单中;
- 另一个解决方式是控制HTTP的Location字段,限制Location的值只能是哪些地址,也能起到同样的效果,其本质还是白名单。
有很多与安全相关的Headers,也可以统一在Web框架中配置,比如:用来对抗ClickJacking的X-Frame-Option,Cookie的HttpOnly Flag。
数据持久层与SQL注入
使用ORM框架对SQL注入是有积极意义的。我们都知道对抗SQL注入的最佳方式就是使用“预编译绑定变量”。在实际解决SQL注入时,还有一个难点就是益康源复杂后,代码数量庞大,难以把可能存在SQL注入的地方不遗漏地找出来,而ORM框架为我们发现问题提供了一个便捷的途径。
以ORM框架ibatis举例,它是基于sqlmap的,生成的SQL语句都结构化地写在XML文件中。ibatis支持动态SQL,可以在SQL语句中插入动态变量:$value$,如果用户能够控制这个变量,则会存在一个SQL注入的漏洞。
而静态变量 #value# 则是安全的,因此在使用ibatis时,只需要搜索所有的sqlmap文件中是否包含动态变量即可。当业务需要使用动态SQL时,可以作为特例处理,比如在上层的代码逻辑中针对该变量进行严格的控制,以保证不会发生注入问题。