84669 person learning
152542 person learning
20005 person learning
5487 person learning
7821 person learning
359900 person learning
3350 person learning
180660 person learning
48569 person learning
18603 person learning
40936 person learning
1549 person learning
1183 person learning
32909 person learning
假如跨站伪造请求成功,怎么保证 ajax 的数据安全性?
走同样的路,发现不同的人生
从本质来讲ajax请求就是http请求。
答主 bumfod 说的确实有道理。crsf 的成因在一定程度上确实是由于http有状态的原因(cookie维持状态),并不是我之前所说的是 http无状态的原因。 在此如果有人被误导,我表示抱歉。
crsf
http有状态的原因
http无状态的原因
我们如果能验证请求确实来是用户确认(自愿)发出的,而不是用户误导的情况下发出的,就能解决这个问题。
检查Referer标头确实是一种方案,但是该标头也有可能被篡改,而且浏览器的实现不一。
以RESTful服务为例。
我们假设有一个资源Books
Books
我们现在要对 Books 资源进行 POST 动作(请求)。那如何保证该动作不会被 csrf。
POST
csrf
通常的做法是
我们在请求 POST 之前,要获取到改对POST请求的 token。只要在 POST 请求时携带对应的 token 才可以 200, 否则 就是 403。 (token 的生成算法多种多样,主要看需求)
token
// 获取 token token = get_token(resource:'Books') // 请求 request(book).token(token).post()
传统 form 表单提交,为了解决 csrf 都会提供如下解决方案。
html <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>
html
<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 的算法多种多样,有依赖于session的,有依赖于请求指纹的,有依赖于ip的,还有依赖于cookie的 等等。具体看业务需求。大多数 web 框架的实现都是使用httpOnly cookie,但是
httpOnly cookie
RESTful服务框架都是依赖于请求指纹的。这些没有什么好坏之分。
请求指纹: 一个请求有很多指纹信息,比如说请求的url和url中的信息,('/books'),请求的ip,请求的UA,请求的标头信息,等等。请求指纹来计算token的话,可以保证无状态特性。
请求指纹
依赖ip: 对请求的客户端 ip 值进行 hash 来作为 token;这样是没有状态的。
依赖ip
依赖session: 这种方法很适用于web网站,就是当用户登录后,对用户的请求根据用户的信息生成 token 存放到 session 中。
依赖session
例如: 我们要修改系统至用户的信息。
/users 资源
/users
在 post 之前我们需要先获取 修改资源的 token。 请求 获取 token
httpGET /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
http
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,可以自己取舍。)
Authorization
access_token
返回 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。
/user/1001
/access_token 为令牌资源
/access_token
javascript 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 });
javascript
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上还是放到标头中,具体看开发者的喜好了。
身份校驗是一切安全/加密傳輸的前提。
保障傳輸數據安全,首先要防止身份僞造。
防止身份僞造,只要設置一段只有身份正確時纔可能有的信息即可。
這種東西叫做密碼(password,用於身份校驗的保密字符串,不一定非得是用戶名/密碼組合中的密碼,也可以是 token)。
題主關注的是跨站請求僞造,那麼只要把密碼儲存在 CSRF 所無法得到的地方即可。
CSRF 只能僞造用戶進行操作,無法僞造用戶獲取信息。
然而 cookies 會被瀏覽器自動一起發送,達不到識別的目的,
所以一般的做法是在表單中添加一項 token:
由於 CSRF 的本質在於攻擊者欺騙用戶去訪問自己設置的地址,所以如果要求在訪問敏感數據請求時,要求用戶瀏覽器提供不保存在 cookie 中,並且攻擊者無法偽造的數據作為校驗,那麼攻擊者就無法再執行 CSRF 攻擊。這種數據通常是表單中的一個數據項。服務器將其生成並附加在表單中,其內容是一個偽亂數。當客戶端通過表單提交請求時,這個偽亂數也一並提交上去以供校驗。正常的訪問時,客戶端瀏覽器能夠正確得到並傳回這個偽亂數,而通過 CSRF 傳來的欺騙性攻擊中,攻擊者無從事先得知這個偽亂數的值,服務器端就會因為校驗 token 的值為空或者錯誤,拒絕這個可疑請求。
http://zh.wikipedia.org/wiki/%E8%B7%A8%E7%AB%99%E8%AF%B7%E6%B1%82%E4%BC%AA%E9%80%A0
樓下認爲 CSRF 的本質是 HTTP 請求無狀態,
這是錯誤的(現已修正,點讚!)。
CSRF 恰恰就是利用了瀏覽器存在漏洞的對「有狀態」HTTP 的實現,也就是 cookie。
所以,即便 HTTP 請求有狀態,只要瀏覽器不校驗調用者身份,依舊會被 CSRF。
CSRF 是欺騙用戶瀏覽器,讓其以用戶的名義執行操作。
(同樣出自維基百科)
项目是前后端分离的话,如何保证安全性。
一般使用cookie加密,在后端验证cookie是否通过
从本质来讲ajax请求就是http请求。
问题的根源
答主 bumfod 说的确实有道理。
crsf
的成因在一定程度上确实是由于http有状态的原因
(cookie维持状态),并不是我之前所说的是http无状态的原因
。在此如果有人被误导,我表示抱歉。
我们如果能验证请求确实来是用户确认(自愿)发出的,而不是用户误导的情况下发出的,就能解决这个问题。
检查Referer标头确实是一种方案,但是该标头也有可能被篡改,而且浏览器的实现不一。
举个例子
以RESTful服务为例。
我们假设有一个资源
Books
我们现在要对
Books
资源进行POST
动作(请求)。那如何保证该动作不会被csrf
。通常的做法是
我们在请求 POST 之前,要获取到改对
POST
请求的token
。只要在POST
请求时携带对应的 token 才可以 200, 否则 就是 403。 (token 的生成算法多种多样,主要看需求)http请求实例
表单实例
传统 form 表单提交,为了解决
csrf
都会提供如下解决方案。在打开表单页面的时候,会给表单生成一个令牌(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
后端可以根据标头中的
Authorization
, url 查询字符串中的 uid, scope, authorize。来生成access_token
比如使用 AES 堆成加密的base64字符串。最后能够再加点盐。(这里标头和查询字符串中都存在
Authorization
,可以自己取舍。)返回 token
修改user的post请求
以上演示了如何组织一个使用令牌来实现csrf的请求。
回到问题上
回到问题的开始 ajax 该怎么做?
依然使用
/user/1001
为例,我们使用指纹加密的方法生成token。/access_token
为令牌资源将token放到url上还是放到标头中,具体看开发者的喜好了。
身份校驗是一切安全/加密傳輸的前提。
保障傳輸數據安全,首先要防止身份僞造。
防止身份僞造,只要設置一段只有身份正確時纔可能有的信息即可。
這種東西叫做密碼(password,用於身份校驗的保密字符串,不一定非得是用戶名/密碼組合中的密碼,也可以是 token)。
題主關注的是跨站請求僞造,那麼只要把密碼儲存在 CSRF 所無法得到的地方即可。
CSRF 只能僞造用戶進行操作,無法僞造用戶獲取信息。
然而 cookies 會被瀏覽器自動一起發送,達不到識別的目的,
所以一般的做法是在表單中添加一項 token:
http://zh.wikipedia.org/wiki/%E8%B7%A8%E7%AB%99%E8%AF%B7%E6%B1%82%E4%BC%AA%E9%80%A0
樓下認爲 CSRF 的本質是 HTTP 請求無狀態,
這是錯誤的(現已修正,點讚!)。
CSRF 恰恰就是利用了瀏覽器存在漏洞的對「有狀態」HTTP 的實現,也就是 cookie。
所以,即便 HTTP 請求有狀態,只要瀏覽器不校驗調用者身份,依舊會被 CSRF。
(同樣出自維基百科)
项目是前后端分离的话,如何保证安全性。
一般使用cookie加密,在后端验证cookie是否通过