什么是会话固定攻击?Spring Boot 中要如何防御会话固定攻击?

前两天和大家聊了 Spring Security 中的 session 并发问题,和小伙伴们聊了如何像 QQ 一样,用户在一台设备上登录成功之后,就会自动踢掉另一台设备上的登录。

当然,Spring Security 中,关于 session 的功能不仅仅是这些,之前和大家说我们学习 Spring Security,也是学习各种各样的网络攻击与防御策略,今天松哥就来和大家聊一个简单的:什么是会话固定攻击以及 Spring Security 中如何防止会话固定攻击。

本文是 Spring Security 系列的第 16 篇,阅读本系列前面的文章有助于更好的理解本文:

  1. 挖一个大坑,Spring Security 开搞!
  2. 松哥手把手带你入门 Spring Security,别再问密码怎么解密了
  3. 手把手教你定制 Spring Security 中的表单登录
  4. Spring Security 做前后端分离,咱就别做页面跳转了!统统 JSON 交互
  5. Spring Security 中的授权操作原来这么简单
  6. Spring Security 如何将用户数据存入数据库?
  7. Spring Security+Spring Data Jpa 强强联手,安全管理只有更简单!
  8. Spring Boot + Spring Security 实现自动登录功能
  9. Spring Boot 自动登录,安全风险要怎么控制?
  10. 在微服务项目中,Spring Security 比 Shiro 强在哪?
  11. SpringSecurity 自定义认证逻辑的两种方式(高级玩法)
  12. Spring Security 中如何快速查看登录用户 IP 地址等信息?
  13. Spring Security 自动踢掉前一个登录用户,一个配置搞定!
  14. Spring Boot + Vue 前后端分离项目,如何踢掉已登录用户?
  15. Spring Security 自带防火墙!你都不知道自己的系统有多安全!

1.HttpSession

看前面文章的评论,我发现有的小伙伴对 HttpSession 还不太熟悉,所以在讲会话固定攻击之前,先来和大家说一说 HttpSession。

HttpSession 是一个服务端的概念,服务端生成的 HttpSession 都会有一个对应的 sessionid,这个 sessionid 会通过 cookie 传递给前端,前端以后发送请求的时候,就带上这个 sessionid 参数,服务端看到这个 sessionid 就会把这个前端请求和服务端的某一个 HttpSession 对应起来,形成“会话”的感觉。

浏览器关闭并不会导致服务端的 HttpSession 失效,想让服务端的 HttpSession 失效,要么手动调用 HttpSession#invalidate 方法;要么等到 session 自动过期;要么重启服务端。

但是为什么有的人会感觉浏览器关闭之后 session 就失效了呢?这是因为浏览器关闭之后,保存在浏览器里边的 sessionid 就丢了(默认情况下),所以当浏览器再次访问服务端的时候,服务端会给浏览器重新分配一个 sessionid ,这个 sessionid 和之前的 HttpSession 对应不上,所以用户就会感觉 session 失效。

注意前面我用了一个默认情况下,也就是说,我们可以通过手动配置,让浏览器重启之后 sessionid 不丢失,但是这样会带来安全隐患,所以一般不建议。

以 Spring Boot 为例,服务端生成 sessionid 之后,返回给前端的响应头是这样的:

在服务端的响应头中有一个 Set-Cookie 字段,该字段指示浏览器更新 sessionid,同时大家注意还有一个 HttpOnly 属性,这个表示通过 JS 脚本无法读取到 Cookie 信息,这样能有效的防止 XSS 攻击。

下一次浏览器再去发送请求的时候,就会自觉的携带上这个 jsessionid 了:

大家先对 HttpSession 有一个大致的了解,接下来我们再来看会话固定攻击。

2.会话固定攻击

什么是会话固定攻击?英文叫做 session fixation attack。

正常来说,只要你不关闭浏览器,并且服务端的 HttpSession 也没有过期,那么维系服务端和浏览器的 sessionid 是不会发生变化的,而会话固定攻击,则是利用这一机制,借助受害者用相同的会话 ID 获取认证和授权,然后利用该会话 ID 劫持受害者的会话以成功冒充受害者,造成会话固定攻击。

一般来说,会话固定攻击的流程是这样,以淘宝为例:

  1. 攻击者自己可以正常访问淘宝网站,在访问的过程中,淘宝网站给攻击者分配了一个 sessionid。
  2. 攻击者利用自己拿到的 sessionid 构造一个淘宝网站的链接,并把该链接发送给受害者。
  3. 受害者使用该链接登录淘宝网站(该链接中含有 sessionid),登录成功后,一个合法的会话就成功建立。
  4. 攻击者利用手里的 sessionid 冒充受害者。

在这个过程中,如果淘宝网站支持 URL 重写,那么攻击还会变得更加容易。

什么是 URL 重写?就是用户如果在浏览器中禁用了 cookie,那么 sessionid 自然也用不了了,所以有的服务端就支持把 sessionid 放在请求地址中:

1
http://www.taobao.com;jsessionid=xxxxxx

如果服务端支持这种 URL 重写,那么对于攻击者来说,按照上面的攻击流程,构造一个这种地址简直太简单不过了。

不过这种请求地址大家在 Spring Security 中应该很少见到(原因请见下文),但是在 Shiro 中可能多多少少有见过。

3.如何防御

这个问题的根源在 sessionid 不变,如果用户在未登录时拿到的是一个 sessionid,登录之后服务端给用户重新换一个 sessionid,就可以防止会话固定攻击了。

如果你使用了 Spring Security ,其实是不用担心这个问题的,因为 Spring Security 中默认已经做了防御工作了。

Spring Security 中的防御主要体现在三个方面:

首先就是上篇文章讲的 StrictHttpFirewall,请求地址中有 ; 请求会被直接拒绝。

另一方面就是响应的 Set-Cookie 字段中有 HttpOnly 属性,这种方式避免了通过 XSS 攻击来获取 Cookie 中的会话信息进而达成会话固定攻击。

第三点则是让 sessionid 变一下。既然问题是由于 sessionid 不变导致的,那我就让 sessionid 变一下。

具体配置如下:

可以看到,在这里,我们有四个选项:

  1. migrateSession 表示在登录成功之后,创建一个新的会话,然后讲旧的 session 中的信息复制到新的 session 中,默认即此
  2. none 表示不做任何事情,继续使用旧的 session。
  3. changeSessionId 表示 session 不变,但是会修改 sessionid,这实际上用到了 Servlet 容器提供的防御会话固定攻击。
  4. newSession 表示登录后创建一个新的 session。

默认的 migrateSession ,在用户匿名访问的时候是一个 sessionid,当用户成功登录之后,又是另外一个 sessionid,这样就可以有效避免会话固定攻击。

这三种方案,可以让我们有效避免会话固定攻击!

4.小结

说了这么多,大家发现,如果你使用了 Spring Security,其实你什么都不用做,Spring Security 已经帮我们做好了会话固定攻击的防御工作,Spring Security 之强大,可见一斑。是不是非常 nice!

好啦,今天就和大家聊这样一个简单的话题,如果小伙伴们觉得有收获,记得点个在看鼓励下松哥哦~

喜欢这篇文章吗?扫码关注公众号【江南一点雨】【江南一点雨】专注于 SPRING BOOT+微服务以及前后端分离技术,每天推送原创技术干货,关注后回复 JAVA,领取松哥为你精心准备的 JAVA 干货!

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×