购物类网站建设方案,用自己网站做邮箱域名,wordpress上传中文文件,广告牌制作报价单明细引言
承接上一篇《Web应用安全————账号冻结与 Session 实时失效》关于 session 的学习#xff0c;本篇博客聚焦如何通过 shiro 解决会话固定导致的漏洞问题。
首先#xff0c;没怎么接触过应用安全方面的小伙伴可能会发起疑问 - 什么是会话固定#xff1f;
简单来说本篇博客聚焦如何通过 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 解决会话固定问题的全过程欢迎大家文末留言。