SpringSecurity CSRF引发的思考CookieSessionToken和JWT

Posted 知难行难1985

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringSecurity CSRF引发的思考CookieSessionToken和JWT相关的知识,希望对你有一定的参考价值。

目录

1.最重要的问题:

1.CSRF攻击是怎样跨域拿到cookie的?

2.怎样阻止CSRF攻击?

2.CSRF 跨站伪造请求

3.Cookie和Session

4.Cookie和Session的区别

5.什么是Token(重点)

仅仅用seesion+cookie存在的问题

【疑问?】token放在客户端哪里?客户端是怎么每次取到token的

6.JSON Web Token(JWT)

Token和JWT:

7.参考文章:


1.最重要的问题:

1.CSRF攻击是怎样跨域拿到cookie的?

【疑问?】浏览器对于 cookie 也是存在同源限制的,也就是与 cookie(domain)处于不同源的网站,浏览器是不会让该网站获取到这个 cookie。那为什么csrf攻击是可行的?

原因是浏览器使用 cookie 的情况

非跨域 XHR(XMLHttpRequest : XMLHttpRequest Level 2 使用指南 - 阮一峰的网络日志Ajax基于XHR)请求情况下,浏览器在发起请求的时候会把符合要求的 cookie 自动带上,(域名,有效期,路径,secure 属性)。跨域 XHR 的请求的情况下,也可以携带 Cookie。浏览器允许跨域提交表单,也就是说,向同一个服务器发请求时会自动带上浏览器保存的对于那个服务器的cookie,而不管你从哪个网站发请求。因为每次请求都会携带cookie在http头上,也是造成带宽浪费的一个原因。但是跨域读取cookie是读取不了的,攻击者没办法知道cookie的内容。

2.怎样阻止CSRF攻击?

1. 验证HTTP Referer字段
根据HTTP协议,在HTTP头中有一个字段叫Referer,它记录了该HTTP请求的来源地址。在通常情况下,访问一个安全受限页面的请求必须来自于同一个网站。比如某银行的转账是通过用户访问http://bank.test/test?page=10&userID=101&money=10000页面完成,用户必须先登录bank. test,然后通过点击页面上的按钮来触发转账事件。当用户提交请求时,该转账请求的Referer值就会是转账按钮所在页面的URL(本例中,通常是以bank. test域名开头的地址)。而如果攻击者要对银行网站实施CSRF攻击,他只能在自己的网站构造请求,当用户通过攻击者的网站发送请求到银行时,该请求的Referer是指向攻击者的网站。因此,要防御CSRF攻击,银行网站只需要对于每一个转账请求验证其Referer值,如果是以bank. test开头的域名,则说明该请求是来自银行网站自己的请求,是合法的。如果Referer是其他网站的话,就有可能是CSRF攻击,则拒绝该请求。

2. 在请求地址中添加token并验证

攻击者在攻击网站放一个src是被攻击网站的转账表单。在session+cookie的情况下,用户打开恶意网页的时候就已经上当了了.因为form 发起的 POST 请求并不受到浏览器同源策略的限制,因此可以任意地使用其他域的 Cookie 向其他域发送 POST 请求,形成 CSRF 攻击。在post请求的瞬间,cookie会被浏览器自动添加到请求头中。

解决思路:提交时要附加本域才能获得信息(token只有你正确输入密码之后才返还)

CSRF的一个特征是,攻击者无法直接窃取到用户的信息(Cookie,Header,网站内容等),仅仅是冒用Cookie中的信息。

而CSRF攻击之所以能够成功,是因为服务器误把攻击者发送的请求当成了用户自己的请求。那么我们可以要求所有的用户请求都携带一个CSRF攻击者无法获取到的Token。服务器通过校验请求是否携带正确的Token,来把正常的请求和攻击的请求区分开,也可以防范CSRF的攻击。token是开发者为了防范csrf而特别设计的令牌,浏览器不会自动添加到headers里,攻击者也无法访问用户的token,所以提交的表单无法通过服务器过滤,也就无法形成攻击。

可以使用JWT来完成这个功能。拿Spring Security来说,可以将Spring Security中的CSRF功能关掉,因为我们已经使用了JWT,这已经能够阻止CSRF攻击。如果不用JWT呢?那么就会用session (配合sessionId放在cookie中),此时需要用csrf-token.

另外:Token或者JWT可以放到cookie中,但是发送任何请求的时候,需要从cookie中获取到token(客户端从 cookie 中获取,同源策略限制在其它域中对cookie 操作,也能防止CSRF攻击),然后将token放到请求的header或者是参数中,但就是怕XSS攻击。所以最安全的方法是将token放入local storage,可以同时防止CSRF和XSS攻击

CSRF攻击之所以能够成功,是因为攻击者可以伪造用户的请求,该请求中所有的用户验证信息都存在于Cookie中,因此攻击者可以在不知道这些验证信息的情况下直接利用用户自己的Cookie来通过安全验证。由此可知,抵御CSRF攻击的关键在于:在请求中放入攻击者所不能伪造的信息,并且该信息不存在于Cookie之中。鉴于此,系统开发者可以在HTTP请求中以参数的形式加入一个随机产生的token,并在服务器端建立一个拦截器来验证这个token,如果请求中没有token或者token内容不正确,则认为可能是CSRF攻击而拒绝该请求。

3. 在HTTP头中自定义属性并验证

攻击者在攻击网站放一个src是被攻击网站的转账表单。在session+cookie的情况下,用户打开恶意网页的时候就已经上当了了.因为form 发起的 POST 请求并不受到浏览器同源策略的限制,因此可以任意地使用其他域的 Cookie 向其他域发送 POST 请求,形成 CSRF 攻击。在post请求的瞬间,cookie会被浏览器自动添加到请求头中。

解决思路:提交时要附加本域才能获得信息(token只有你正确输入密码之后才返还)

CSRF的一个特征是,攻击者无法直接窃取到用户的信息(Cookie,Header,网站内容等),仅仅是冒用Cookie中的信息。

而CSRF攻击之所以能够成功,是因为服务器误把攻击者发送的请求当成了用户自己的请求。那么我们可以要求所有的用户请求都携带一个CSRF攻击者无法获取到的Token。服务器通过校验请求是否携带正确的Token,来把正常的请求和攻击的请求区分开,也可以防范CSRF的攻击。token是开发者为了防范csrf而特别设计的令牌,浏览器不会自动添加到headers里,攻击者也无法访问用户的token,所以提交的表单无法通过服务器过滤,也就无法形成攻击。

可以使用JWT来完成这个功能。拿Spring Security来说,可以将Spring Security中的CSRF功能关掉,因为我们已经使用了JWT,这已经能够阻止CSRF攻击。如果不用JWT呢?那么就会用session(配合sessionId放在cookie中),此时需要用csrf-token.

另外:Token或者JWT可以放到cookie中,但是发送任何请求的时候,需要从cookie中获取到token(客户端从 cookie 中获取,同源策略限制在其它域中对cookie 操作,也能防止CSRF攻击),然后将token放到请求的header或者是参数中,但就是怕XSS攻击。所以最安全的方法是将token放入local storage,可以同时防止CSRF和XSS攻击

自定义属性的方法也是使用token并进行验证,和前一种方法不同的是,这里并不是把token以参数的形式置于HTTP请求之中,而是把它放到HTTP头中自定义的属性里。通过XMLHttpRequest这个类,可以一次性给所有该类请求加上csrftoken这个HTTP头属性(当然也可以使用自定义header,根据自己需要),并把token值放入其中。这样解决了前一种方法在请求中加入token的不便,同时,通过这个类请求的地址不会被记录到浏览器的地址栏,也不用担心token会通过Referer泄露到其他网站。

2.CSRF 跨站伪造请求

攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。
一般流程如下:

该攻击特点:

①攻击一般发起在第三方网站,而不是被攻击的网站。被攻击的网站无法防止攻击发生。
②攻击利用受害者在被攻击网站的登录凭证,冒充受害者提交操作;而不是直接窃取数据。
③整个过程攻击者并不能获取到受害者的登录凭证,仅仅是“冒用”。
④跨站请求可以用各种方式:图片URL、超链接、CORS、Form提交等等。部分请求方式可以直接嵌入在第三方论坛、文章中,难以进行追踪。
⑤CSRF通常是跨域的,因为外域通常更容易被攻击者掌控。但是如果本域下有容易被利用的功能,比如可以发图和链接的论坛和评论区,攻击可以直接在本域下进行,而且这种攻击更加危险。

针对其攻击特点也是有一些列防护措施的,之后会讲~

3.Cookie和Session

HTTP 是无状态的协议(对于事务处理没有记忆能力,每次客户端和服务端会话完成时,服务端不会保存任何会话信息):每个请求都是完全独立的,服务端无法确认当前访问者的身份信息,无法分辨上一次的请求发送者和这一次的发送者是不是同一个人。所以服务器与浏览器为了进行会话跟踪(知道是谁在访问我),就必须主动的去维护一个状态,这个状态用于告知服务端前后两个请求是否来自同一浏览器。

而这个状态需要通过 cookie 或者 session 去实现。

cookie 存储在客户端: cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。

cookie 是不可跨域的: 每个 cookie 都会绑定单一的域名,无法在别的域名下获取使用,一级域名和二级域名之间是允许共享使用的

session 是另一种记录服务器和客户端会话状态的机制

session 是基于 cookie 实现的,session 存储在服务器端,sessionId 会被存储到客户端的cookie 中

 

cookie只是实现session的其中一种方案。虽然是最常用的,但并不是唯一的方法。禁用cookie后还有其他方法存储,比如放在url中

现在大多都是Session + Cookie,但是只用session不用cookie,或是只用cookie,不用session在理论上都可以保持会话状态。可是实际中因为多种原因,一般不会单独使用

用session只需要在客户端保存一个id,实际上大量数据都是保存在服务端。如果全部用cookie,数据量大的时候客户端是没有那么多空间的。

如果只用cookie不用session,那么账户信息全部保存在客户端,一旦被劫持,全部信息都会泄露。并且客户端数据量变大,网络传输的数据量也会变大


4.Cookie和Session的区别

①安全性: Session 比 Cookie 安全,Session 是存储在服务器端的,Cookie 是存储在客户端的。
②存取值的类型不同:Cookie 只支持存字符串数据,想要设置其他类型的数据,需要将其转换成字符串,Session 可以存任意数据类型。
③有效期不同: Cookie 可设置为长时间保持,比如我们经常使用的默认登录功能,Session 一般失效时间较短,客户端关闭(默认情况下)或者 Session 超时都会失效。
④存储大小不同: 单个 Cookie 保存的数据不能超过 4K,Session 可存储数据远高于 Cookie,但是当访问量过多,会占用过多的服务器资源。

【疑问?】浏览器对于 cookie 也是存在同源限制的,也就是与 cookie(domain)处于不同源的网站,浏览器是不会让该网站获取到这个 cookie。那为什么csrf攻击是可行的?

原因是浏览器使用 cookie 的情况

除了跨域 XHR 请求情况下,浏览器在发起请求的时候会把符合要求的 cookie 自动带上。(域名,有效期,路径,secure 属性),跨域 XHR 的请求的情况下,也可以携带 Cookie。同时浏览器允许跨域提交表单。也就是说,向同一个服务器发请求时会自动带上浏览器保存的对于那个服务器的cookie,而不管你从哪个网站发请求。因为每次请求都会携带在http头上,也是造成带宽浪费的一个原因。但是读取cookie是读取不了的,攻击者没办法知道cookie的内容

5.什么是Token(重点)

访问资源接口(API)时所需要的资源凭证

简单 token 的组成: uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,token 的前几位以哈希算法压缩成的一定长度的十六进制字符串)

特点:
①服务端无状态化()、可扩展性好
②支持移动端设备
③安全
④支持跨程序调用

token 的身份验证流程:

每一次请求都需要携带 token,需要把 token 放到 HTTP 的 Header 里

基于 token 的用户认证是一种服务端无状态的认证方式,服务端不用存放 token 数据。用解析 token 的计算时间换取 session 的存储空间,从而减轻服务器的压力,减少频繁的查询数据库

token 完全由应用管理,所以它可以避开同源策略

仅仅用seesion+cookie存在的问题

①session就会面对负载均衡问题
session是有状态的,,当服务器采用分布式或集群时,session就会面对负载均衡问题。负载均衡多服务器的情况,不好确认当前用户是否登录,因为多服务器不共享session。这个问题也可以将session存在一个服务器中来解决,但是就不能完全达到负载均衡的效果。

解决思路:服务器无状态化

客户端登陆传递信息给服务端,服务端收到后把用户信息加密(token)传给客户端,客户端将token存放于localStroage等容器中。客户端每次访问都传递token,服务端解密token,就知道这个用户是谁了。通过cpu加解密,服务端就不需要存储session占用存储空间,就很好的解决负载均衡多服务器的问题了。这个方法叫做JWT(Json Web Token)(和token这个概念区别一下)

②无法防止csrf攻击

攻击者在攻击网站放一个src是被攻击网站的转账表单。在session+cookie的情况下,用户打开恶意网页的时候就已经上当了了.因为form 发起的 POST 请求并不受到浏览器同源策略的限制,因此可以任意地使用其他域的 Cookie 向其他域发送 POST 请求,形成 CSRF 攻击。在post请求的瞬间,cookie会被浏览器自动添加到请求头中。

解决思路:提交时要附加本域才能获得信息(token只有你正确输入密码之后才返还)

CSRF的一个特征是,攻击者无法直接窃取到用户的信息(Cookie,Header,网站内容等),仅仅是冒用Cookie中的信息。

而CSRF攻击之所以能够成功,是因为服务器误把攻击者发送的请求当成了用户自己的请求。那么我们可以要求所有的用户请求都携带一个CSRF攻击者无法获取到的Token。服务器通过校验请求是否携带正确的Token,来把正常的请求和攻击的请求区分开,也可以防范CSRF的攻击。token是开发者为了防范csrf而特别设计的令牌,浏览器不会自动添加到headers里,攻击者也无法访问用户的token,所以提交的表单无法通过服务器过滤,也就无法形成攻击。

【疑问?】token放在客户端哪里?客户端是怎么每次取到token的

①输出到客户端页面上,服务端将 token 渲染到 html 中。
也就是通过一个 dom 结点保存 token 信息,客户端就可以通过 dom 操作获取到该 token 内容。(同源策略会限制脚本 API 操作)

如果是get请求的话可以直接附加在请求后面,每次页面加载时,使用JS遍历整个DOM树,对于DOM中所有的a和form标签后加入Token。这样可以解决大部分的请求,但是对于在页面加载之后动态生成的html代码,这种方法就没有作用,还需要程序员在编码时手动添加Token。

如果是post请求还可以加一个隐藏表单

 <input type=”hidden” name=”csrftoken” value=”tokenvalue”/>

②如果你能保证攻击者没办法得到cookie的内容,放在里面也没关系。
客户端从 cookie 中获取(同源策略限制 cookie 操作),就是怕XSS攻击。

③放在localstorage里面,跨域无法操作
也是受同源策略影响,和cookie不同,仅仅在客户端中保存,不会进行服务器通信。

6.JSON Web Token(JWT)

是目前最流行的跨域认证解决方案,是一种认证授权机制。上面也提到了,针对负载均衡的解决方案

教程(阮一峰):http://www.ruanyifeng.com/blog/2018/07/json_web_token-tutorial.html

Token和JWT:

相同:
● 都是访问资源的令牌
● 都可以记录用户的信息
● 都是使服务端无状态化
● 都是只有验证成功后,客户端才能访问服务端上受保护的资源

区别:
● Token:服务端验证客户端发送过来的 Token 时,还需要查询数据库获取用户信息,然后验证 Token 是否有效。
● JWT:将 Token 和 Payload 加密后存储于客户端,服务端只需要使用密钥解密进行校验(校验也是 JWT 自己实现的)即可,不需要查询或者减少查询数据库,因为 JWT 自包含了用户信息和加密的数据。
 

7.参考文章:

浏览器同源政策及其规避方法 - 阮一峰的网络日志

跨域资源共享 CORS 详解 - 阮一峰的网络日志

XMLHttpRequest Level 2 使用指南 - 阮一峰的网络日志

Ajax基于XHR: XHR和AJAX终于搞懂了!! - 知乎

以上是关于SpringSecurity CSRF引发的思考CookieSessionToken和JWT的主要内容,如果未能解决你的问题,请参考以下文章

springsecurity:使用从 CrossOrigin REST 服务获得的 CSRF 令牌来发布数据

一次渗透测试引发的Json格式下CSRF攻击的探索

Spring Security 和 CSRF 攻击

SpringSecurity的防Csrf攻击

[SpringSecurity]web权限方案_CSRF功能

将 AngularJS 与 SpringSecurity3.2 一起用于 CSRF