Rails CSRF安全性功能以及取消form验证
預設產生出來的 Controller 都繼承自 ApplicationController
。因此定義在這裡的方法可以被所有 Controller 取用,你可以在這邊定義一些共用的方法。預設的 application_controller.rb 長的如下:
class ApplicationController < ActionController::Base
protect_from_forgery
end
其中的 protect_from_forgery
方法啟動了 CSRF 安全性功能,所有非 GET 的 HTTP request都必須帶有一個 Token 參數才能存取, Rails 會自動在所有表單中幫你插入 Token 參數,預設的Layout 中也有一行 <%= csrf_meta_tag %>
標籤可以讓 JavaScript 讀取到這個 Token 。會需要關閉這個功能的時機是,你需要開放 API 給非瀏覽器客戶端,這時候你會需要取消它:
class ApisController < ApplicationController
skip_before_filter :verify_authenticity_token
end
1.跨站点请求伪造
首先,什么是跨站点请求伪造?
跨站点请求伪造-CSRF(Cross Site Request Forgery):是一种网络攻击方式。
说的白话一点就是,别的站点伪造你的请求,最可怕的是你还没有察觉并且接收了。听起来确实比较危险,下面有个经典的实例,了解一下跨站点请求伪造到底是怎么是实现的,知己知彼。
受害者:Bob 黑客:Mal 银行:bank bob在银行有一笔存款,可以通过请求
http://bank.example/withdraw?account=bob&amount=1000000&for=bob2
把钱转到bob2下。通常情况下,该请求到达网站后,服务器会验证请求是否来自一个合法的session,并且该session的用户Bob已登录。Mal在该银行也有账户,于是他伪造了一个地址http://bank.example/withdraw?account=bob&amount=1000000&for=mal
,但是如果直接访问,服务器肯定会识别出当前登录用户是mal而不是Bob,不能接受请求。于是通过CSRF攻击方式,将此链接伪造在广告下,诱使Bob自己点这个链接,那么请求就会携带Bob浏览起的cookie一起发送到银行,而Bob同时又登录了银行或者刚刚登录不久session还没有过期,那服务器发现cookie中有Bob的登录信息,就接收了响应,攻击就成功了
2.现在主要的几种防御CSRF的策略:
1. 验证Referer:
referer携带请求来源,从示例可以看出,受害者发送非法请求肯定不是在银行的界面,所以在服务器通过验证Referer是不是bank.example
开始就可以了,这个方法简单粗暴。
最简单的实现就是加个Filter:
完成上边的工具方法后,需要在form中添加token,如下:
验证可以放在Filter里也可以放在Service里,只要保证请求/test/tokentest.htm
会先验证就行了。直接调用工具方法Token.verify()
以下就不赘述了。
3. 在HTTP头中自定义属性并验证:
这个方法和上面那个类似,也是设置token,只是把token设置为HTTP头中的自定义属性。
通过XMLHttpRequest可以一次性给所有该类请求的HTTP头加上token 属性,但是XMLHttpRequest请求通常用于Ajax方法对局部页面的异步刷新,比较有局限性;而且通过XMLHttpRequest请求的地址不会被记录到浏览器的地址栏,一方面不会通过Referer泄露token,另一方面会导致前进,后退,刷新,收藏等操作失效,所以还是慎用。
虽然上面介绍了几种方法,但现在还没有一种完美的解决方案,但是通过Referer和Token方案结合起来使用,也能很得有效CSRF攻击。