前两天和大家聊了 Spring Security 中的 session 并发问题,和小伙伴们聊了如何像 QQ 一样,用户在一台设备上登录成功之后,就会自动踢掉另一台设备上的登录。
当然,Spring Security 中,关于 session 的功能不仅仅是这些,之前和大家说 我们学习 Spring Security,也是学习各种各样的网络攻击与防御策略 ,今天松哥就来和大家聊一个简单的:什么是会话固定攻击以及 Spring Security 中如何防止会话固定攻击。
本文是 Spring Security 系列的第 16 篇,阅读本系列前面的文章有助于更好的理解本文:
看前面文章的评论,我发现有的小伙伴对 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 有一个大致的了解,接下来我们再来看会话固定攻击。
什么是会话固定攻击?英文叫做 session fixation attack。
正常来说,只要你不关闭浏览器,并且服务端的 HttpSession 也没有过期,那么维系服务端和浏览器的 sessionid 是不会发生变化的,而会话固定攻击,则是利用这一机制,借助受害者用相同的会话 ID 获取认证和授权,然后利用该会话 ID 劫持受害者的会话以成功冒充受害者,造成会话固定攻击。
一般来说,会话固定攻击的流程是这样,以淘宝为例:
在这个过程中,如果淘宝网站支持 URL 重写,那么攻击还会变得更加容易。
什么是 URL 重写?就是用户如果在浏览器中禁用了 cookie,那么 sessionid 自然也用不了了,所以有的服务端就支持把 sessionid 放在请求地址中:
http://www.taobao.com;jsessionid=xxxxxx
如果服务端支持这种 URL 重写,那么对于攻击者来说,按照上面的攻击流程,构造一个这种地址简直太简单不过了。
不过这种请求地址大家在 Spring Security 中应该很少见到(原因请见下文),但是在 Shiro 中可能多多少少有见过。
这个问题的根源在 sessionid 不变,如果用户在未登录时拿到的是一个 sessionid,登录之后服务端给用户重新换一个 sessionid,就可以防止会话固定攻击了。
如果你使用了 Spring Security ,其实是不用担心这个问题的,因为 Spring Security 中默认已经做了防御工作了。
Spring Security 中的防御主要体现在三个方面:
首先就是 上篇文章讲的 StrictHttpFirewall ,请求地址中有 ;
请求会被直接拒绝。
另一方面就是响应的 Set-Cookie 字段中有 HttpOnly 属性,这种方式避免了通过 XSS 攻击来获取 Cookie 中的会话信息进而达成会话固定攻击。
第三点则是让 sessionid 变一下。既然问题是由于 sessionid 不变导致的,那我就让 sessionid 变一下。
具体配置如下:
可以看到,在这里,我们有四个选项:
默认的 migrateSession ,在用户匿名访问的时候是一个 sessionid,当用户成功登录之后,又是另外一个 sessionid,这样就可以有效避免会话固定攻击。
这三种方案,可以让我们有效避免会话固定攻击!
说了这么多,大家发现,如果你使用了 Spring Security,其实你什么都不用做,Spring Security 已经帮我们做好了会话固定攻击的防御工作,Spring Security 之强大,可见一斑。是不是非常 nice!
好啦,今天就和大家聊这样一个简单的话题,如果小伙伴们觉得有收获,记得点个在看鼓励下松哥哦~