Apache Shiro 基础与 RememberMe 机制

发布时间:2026/8/2 14:27:18

Apache Shiro 基础与 RememberMe 机制 Apache Shiro 基础与 RememberMe 机制写在前面前面反序列化系列我们讲了 CC 链执行体和 RMI投递通道。这篇开个新坑–Apache Shiro。Shiro 是 Java 里和 Spring Security 并列的安全框架在国内项目里铺得很广。它有两个特别经典的漏洞方向RememberMe 反序列化Shiro-550 / Shiro-721Shiro 的记住我功能把用户身份序列化后塞进 Cookie反序列化时就是readObject入口–和 RMI 一样是反序列化的投递通道。权限绕过CVE-2020-1957 等一连串Shiro 和 Spring 对 URL 路径的解析不一致导致鉴权被绕过。这篇是 Shiro 专栏的地基讲清 Shiro 框架、RememberMe 机制、以及为什么它会成为反序列化和权限绕过的温床。后面 550/721/权限绕过各篇都在这个基础上展开。前置Java 反序列化漏洞基础、CC 链系列。一、Shiro 是什么Apache Shiro 是一个 Java 安全框架提供四大功能Authentication认证验证你是谁登录。Authorization授权验证你能做什么权限。Cryptography加密提供加解密工具。Session Management会话管理自带会话机制不依赖 HttpSession可用于非 Web 环境。和 Spring Security 比Shiro 更轻量、API 更友好所以在国内中小项目里很常见。核心概念Subject当前操作者的抽象可以是用户、第三方服务等。所有安全操作都通过 Subject 发起比如subject.login(token)、subject.checkPermission(user:create)。SecurityManager核心调度器管理所有 Subject、Realm、Session。Shiro 的所有组件都由 SecurityManager 协调。Realm数据源桥梁。Shiro 自己不知道用户密码存在哪Realm 负责从 DB/配置/LDAP 读用户、角色、权限并完成实际的认证授权校验。应用通常自定义一个 Realm 继承AuthorizingRealm。SessionShiro 自带的会话Subject.getSession()不依赖 Servlet 容器的 HttpSession。二、Shiro 的认证流程一次典型的登录// 1. 封装用户名密码勾选 RememberMeUsernamePasswordTokentokennewUsernamePasswordToken(admin,123456);token.setRememberMe(true);// 2. Subject 登录SubjectcurrentUserSecurityUtils.getSubject();try{currentUser.login(token);// - SecurityManager - Realm 验证// 登录成功}catch(AuthenticationExceptione){// 登录失败}subject.login(token)背后SecurityManager 把 token 交给配置的 RealmRealm 的doGetAuthenticationInfo从数据源查用户、比对密码返回AuthenticationInfo含 Principal 用户身份。成功则 Subject 认证通过。三、RememberMe 机制重点用户勾选记住我登录成功后Shiro 把用户身份记住在 Cookie 里下次访问不用重新登录。这是 Shiro-550 和 Shiro-721 的共同根源。3.1 登录时身份写进 Cookie登录成功token.isRememberMe() true │ ▼ 1. 取 Principal用户身份对象 2. 序列化 Principal // Java 序列化 - byte[] 3. AES 加密 // 默认 CBC 模式 4. Base64 编码 // 转成 Cookie 安全的文本 5. 写入 rememberMe Cookie3.2 下次请求从 Cookie 恢复身份请求带 rememberMe Cookie │ ▼ 1. 读 Cookie 值 2. Base64 解码 3. AES 解密 4. ★ 反序列化恢复 Principal // ObjectInputStream.readObject() - 攻击点 5. 用 Principal 恢复身份免登录第 4 步反序列化恢复 Principal就是readObject入口。Cookie 内容攻击者可控只要能控制 Cookie 里的密文就能让 Shiro 反序列化任意对象。四、RememberMe 的反序列化攻击面把 RememberMe 流程和反序列化两步法对一下入口类Shiro 的CookieRememberMeManager读 Cookie 后readObject还原 Principal。执行体CC 链或其他 gadget 链。攻击者把 gadget payload 加密塞进 CookieShiro 解密后readObject触发。Shiro 在这里只是投递通道和 RMI 同类角色真正干活的还是 gadget 链。两个漏洞的差别在怎么控制 Cookie 密文Shiro-550CVE-2016-4437密钥泄露影响Shiro ≤ 1.2.4根因AES 密钥硬编码在源码里默认kPHbIxk5D2deZiIxcaaaA。利用攻击者知道密钥直接用它加密 CC 链 payload放进 Cookie。Shiro 解密成功 - 反序列化 - RCE。门槛低密钥是公开的加密一段 payload 就行。Shiro-721CVE-2019-12422Padding Oracle影响Shiro 1.2.5 ~ 1.4.1根因1.2.5 起密钥随机化了但 RememberMe 仍用AES-CBC模式CBC 存在 Padding Oracle 漏洞。利用攻击者先登录一个合法账号拿到一个合法rememberMeCookie再用 Padding Oracle 攻击逐字节篡改密文构造出含反序列化 payload 的任意密文–全程不需要密钥。门槛高需要有效账号 Padding Oracle 攻击慢通常数千次请求。两者最终都落在反序列化 Principal这一步区别只是密文怎么来。一个 Shiro 特有的坑gadget 链选择Shiro 反序列化有几个和 RMI 不同的地方Shiro 默认 classpath 不一定有 commons-collections但自带 commons-beanutils。所以实战中 Shiro-550 常用CommonsBeanutils1CB1链而非 CC 链–CB1 用BeanComparatorTemplatesImpl依赖 Shiro 自带的 commons-beanutils更通用。Shiro 用自己的ClassResolvingObjectInputStream对类加载较严格某些 CC 链可能跑不通。Cookie 有长度限制。CB1 等链的 payload 较大可能超 Cookie 限制被截断需要想办法缩短 payload或分块。这些细节在 Shiro-550 篇展开。五、Shiro 权限校验与路径绕过预告RememberMe 是反序列化方向另一大方向是权限绕过。Shiro 在 Web 环境里通过ShiroFilterFactoryBean配置URL 过滤器链把 URL 模式映射到过滤器anon匿名可访问、authc需登录、perms需某权限等filterChainDefinitionMap.put(/login,anon);filterChainDefinitionMap.put(/admin/**,authc, perms[admin:*]);filterChainDefinitionMap.put(/**,authc);// 其他都需登录请求进来时Shiro 拿请求 URL 去匹配过滤器链做鉴权。问题在于Shiro 对 URL 路径的解析和 Spring MVC / Servlet 容器不一致。比如Shiro 可能不处理;Spring 的矩阵变量分隔符导致/admin/..;/login在 Shiro 看是/loginanon在 Spring 看是/admin/..受保护。URL 编码、双重编码、/规范化的处理差异也能让 Shiro 和 Spring 对同一个 URL 产生不同理解。攻击者利用这种不一致构造一个 Shiro 以为无需鉴权、但 Spring 实际路由到受保护资源的 URL从而绕过权限。这是 CVE-2020-1957 等一连串漏洞的共同根。综述篇会统一讲透。六、小结这篇是 Shiro 专栏的地基几个核心认知Shiro 是 Java 安全框架核心是 Subject / SecurityManager / Realm提供认证、授权、加密、会话。RememberMe 是反序列化的根身份序列化 AES Base64 存 Cookie恢复时readObject还原 Principal 反序列化入口。Shiro-550 vs Shiro-721都是 RememberMe 反序列化区别在密文来源–550 密钥泄露直接加密721 Padding Oracle合法 Cookie 篡改。权限绕过的根Shiro 与 Spring 的 URL 路径解析不一致。Shiro 是通道gadget 链是执行体和 RMI 一样的定位但 Shiro 默认带 commons-beanutils常用 CB1 链。接下来三篇Shiro-550 复现、Shiro-721 复现、权限绕过综述。参考Apache Shiro 官方文档https://shiro.apache.org/

相关新闻