Web应用安全————Shiro 解决会话固定漏洞

引言

承接上一篇《Web应用安全————账号冻结与 Session 实时失效》关于 session 的学习,本篇博客聚焦如何通过 shiro 解决会话固定导致的漏洞问题。

首先,没怎么接触过应用安全方面的小伙伴可能会发起疑问 - 什么是会话固定?

简单来说,系统在登录前和登录后使用同一个 session id,就是会话固定,默认的session 管理机制都是会话固定的。会话固定可能会造成“会话固定攻击”(Session Fixation Attack)。这种攻击形式简单来说,有几种:

1、黑客先访问一个目标网站,在未登录的情况下,获得了一个 session id,然后将这个 session id 以钓鱼邮件的形式发给某个该网站的用户,用户缺乏信息安全意识,点击邮件中带有 session id 的连接并登录成功后,根据“会话固定”的登录前和登录后 session id 保持不变的原理,黑客可以在一定时间内使用 这个 session id 绕过登录验证,操作该用户的个人账户。

2、黑客通过某种形式的网络抓包,在某个用户登录时,劫持到了 session id,待该用户登录成功后,根据“会话固定”的原理,黑客可以在一定时间内通过这个 session id 随意操作该用户的个人账户。

一、会话固定攻击演示

固定会话攻击实际上就是黑客通过某种方式获得了正常用户登录时的 session id,然后利用用户的登录操作,使 session id 生效,并绕过登录验证。由于在登录前,session id 会通过 cookie 保存到浏览器中,因此很容易被黑客窃取。

1、首先,普通用户打开登录页面,会获得一个由服务器响应携带而来的 session id cookie:

在浏览器的控制台,我们可以轻而易举的得到这个 session id,此时,由于还未登录,session id 是无法正常使用的,黑客通过某种方式,比如脚本,或者桌面监控等,获取到了这个 session id ,放入自己准备登录的工具中。这里我使用post man 来演示黑客端的登录操作。

2、黑客输入目标网站的首页地址,这时,应用会拦截请求并弹出登录页面,此时黑客同样会收到服务器返回的 session id,不过他不会使用这个 session id,而是将窃取来的 session id 拷贝到 cookie 中,等待用户的登录操作

3、紧接着,用户不知道自己的 session id 已经被窃取,依然执行了登录操作,并成功登录:

4、此时,黑客使用这个已经完成登录操作的 session id 再次执行首页的访问:

可以看到上面的 GIF,黑客仅仅是使用了一个 session id 就完成了对用户首页的访问,轻而易举地绕过了登录验证。如果这个用户是超级管理员,那么就可能对系统造成不小的伤害。

因此,为了解决这个会话固定的问题,必须在登录前和登录后刷新 session id ,并使登录前生成 的session 失效,这样,才不会让黑客利用会话固定绕过登录验证。

二、Shiro 防止会话固定

如果说上一篇文章可以是框架无关的操作,那么解决会话固定,就完全依赖于安全框架自身的一些封装方式。

在Shiro 中, session 的生成和保存是这样的:

1、shiro 检测到用户的请求,并生成 session,保存到缓存中 ,并将 session id 绑定在 response 的 cookie 中,响应回浏览器 。

2、用户携带 session id,完成登录操作后,服务器端就会更新 session 的状态,并将其 绑定到 Subject 中。

在登录前的 session 生成时,session 还不知道它要绑定到哪个用户上,仅仅是生成了一个标记了主机地址的SimpleSession 对象。当用户调用 shiro 的登录验证方法 login()的时候,完成正常的验证操作,Shiro 就会将这个 session 绑定到当前用户 Subject 中。

public void login(AuthenticationToken token) throws AuthenticationException {Subject subject = securityManager.login(this, token);......Session session = subject.getSession(false);if (session != null) {this.session = decorate(session);} else {this.session = null;}
}

因此,我的解决思路就是,在完成 login 方法后,注销掉当前的 session ,并重新获得一个 新的session ,绑定到 Subject 中。

/**
* 点击登录执行的动作
*/
@RequestMapping(value = "/login", method = RequestMethod.POST)
public String loginVali() {// ...... 从request 中获取 username 和 passwordSubject currentUser = ShiroKit.getSubject();UsernamePasswordToken token = new UsernamePasswordToken(username, password.toCharArray());......currentUser.login(token);// 创建新的session 会话,并管理 shiro session idsessionProcessor.shiroSessionIdIntoPool();// Session 重置后,重新调用login()方法,其内部自动捆绑新的 session 到当前SubjectcurrentUser.login(token);return REDIRECT + "/";
}

这里我单独提取了一个方法,来完成这一操作: 

注意,getSession() 方法需要我们传入一个参数,默认是 true,意思是,如果这个用户还没有session ,就为其创建一个新的 session,如果是false,那么就不为其创建。上图的红框内,我们首先获得了当前登录用户的 session 对象,并调用了 stop 方法,将其注销,再为用户生成 一个 新的 session 对象。其他的部分,主要是为了维护一个 session id 池,将用户名与 session id的对应关系保存在 一个 map 中,方便管理员用户找到其他用户的 session id,并执行相关操作。

注意,在生成新的 session 后,必须重复调用一下 login 方法,将其再次绑定到当前用户中

三、测试结果演示

用户打开登录界面,依然会获得一个服务端返回的 session id :

黑客窃取到这个 session id ,并拷贝到自己的登陆工具中(演示使用的是Post Man):

用户输入用户名和密码,登录到首页后,可以看到,虽然地址栏中使用了旧的 session id 完成登录操作,但是cookie 中的已经变成了新的 session id ,并为接下来的请求提供了新的 session :

此时,如果黑客使用旧的 session id 再次执行登录,就无法成功(请对比修改前的效果):

由此可见,我们在代码中刷新了 session 的操作已经成功。

总结

解决会话固定实际上就是在登录后,通过代码实现 session 的刷新(重新生成),保证登录前后使用不同的 session id 即可。虽然新的 session id 依然有可能被黑客窃取,但相对于固定的 session id ,也算在一定程度上提升了系统的安全性

在解决会话固定的时候,我考虑了很多问题。

最初并不了解会话固定需要解决的最关键问题是什么,误以为只需要将用户过去的 session 注销即可,实则不然。导致这个想法的原因是由于我发现,当用户没有安全退出,而是直接关闭浏览器后,服务器端的 session 无法主动注销,需要等待到超时时间后,再执行注销操作。因此在 session 的处理方法中,我在用户 登录之后,先根据用户名 找到他上次没有注销,且未超时的 session ,并将其注销。但这还不是“会话固定”。

后来我了解到了会话固定实际上是登录前后拥有相同的 session id ,才意识到,注销以前使用过的 session 并没有真正解决了 session 会话固定的问题。

于是我开始思考在 Shiro 中如何创建新的 session,我本以为需要继承 DefaultSessionManager 或其子类并重写doCreateSession(SessionContext context) 方法,然后代替DefaultWebSessionManager 装载到 SecurityManager 中,但我发现 SessionContext 好像很难从请求中 手动构造出来(需要重写好几层继承关系的逻辑链),把整个 session 的创建过程和保存过程梳理了好几遍,都没找到很好的切入点,最后,还是通过 Subject 的 getSession 方法 发现了可执行的方案。

注销掉原来的 session 并通过 getSession(true) 方法成功刷新了 session 之后,我又发现,在重定向的时候,会导致系统找不到用户请求中的 session id,也就是说,我新生成的 session ,用户并没有成功接收到它的 session id。于是我又开始思考如何将 session id 放入 cookie 中返回给浏览器。但实际上,并不是如此,在生成session 的时候,SessionManager就已经将 session id 放入到了 response 的 cookie 中

其实是用户和新生成的 session 并没有形成绑定,于是我又开始研究 Shiro 的 login()方法究竟做了哪些工作,这才发现,login 实际上在验证完成用户之后,会把已经生成的 session 绑定 到当前 Subject 上,于是,这才有了两次调用 login() 的操作。

	currentUser.login(token);// 创建新的session 会话,并管理 shiro session idsessionProcessor.shiroSessionIdIntoPool();// Session 重置后,重新调用login()方法,其内部自动捆绑新的 session 到当前SubjectcurrentUser.login(token);

虽然实际修改的代码就只有两三行,但不得不说,会话固定的修改过程真是充满了挑战,前后一刻不停整整花了2大天时间。不过,通过自己的思考和努力解决一个问题真的非常有成就感。

综上,就是通过 Shiro 解决会话固定问题的全过程,欢迎大家文末留言。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/558745.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Web应用安全————多点登录互斥

引言 在实际生活中,很多网站都做了多点登录互斥的操作,简单来说就是同一个账号,只能在一台电脑上登录,如果有人在其他地方登录,那么原来登录的地方就会自动下线,再进行操作就会弹出登录界面。 实现思路 …

Linux进阶之路————磁盘查询

引言 承接《Linux进阶之路————Linux磁盘分区与挂载》,本文介绍实际生产中对于磁盘的监控和查询。 一、查询磁盘整体使用情况 基本语法: df -h 该命令会显示包括我们手动挂载的磁盘,如果使用 umount 卸载磁盘,那么将不会显示…

Linux进阶之路————CentOS网络配置

引言 Linux在装机后,如果没有特殊配置,会使用动态获取 IP 地址的策略。本文描述了,虚拟机使用网络的拓扑图,以及如何通过配置,将 IP 地址固定下来,不会因为重启而失效。同时可以访问外网地址。 一、NAT模…

Linux进阶之路————进程与服务管理

引言 在Linux 中,每个执行的程序(代码)都成为一个进程,Linux 为每一个进程分配了一个唯一的 id 号 - PID。 每个进程都会对应一个父进程,而这个父进程可以复制多个子进程,例如 www 服务器。 每个进程都可…

Linux进阶之路———— RPM 与 YUM 包管理

引言 rpm 是一种用于互联网下载的打包及安装工具,它包含在某些 Linux 发行版中,生成具有 .rpm 扩展名的文件。rpm 是 redhat package manager(RedHat 软件包管理器)的缩写,类似 Windows 下的 setup.exe 文件。这一文件…

Linux进阶之路———Shell 编程入门

引言 通过 Shell 编程的学习,铺平架构师道路上的一块大砖。 Shell 在Linux 系统中的定位如下所示: 一、第一个 Shell 脚本 我们通过一个简单的 Shell 脚本来感受一下。 在 Shell 中不需要加 “;” 结尾,通过 vim 可以进行 shell 的编程工…

Linux 实操———CentOS 6 安装配置 Oracle JDK 1.8

引言 本篇博客也属于Linux进阶系列,主要讲解如何在CentOS 6 下安装并配置 JDK 8。由于通过 yum 搜索的结果都是 openjdk,而目前企业中还是以 Oracle jdk 为主,因此,操作步骤这样的。 在Oracle 官网把 jdk 1.8 下载下来&#xff…

Linux 实操———CentOS 6 安装配置 Tomcat

引言 Linux下安装Tomcat。 一、下载、传输与解压 同《Linux 实操———CentOS 6 安装配置 Oracle JDK 1.8》一样,前期都是先在远程机上下载压缩包,然后通过远程终端,将压缩包放在 Linux 的 opt 目录下,然后解压。 下载地址是T…

Spring Boot 实用开发技巧————Eclipse 远程调试

引言 在之前的开发当中,都会进行本地项目启动,然后向本地服务发起请求来进行 Debug 调试代码,这也是开发人员最常见的调试操作。但是当项目逐渐成型,慢慢的将各个模块部署到服务器后,调试的手段可能就仅仅剩下查看执行…

Linux 实操———— Shell 远程执行命令

引言 目前,开发人员的部署方式是,将项目打包(Maven 打包) 然后将 生成的 jar 包等文件,通过Xshell 等终端工具手动传输到远程服务器上,然后再通过在终端执行远程服务器上的 shell 脚本来启动服务。 本篇博客聚焦这样一种解决方案…

Spring Boot 设置 ASCII banner 艺术字

引言 无意中看到Spring boot 项目的 resources 目录下有一个 banner.txt ,打开一看,居然是ASCII 字符画。于是兴起,简单研究了一下。 Spring boot 可以加载 resources 目录下的 banner.txt 文件,将字符画在启动之初输出到日志或…

MySQL 基础 ———— 分组查询

引言 承接上一篇《MySQL 基础 ————高频函数总结》,本篇单独针对分组查询进行简单的总结和归纳,并为后续更为复杂的DQL 语句做好铺垫。 查询语句: SELECT AVG(salary) FROM teacher; 实际上是以全表的 salary 字段来求平均值。但是在实…

MySQL 基础 ———— 连接查询

引言 本篇文章承接《数据库与SQL语句》专栏,进入DQL的重要环节,可以说,这一部分的内容应该占据SQL语言的大部分使用场景。 本篇的连接查询知识,和后面的一些重要的查询知识总结,共同构成了在工作中80%的MySQL应用场景…

MySQL 基础 ———— 子查询

引言 承接《MySQL 基础 ———— 连接查询》,本文介绍和展示SQL中子查询的使用。 子查询是出现在其他语句中的select 语句,也称为内查询。外部的查询语句,称为主查询或外查询。 一、子查询的分类和支持的子句 按照子查询出现的位置&#…

MySQL 基础 ———— SQL语句的执行顺序与 LIMIT 子句

引言 到目前为止,已经总结了常见的SQL子句,包括 SELECT 、FROM、JOIN ... ON、WHERE、GROUP BY、HAVING、ORDER BY。 虽然SQL的书写顺序是固定的,但在MySQL引擎中执行的顺序并不完全和书写顺序一致。除了上述这些子句,下面将会介…

MySQL 基础———— UNION 联合查询

引言 联合查询与连接查询不同,通过UNION 关键字,我们可以将多个查询语句一同执行并将结果集展示出来,不涉及到任何关联关系。 UNION 的含义是“联合,并集,结合”,在MySQL中可以将多个查询语句的结果合并成…

MySQL 基础————常用数据类型

引言 从第一次学习mysql开始,不知道为什么MySQL的数据类型始终没有像Java 一样深入脑海,对某些数据类型的定义和用法,也并不清晰,这篇文章,就好好总结一番,将MySQL中几个常用的数据类型归纳一下。 一、类…

MySQL 基础 ———— SAVEPOINT 的应用

引言 savepoint 关键字用于在数据库事务中设置一个存储点,在一个较长的事务中暂存数据,如果在事务末尾执行回滚,可选择性的回滚到 savepoint 设置的暂存点。 本文承接上一篇博客《MySQL 基础 ————事务与隔离级别总结》,进一…

MySQL 基础 ———— 视图的应用与总结

引言 视图是一种虚拟表,和普通表的使用是一样的,视图的一大特点就是“临时性”,是通过表动态生成的数据,只保存SQL逻辑,不保存查询结果。 视图在实际生产中主要有两种应用场景: 1、多个地方用到同样的查…

MySQL 基础 ———— 变量

一、MySQL系统变量 系统变量是由系统提供,属于服务器层面。 系统变量分为:全局变量和会话变量。 全局变量一般要加 GLOBAL 关键字,例如在《MySQL 基础 ————事务与隔离级别总结》中提到的 GLOBAL TRANSACTION,就属于全局变量…