从session到token,csrf攻击

一、Http协议无状态

HTTP是一个基于TCP/IP通信协议来传递数据的。它是无连接和无状态的。
无连接:限制每次连接只处理一个请求。服务器处理完客户端的请求,并收到客户端的应答后,断开连接。采用这种方式可以节省传输时间。
无状态:协议对于客户端请求的处理没有记忆能力。也就是说这一次请求和上一次请求是没有任何联系的。
这种无状态的的好处是快速。但无状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。

二、Session 与 Token

1、Session机制

由于http协议的无状态,那就是必须管理会话,服务器必须记住哪些用户登录了系统,哪些用户往自己的购物车中放了商品,也就是说服务器必须把每客户端区分开。
于是就诞生了session,服务器给客户端发一个会话标识(session id), 也就是一个随机的字符串,每个客户端获取的都不一样,每次客户端向服务器发起HTTP请求的时候,把这个字符串一并带上,这样服务器就能区客户端的身份了。
不同的用户访问服务端的时候会在session对象中存储键值对,“键”用来存储开启这个用户信息的“钥匙”,在登录成功后,“钥匙”通过cookie返回给客户端,客户端存储为sessionId记录在cookie中。当客户端再次访问时,会默认带上cookie中的sessionId来实现会话机制。服务器创建session出来后,会把session的id号,以cookie的形式回写给客户机。这样,只要客户机的浏览器不关,再去访问服务器时,都会带着session的id号去,服务器发现客户机浏览器带session id过来了,就会使用内存中与之对应的session为之服务。

客户端访问服务器的流程如下:
1)客户端会发送一个http请求到服务器端;
2)服务器端接受客户端请求后,建立一个session,并发送一个http响应到客户端,这个响应头,其中就包含Set-Cookie头部。该头部包含了sessionId。Set-Cookie格式如下,具体请看Cookie详解
Set-Cookie: value[; expires=date][; domain=domain][; path=path][; secure]
3)在客户端发起的第二次请求,假如服务器给了set-Cookie,浏览器会自动在请求头中添加cookie;
4)服务器接收请求,解析cookie,验证session id信息,核对成功后返回response给客户端;

 

2、Token机制

我们知道session是有状态的,一般存于服务器内存或硬盘中,当服务器采用分布式或集群时,session就会面对负载均衡问题。
当多个服务器集群时,单台服务器是不能区分当前用户是否登录的,因为服务器之间不共享session。当然,我们可以把session集中保存在一台服务器上来解决,但是就不能完全达到负载均衡的效果。于是产生了token机制。
客户端登陆服务器时,服务端验证用户身份合法后收到后生成一个token传给客户端,客户端通过cookie、sessionStorage、localStorage存储token,再次请求时不会默认携带,需要在请求头中添加认证字段Authorization携带token信息,服务器端收到请求后解析请求头,再通过token信息查找用户登录状态。
token也称作令牌,通常由uid+time+sign[+固定参数]组成。
token的认证方式类似于临时的证书签名, 并且是一种服务端无状态的认证方式(所谓无状态就是服务端并不会保存身份认证相关的数据), 非常适合于前后端分离项目的REST API的应用场景。

token 的认证流程与session机制很相似:
1)用户登录,成功后服务器返回token给客户端;
2)客户端收到token后保存在客户端;
3)客户端再次访问服务器,将token放入http headers中;
4)服务器端采用filter过滤器对token进行校验。校验成功则返回请求数据,校验失败则返回错误码;

 

三、CSRF攻击

1、CSRF攻击概念:
CSRF跨站点请求伪造(Cross—Site Request Forgery),是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并执行一定的操作。因为浏览器认证过,所以网站会认为是真正的用户在操作。
攻击者盗用了你的身份,以你的名义发送恶意请求,对服务器来说这个请求是完全合法的,但是却完成了攻击者所期望的一个操作,比如以你的名义发送邮件、发消息,盗取你的账号,添加系统管理员,甚至于购买商品、虚拟货币转账等。

2、CSRF攻击原理:
如下,其中Web A为存在CSRF漏洞的网站,Web B为攻击者构建的恶意网站,User C为Web A网站的合法用户。
1)用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;
2)在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;
3)用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;
4)网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;
5)浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。

3、CSRF攻击实例:
受害者Bob在银行有一笔存款,通过对银行的网站发送请求 http://bank.example/withdraw?account=bob&amount=1000000&for=bob2 可以使 Bob 把 1000000 的存款转到 bob2 的账号下。通常情况下,该请求发送到网站后,服务器会先验证该请求是否来自一个合法的session,并且该session的用户Bob已经成功登陆。
黑客 Mallory 自己在该银行也有账户,他知道上文中的 URL 可以把钱进行转帐操作。Mallory 可以自己发送一个请求给银行:http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory。但是这个请求来自 Mallory 而非 Bob,他不能通过安全认证,因此该请求不会起作用。
这时,Mallory 想到使用 CSRF 的攻击方式,他先自己做一个网站,在网站中放入如下代码:src=”http://bank.example/withdraw?account=bob&amount=1000000&for=Mallory ”,并且通过广告等诱使 Bob 来访问他的网站。当 Bob 访问该网站时,上述 url 就会从 Bob 的浏览器发向银行,而这个请求会附带 Bob 浏览器中的 cookie 一起发向银行服务器。大多数情况下,该请求会失败,因为他要求 Bob 的认证信息。但是,如果 Bob 当时恰巧刚访问他的银行后不久,他的浏览器与银行网站之间的 session 尚未过期,浏览器的 cookie 之中含有 Bob 的认证信息。这时,悲剧发生了,这个 url 请求就会得到响应,钱将从 Bob 的账号转移到 Mallory 的账号,而 Bob 当时毫不知情。等以后 Bob 发现账户钱少了,即使他去银行查询日志,他也只能发现确实有一个来自于他本人的合法请求转移了资金,没有任何被攻击的痕迹。而 Mallory 则可以拿到钱后逍遥法外。

4、CSRF漏洞检测:
检测CSRF漏洞是一项比较繁琐的工作,最简单的方法就是抓取一个正常请求的数据包,去掉Referer字段后再重新提交,如果该提交还有效,那么基本上可以确定存在CSRF漏洞。
随着对CSRF漏洞研究的不断深入,不断涌现出一些专门针对CSRF漏洞进行检测的工具,如CSRFTester,CSRF Request Builder等。
以CSRFTester工具为例,CSRF漏洞检测工具的测试原理如下:使用CSRFTester进行测试时,首先需要抓取我们在浏览器中访问过的所有链接以及所有的表单等信息,然后通过在CSRFTester中修改相应的表单等信息,重新提交,这相当于一次伪造客户端请求。如果修改后的测试请求成功被网站服务器接受,则说明存在CSRF漏洞,当然此款工具也可以被用来进行CSRF攻击。

5、CSRF漏洞防御:
1)检查referer字段
HTTP头中有一个Referer字段,这个字段是用来标明请求来源于哪一个网址。通常来说,Referer字段应和请求的地址是在同一个域名下的。服务器可以通过判断Referer字段来判断请求的来源。
这种方法简单易行,但也有其局限性。http协议无法保证来访的浏览器的具体实现,可以通过篡改Referer字段的方式来进行攻击。
2)使用token
CSRF 攻击之所以能够成功,是因为黑客可以完全伪造用户的请求,该请求中所有的用户验证信息都是存在于 cookie 中,因此黑客可以在不知道这些验证信息的情况下直接利用用户自己的 cookie 来通过安全验证。要抵御 CSRF,关键在于在请求中放入黑客所不能伪造的信息,并且该信息不存在于 cookie 之中。可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。
这种方法要比检查 Referer 要安全一些,token可以在用户登陆后产生并放于session之中,然后在每次请求时把token从session中拿出,与请求中的 token 进行比对,但这种方法的难点在于如何把 token 以参数的形式加入请求。

 

posted @ 2020-02-19 17:03  Alan6  阅读(1112)  评论(0编辑  收藏  举报