0

假如跨站伪造请求成功,怎么保证 ajax 的数据安全性?

查看全部 4 个回答

4

已采纳

从本质来讲ajax请求就是http请求。

问题的根源

答主 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 都会提供如下解决方案。

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 怎么来的

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

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

后端可以根据标头中的 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 为令牌资源

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上还是放到标头中,具体看开发者的喜好了。

2

@bumfod 多谢你的指正,你说的确实有道理。我也参考了 维基百科 修正了答案。你对知识的严谨性的要求很值得我学习。

东来 · 2015年01月22日

展开评论