假如跨站偽造請求成功,怎么保證 ajax 的數據安全性?
問題的根源
答主 bumfod 說的確實有道理。crsf 的成因在一定程度上確實是由於http有狀態的原因(cookie維持狀態),並不是我之前所說的是 http無狀態的原因。
在此如果有人被誤導,我表示抱歉。
我們如果能驗證請求確實來是用戶確認(自願)發出的,而不是用戶誤導的情況下發出的,就能解決這個問題。
檢查Referer標頭確實是一種方案,但是該標頭也有可能被篡改,而且瀏覽器的實現不一。
舉個例子
以RESTful服務為例。
我們假設有一個資源Books
我們現在要對 Books 資源進行 POST 動作(請求)。那如何保證該動作不會被 csrf。
通常的做法是
我們在請求 POST 之前,要獲取到改對POST請求的 token。只要在 POST 請求時攜帶對應的 token 才可以 200, 否則 就是 403。 (token 的生成算法多種多樣,主要看需求)
http請求實例
// 獲取 token token = get_token(resource:'Books') // 請求 request(book).token(token).post()
表單實例
傳統 form 表單提交,為了解決 csrf 都會提供如下解決方案。
<form action="/books" method="POST" > <input type="text" name="name" /> <input type="text" name="author" /> <input type="text" name="publisher" /> <!-- csrf token --> <input type="hidden" name="token" value="j8i32hh2n2e8jdij92ecndaj9923dna9" /> </form>

在打開表單頁面的時候,會給表單生成一個令牌(token),當表單提交的時候,就會根據令牌來驗證表單的合法性。
token 怎么來的
token 的算法多種多樣,有依賴於session的,有依賴於請求指紋的,有依賴於ip的,還有依賴於cookie的 等等。具體看業務需求。大多數 web 框架的實現都是使用httpOnly cookie,但是
RESTful服務框架都是依賴於請求指紋的。這些沒有什么好壞之分。
請求指紋: 一個請求有很多指紋信息,比如說請求的url和url中的信息,('/books'),請求的ip,請求的UA,請求的標頭信息,等等。請求指紋來計算token的話,可以保證無狀態特性。
依賴ip: 對請求的客戶端 ip 值進行 hash 來作為 token;這樣是沒有狀態的。
依賴session: 這種方法很適用於web網站,就是當用戶登錄后,對用戶的請求根據用戶的信息生成 token 存放到 session 中。
如何組織
例如:
我們要修改系統至用戶的信息。
/users 資源
在 post 之前我們需要先獲取 修改資源的 token。
請求 獲取 token
GET /access_token?uid=1001&scope=users&authorize=sujijd1026ajkshd28saos29hdandja HTTP/1.1 Authorization: sujijd1026ajkshd28saos29hdandja Accept: application/json;charset=utf8 Cache-Control: no-cache Host: localhost:8080
后端可以根據標頭中的 Authorization, url 查詢字符串中的 uid, scope, authorize。來生成 access_token 比如使用 AES 堆成加密的base64字符串。最后能夠再加點鹽。
(這里標頭和查詢字符串中都存在 Authorization,可以自己取舍。)
返回 token
HTTP/1.1 200 OK Connection → keep-alive Content-Length → 2612 Content-Type → application/json; charset=utf-8 {"access_token": "diaj2iejqd8qqld9k92doijq9j2oie1u"}
修改user的post請求
POST /users?access_token=diaj2iejqd8qqld9k92doijq9j2oie1u HTTP/1.1 Authorization: sujijd1026ajkshd28saos29hdandja X-Token: diaj2iejqd8qqld9k92doijq9j2oie1u Accept: application/json;charset=utf8 Cache-Control: no-cache Host: localhost:8080 {"name": "如何變得有思想", "author": "阮一峰", "publisher": "人民郵電出版社"}
以上演示了如何組織一個使用令牌來實現csrf的請求。
回到問題上
回到問題的開始 ajax 該怎么做?
依然使用 /user/1001 為例,我們使用指紋加密的方法生成token。
/access_token 為令牌資源
var $request = function(ajax_param1) { return function(data){ return $.ajax(merge(ajax_param1, {'data': data})); }; }; var get_token = $request({ 'type': 'GET', 'url': '/access_token' }); var create_book = function(book){ // post return get_token({uid: 1001}).then(function(token){ return $request({ 'type': 'POST', 'url': '/books', // 關鍵點 'headers': { 'x-crsf-token': token.access_token, 'Authorization': 'sujijd1026ajkshd28saos29hdandja' } })(book); }); } var book = {name: '如何變得有思想', author: '阮一峰', publisher: '人民郵電出版社'}; create_book(book).then(function(res){ console.log(res); // 200 ok! book created });
將token放到url上還是放到標頭中,具體看開發者的喜好了。
