一、CSRF的原理
csrf漏洞的成因就是网站的cookie在浏览器中不会过期,只要不关闭浏览器或者退出登录,那以后只要是访问这个网站,都会默认你已经登录的状态。而在这个期间,攻击者发送了构造好的csrf脚本或包含csrf脚本的链接,如果服务器未验证当前请求人,用户点击了恶意请求,可能会执行一些用户不想做的功能(比如是添加账号等)。就这个操作不是用户真正想要执行的。
从下图可以看出,要完成一次CSRF攻击,受害者必须依次完成两个步骤:
1.登录受信任网站A,并在本地生成Cookie。
2.在不登出A的情况下,访问危险网站B。
看到这里,你也许会说:“如果我不满足以上两个条件中的一个,我就不会受到CSRF的攻击”。是的,确实如此,但你不能保证以下情况不会发生:
1.你不能保证你登录了一个网站后,不再打开一个tab页面并访问另外的网站。
2.你不能保证你关闭浏览器了后,你本地的Cookie立刻过期,你上次的会话已经结束。(事实上,关闭浏览器不能结束一个会话,但大多数人都会错误的认为关闭浏览器就等于退出登录/结束会话了......)
3.上图中所谓的攻击网站,可能是一个存在其他漏洞的可信任的经常被人访问的网站。
二、CSRF防御机制
在请求地址中添加token并验证
由于CSRF攻击的本质在于攻击者欺骗用户去访问自己设置的请求,所以如果在请求敏感数据时,要求用户浏览器提供不是保存在cookie中并且攻击者无法伪造的数据作为校验条件,那么攻击者就无法伪造未校验的请求进行CSRF攻击。
服务器将其生成并附加在窗体中,其内容是一个伪随机数。当客户端通过表单提交请求时,这个伪随机数也一并提交上去以供校验。正常的访问时,客户端浏览器能够正确得到并传回这个伪随机数,而通过CSRF传来的欺骗性攻击中,攻击者无从获取页面信息得知这个伪随机数的值,服务端就会因为校验token的值为空或者错误,拒绝这个可疑请求。
验证 HTTP Referer 字段
HTTP header中包含一个Referer字段,这个字段用于指明http请求的来源地址。当用户进行正常请求时,此Referer字段应该与请求的地址位于同一个域名下(http://bank.com)。如果是通过CSRF攻击伪造的请求则此Referer应当包含恶意网址的地址(http://evil.com)。所以通过对Referer来源地址的校验可以识别出伪造的恶意请求。
这种办法简单易行,工作量低,仅需要在关键访问处增加一步校验。但这种办法也有其局限性,因其完全依赖浏览器发送正确的Referer字段。虽然http协议对此字段的内容有明确的规定,但并无法保证来访的浏览器的具体实现,亦无法保证浏览器没有安全漏洞影响到此字段。并且也存在攻击者攻击某些浏览器,篡改其Referer字段的可能。
在HTTP头中自定义属性并验证
这种方法也是使用 token 并进行验证,和上一种方法不同的是,这里并不是把 token 以参数的形式置于 HTTP 请求之中,而是把它放到 HTTP 头中自定义的属性里。
通过 XMLHttpRequest 这个类,可以一次性给所有该类请求加上 csrftoken 这个 HTTP 头属性,并把 token 值放入其中。这样解决了上种方法在请求中加入 token 的不便,同时,通过 XMLHttpRequest 请求的地址不会被记录到浏览器的地址栏,也不用担心 token 会透过 Referer 泄露到其他网站中去。
使用验证码
在发送敏感请求前添加验证码,CSRF是在用户不知情的情况下构造了网络请求,因此添加验证码能强制用户必须与应用进行交互,服务器通过验证码来识别是不是用户主动发送的请求。
三、CSRF的一些相关问题
Token和Cookie都是进行身份验证的方式,但有以下区别:
存储位置不同:Cookie存储在浏览器,Token存储在服务器。
安全性不同:Cookie存储在内存中,不安全,Token存储在服务器,相对较安全。
使用方式不同:Cookie需要设置过期时间,Token不需要。
跨域限制不同:Cookie受同源策略限制,Token没有跨域限制。
客户端是如何解密token:
客户端解密Token的过程通常发生在客户端与服务器之间的身份验证交互过程中。以下是一个常见的基于JWT(JSON Web Token)的Token解密过程:
- 客户端向服务器发送请求,请求中携带Token。
- 服务器接收到请求后,从请求中获取Token。
- 服务器对Token进行验证,确保Token的有效性。验证方式通常包括检查Token是否过期、检查Token的签名是否被篡改等。
- 如果Token验证成功,服务器会使用事先约定好的密钥对Token进行解密,得到Token携带的用户信息。
- 服务器将解密后的用户信息与数据库中的用户信息进行比对,确认用户的身份和权限。
- 如果用户身份和权限验证通过,服务器会执行请求中指定的操作,并将结果返回给客户端。
需要注意的是,Token的解密过程需要使用服务器端保存的密钥,因此密钥的安全性至关重要。在生产环境中,密钥应该被妥善保管,避免泄露或被恶意获取。客户端解密Token的过程通常发生在客户端与服务器之间的身份验证交互过程中。