假如跨站偽造請求成功,怎么保證 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上還是放到標頭中,具體看開發者的喜好了。