### 跨站偽造請求的防范
[跨站偽造請求(Cross-site request forgery)](http://en.wikipedia.org/wiki/Cross-site_request_forgery), 簡稱為 XSRF,是個性化 Web 應用中常見的一個安全問題。前面的鏈接也詳細講述了 XSRF 攻擊的實現方式。
當前防范 XSRF 的一種通用的方法,是對每一個用戶都記錄一個無法預知的 cookie 數據,然后要求所有提交的請求中都必須帶有這個 cookie 數據。如果此數據不匹配 ,那么這個請求就可能是被偽造的。
Tornado 有內建的 XSRF 的防范機制,要使用此機制,你需要在應用配置中加上 `xsrf_cookies` 設定:
```
settings = {
"cookie_secret": "61oETzKXQAGaYdkL5gEmGeJJFuYh7EQnp2XdTP1o/Vo=",
"login_url": "/login",
"xsrf_cookies": True,
}
application = tornado.web.Application([
(r"/", MainHandler),
(r"/login", LoginHandler),
], **settings)
```
如果設置了 `xsrf_cookies`,那么 Tornado 的 Web 應用將對所有用戶設置一個 `_xsrf` 的 cookie 值,如果 `POST` `PUT` `DELET` 請求中沒有這 個 cookie 值,那么這個請求會被直接拒絕。如果你開啟了這個機制,那么在所有 被提交的表單中,你都需要加上一個域來提供這個值。你可以通過在模板中使用 專門的函數 `xsrf_form_html()` 來做到這一點:
```
<form action="/new_message" method="post">
{{ xsrf_form_html() }}
<input type="text" name="message"/>
<input type="submit" value="Post"/>
</form>
```
如果你提交的是 AJAX 的 `POST` 請求,你還是需要在每一個請求中通過腳本添加上 `_xsrf` 這個值。下面是在 FriendFeed 中的 AJAX 的 `POST` 請求,使用了 [jQuery](http://jquery.com/) 函數來為所有請求組東添加 `_xsrf` 值:
```
function getCookie(name) {
var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
return r ? r[1] : undefined;
}
jQuery.postJSON = function(url, args, callback) {
args._xsrf = getCookie("_xsrf");
$.ajax({url: url, data: $.param(args), dataType: "text", type: "POST",
success: function(response) {
callback(eval("(" + response + ")"));
}});
};
```
對于 `PUT` 和 `DELETE` 請求(以及不使用將 form 內容作為參數的 `POST` 請求) 來說,你也可以在 HTTP 頭中以 `X-XSRFToken` 這個參數傳遞 XSRF token。
如果你需要針對每一個請求處理器定制 XSRF 行為,你可以重寫 `RequestHandler.check_xsrf_cookie()`。例如你需要使用一個不支持 cookie 的 API, 你可以通過將 `check_xsrf_cookie()` 函數設空來禁用 XSRF 保護機制。然而如果 你需要同時支持 cookie 和非 cookie 認證方式,那么只要當前請求是通過 cookie 進行認證的,你就應該對其使用 XSRF 保護機制,這一點至關重要。