RuoYi issue4: Unauthorized Role Assignment Deletion
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 @ResponseBody
262 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 @ResponseBody
274 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 @Override
385 public int deleteAuthUser(SysUserRole userRole)
386 {
387 return userRoleMapper.deleteUserRoleInfo(userRole);
388 }
389
390 /**
391 * 批量取消授权用户角色
392 *
393 * @param roleId 角色ID
394 * @param userIds 需要删除的用户数据ID
395 * @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 in
44 <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.

浙公网安备 33010602011771号