跨站请求伪造(CSRF)是一种安全漏洞,攻击者利用受害者的 session 来通过受害者的浏览器发出请求。攻击者通过受害者的浏览器发送请求,并伪造成是受害者自己发出的请求。
建议你先熟悉CSRF,哪些是相关的攻击向量而哪些不是。我们建议从这里开始。
很难直接区分哪些请求是安全的而哪些请求容易被CSRF攻击,这是因为没有对插件及未来扩展的明确规范。因为历史遗留原因,浏览器的插件及扩展放宽了信任规则,引入了CSRF这样的漏洞,现在修复漏洞的任务就落到了框架头上。因此Play默认采取了保守的方案,但允许开发者自由修改策略。默认情况下,当下列所有情况全部满足时Play会要求做CSRF检查:
请求方法不是 GET,HEAD 或者 OPTIONS
请求包含一个或多个 Cookie 或者 Authorization 头
CORS 过滤器没有配置为信任请求源(request's origin)
注意:如果你使用了基于浏览器的身份验证,如NTLM或者客户端证书,而非基于cookies的或者HTTP的身份验证,那么你必须将 play.filters.csrf.header.protectHeaders设置为null,或者在protectHeaders中将身份验证的请求头包含进来。
Play的CSRF防护
Play支持多种方式来校验一个请求是否是CSRF请求。最优先的方式就是使用CSRF token。此token在query string或请求体中随每次请求一起提交,并在用户session中保存。Play每次都会校验这两个token是否存在及是否匹配。
为了对那些非浏览器的请求做简单保护,Play仅仅在请求头中检查cookies。如果请求是AJAX提交的,你可以将CSRF token保存在html页面中,然后将它放到Csrf-Token中提交。
或者你可以这样设置 play.filters.csrf.header.bypassHeaders:
如果设置了 X-Requested-With 头,Play就将请求视为安全的。很多JS 库都支持添加 X-Request-With ,如JQuery。
如果 Csrf-Token 头的值为 nocheck,或者提供了一个有效的 CSRF token,Play就将请求视为安全的。
具体的设置方法如下:
play.filters.csrf.header.bypassHeaders {
X-Requested-With = "*"
Csrf-Token = "nocheck"
}
在使用这种方式来防御CSRF攻击时应该格外谨慎,因为它会被历史遗留的浏览器插件破坏。
信任CORS请求
默认如果在CSRF 过滤器之前存在CORS过滤器,CSRF过滤器将允许特定来源的CORS请求。要禁用这种检查,可以将 play.filters.csrf.bypassCorsTrustedOrigins 设置为 false。
使用全局CSRF过滤器
注意:在Play2.6.x中,CSRF过滤器在Play的默认过滤器调用链中。要查看更多过滤器信息请点击这里。
Play提供了全局CSRF过滤器来过滤所有的请求。这也是为项目添加CSRF防御的最简单方式。也可以这样来手动添加它:
play.filters.enabled += "play.filters.csrf.CSRFFilter"
也可以通过在路由前添加nocsrf修饰符来为特定的路由禁用CSRF过滤:
+ nocsrf
POST /api/new controllers.Api.newThing
使用隐式请求
所有的CSRF功能都默认已经提供了一个默认的隐式 RequestHeader(也可以使用Request,它继承了RequestHeader),当没有找到时,它将不会编译。下面会展示具体的例子。
在Action中定义隐式Request
对于所有需要回去CSRF token的action来说都需要一个隐式的request:
// this actions needs to access CSRF token
def someMethod = Action { implicit request =>
// access the token as you need
Ok
}