防禦CSRF攻擊
跨站請求偽造(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
}