Vulnerability call chain
1.1 Summary
RuoYi has a missing authorization vulnerability: Unauthorized Role Assignment Deletion. 越权撤销目标用户角色,导致权限被移除。
- Attack precondition: 拥有
system:role:edit。 - Affected authorization property: ``sys_user_role.user_id
,sys_user_role.role_id。 - Security impact: 越权撤销目标用户角色,导致权限被移除。
1.2 Exploit path
POST /system/role/authUser/cancel 或 /system/role/authUser/cancelAll,提交任意 roleId/userId(s)。
1.3 Key code evidence
ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java
Evidence location: https://github.com/yangzongzhuan/RuoYi/blob/master/ruoyi-admin/src/main/java/com/ruoyi/web/controller/system/SysRoleController.java#L262
259 @Log(title = "角色管理", businessType = BusinessType.GRANT)260 @PostMapping("/authUser/cancel")261 @ResponseBody262 public AjaxResult cancelAuthUser(SysUserRole userRole)263 {264 return toAjax(roleService.deleteAuthUser(userRole));265 }266 267 /**268 * 批量取消授权269 */270 @RequiresPermissions("system:role:edit")271 @Log(title = "角色管理", businessType = BusinessType.GRANT)272 @PostMapping("/authUser/cancelAll")273 @ResponseBody274 public AjaxResult cancelAuthUserAll(Long roleId, String userIds)275 {276 return toAjax(roleService.deleteAuthUsers(roleId, userIds));277 }278 279 /**280 * 选择用户281 */282 @RequiresPermissions("system:role:list")283 @GetMapping("/authUser/selectUser/{roleId}")
ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java
Evidence location: https://github.com/yangzongzhuan/RuoYi/blob/master/ruoyi-system/src/main/java/com/ruoyi/system/service/impl/SysRoleServiceImpl.java#L377
374 {375 return roleMapper.updateRole(role);376 }377 378 /**379 * 取消授权用户角色380 * 381 * @param userRole 用户和角色关联信息382 * @return 结果383 */384 @Override385 public int deleteAuthUser(SysUserRole userRole)386 {387 return userRoleMapper.deleteUserRoleInfo(userRole);388 }389 390 /**391 * 批量取消授权用户角色392 * 393 * @param roleId 角色ID394 * @param userIds 需要删除的用户数据ID395 * @return 结果
ruoyi-system/target/classes/mapper/system/SysUserRoleMapper.xml
Evidence location: https://github.com/yangzongzhuan/RuoYi/blob/master/ruoyi-system/target/classes/mapper/system/SysUserRoleMapper.xml#L38
35 </foreach>36 </insert>37 38 <delete id="deleteUserRoleInfo" parameterType="SysUserRole">39 delete from sys_user_role where user_id=#{userId} and role_id=#{roleId}40 </delete>41 42 <delete id="deleteUserRoleInfos">43 delete from sys_user_role where role_id=#{roleId} and user_id in44 <foreach collection="userIds" item="userId" open="(" separator="," close=")">45 #{userId}46 </foreach> 47 </delete>48 </mapper>
roleService.c
Evidence location: https://github.com/yangzongzhuan/RuoYi/blob/master/roleService.c
5. userService.c
Evidence location: https://github.com/yangzongzhuan/RuoYi/blob/master/userService.c
3. Root Cause Analysis
Root Cause 1: Missing server-side authorization on the vulnerable operation.
The endpoint accepts user-controlled authorization-sensitive identifiers or fields, but the write/read path does not prove that the current caller may operate on the target object.
Root Cause 2: Missing object-scope or grant-bound validation.
The implementation relies on endpoint access, UI filtering, or object existence checks instead of enforcing target ownership, tenant boundary, role ceiling, or grantable-resource constraints at the service layer.
4. Recommended fix
删除前调用 roleService.checkRoleDataScope(roleId) 和每个 userService.checkUserDataScope(userId);操作后清理授权缓存。
5. Verification after fix
- Unauthorized callers receive HTTP 403 or equivalent rejection.
- Out-of-scope target identifiers are rejected before database writes or sensitive reads.
- Role, permission, tenant, organization, ownership, or grant-bound ceilings are enforced server-side.
- Direct HTTP requests are rejected even when front-end controls are hidden.
