HttpSession

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

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

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

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

会话固定攻击

正常来说,只要你不关闭浏览器,并且服务端的 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 中可能多多少少有见过。

如何防御

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

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

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

  1. 就是 StrictHttpFirewall,请求地址中有 ; 请求会被直接拒绝。
  2. 就是响应的 Set-Cookie 字段中有 HttpOnly 属性,这种方式避免了通过 XSS 攻击来获取 Cookie 中的会话信息进而达成会话固定攻击。
  3. 则是让 sessionid 变一下。既然问题是由于 sessionid 不变导致的,那我就让 sessionid 变一下

配置

  1. @Override
  2. protected void configure(HttpSecurity http) throws Exception {
  3. http.authorizeRequests()
  4. .anyRequest()
  5. .authenticated()
  6. .and() //所有请求都需要认证才能访问
  7. .formLogin()
  8. .permitAll()
  9. .and()
  10. .sessionManagement()
  11. .sessionFixation()
  12. .migrateSession()//配置防御会话固定攻击
  13. ;
  14. }

sessionFixation后面有四个方法
image.png

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

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