若依(RuoYi)SQL 注入漏洞排查与安全加固指南
若依(RuoYi)框架中存在的 SQL 注入风险,以及对应的防范方法和最佳实践,这是保障基于若依开发的系统安全的关键问题。
若依最新前后端分离源码下载:
https://gitee.com/ruoyieleadmin/ruoyi-ele-admin
一、若依框架中 SQL 注入的主要风险点
若依框架本身已经内置了很多安全防护措施,但在二次开发过程中,如果开发者不遵循规范,就容易引入 SQL 注入风险,主要集中在以下几个场景:
1. 自定义 SQL 拼接(最常见)
若依提供了基于 MyBatis 的 Mapper 层开发方式,很多开发者为了实现复杂查询,会手动拼接 SQL 语句,而不是使用 MyBatis 的参数绑定。风险示例:
java
运行
// 错误写法:直接拼接参数,存在SQL注入
@Select("SELECT * FROM sys_user WHERE username = '" + username + "' AND status = 1")
List<SysUser> selectUserByUsername(String username);
// 或在XML中拼接(错误示例)
<select id="selectUser" resultType="SysUser">
SELECT * FROM sys_user
WHERE 1=1
<if test="username != null">
AND username = '${username}' <!-- 使用${}直接拼接,风险极高 -->
</if>
</select>
2. 忽略若依内置的 SQL 过滤组件
若依框架自带了SQLFilter拦截器,用于过滤常见的 SQL 注入关键字(如 OR 1=1、UNION 等),但如果开发者在自定义接口时:
- 关闭 / 禁用了该过滤器;
- 新增接口未纳入过滤器的拦截范围;
- 绕过了参数校验逻辑。
3. 动态表名 / 字段名使用不当
若依中有时需要动态指定表名或字段名(如多租户、动态查询场景),如果直接用${}拼接且未做严格校验,也会引入风险。
4. 未使用框架封装的 CRUD 方法
若依的BaseMapper、ServiceImpl封装了大量安全的 CRUD 方法(如selectById、list(Wrappers)),如果弃用这些方法,自行写原生 SQL 且不规范,就会暴露风险。
二、若依框架下防范 SQL 注入的最佳实践
1. 核心原则:使用参数绑定,拒绝手动拼接
MyBatis 中#{}会自动做参数转义,而${}是直接字符串替换,这是防范 SQL 注入的核心:正确写法:
java
运行
// 注解方式(推荐)
@Select("SELECT * FROM sys_user WHERE username = #{username} AND status = 1")
List<SysUser> selectUserByUsername(@Param("username") String username);
// XML方式(推荐)
<select id="selectUser" resultType="SysUser">
SELECT * FROM sys_user
WHERE 1=1
<if test="username != null">
AND username = #{username} <!-- 使用#{},自动转义 -->
</if>
</select>
2. 启用并维护若依的 SQL 过滤
若依的SQLFilter位于com.ruoyi.framework.web.filter包下,确保:
- 过滤器已在配置类中注册(默认已注册);
- 自定义接口添加
@RepeatSubmit/@PreAuthorize等注解,纳入拦截范围; - 如需调整过滤规则,修改
SQLFilter中的sqlInject方法,补充拦截关键字。
核心过滤逻辑示例:
java
运行
public class SQLFilter {
// SQL注入关键字过滤
private static String[] keywords = {"or", "and", "union", "exec", "insert", "update", "delete", "select", "count", "drop"};
public static void sqlInject(String str) {
if (StringUtils.isEmpty(str)) {
return;
}
// 统一转为小写
str = str.toLowerCase();
for (String keyword : keywords) {
if (str.contains(keyword)) {
throw new RuntimeException("参数包含非法SQL关键字:" + keyword);
}
}
}
}
3. 动态表名 / 字段名的安全处理
如果必须使用${}拼接表名 / 字段名,需做严格的白名单校验:
xml
<select id="selectByDynamicTable" resultType="SysUser">
SELECT * FROM ${tableName} WHERE id = #{id}
</select>
后端校验逻辑:
java
运行
// 白名单:仅允许指定的表名
private static Set<String> ALLOW_TABLES = new HashSet<>(Arrays.asList("sys_user", "sys_role", "sys_dept"));
public List<SysUser> selectByDynamicTable(String tableName, Long id) {
// 校验表名是否在白名单中
if (!ALLOW_TABLES.contains(tableName)) {
throw new RuntimeException("非法的表名:" + tableName);
}
return userMapper.selectByDynamicTable(tableName, id);
}
4. 优先使用若依封装的安全方法
- 单表查询:使用
baseMapper.selectList(Wrappers.lambdaQuery()); - 条件查询:使用
QueryWrapper/LambdaQueryWrapper(自动参数绑定);
java
运行
// 推荐:使用LambdaQueryWrapper,无SQL注入风险
LambdaQueryWrapper<SysUser> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(SysUser::getUsername, username)
.eq(SysUser::getStatus, 1);
List<SysUser> userList = userService.list(queryWrapper);
5. 额外防护:参数校验 + 权限控制
- 对入参做长度、格式校验(如用户名仅允许字母 / 数字);
- 结合若依的权限框架(Shiro/Spring Security),限制接口访问权限;
- 生产环境关闭 MyBatis 的 SQL 日志打印,避免敏感信息泄露。
三、若依框架自带的安全防护机制
除了上述开发者需要注意的点,若依本身已内置以下防护:
XssFilter:过滤 XSS 攻击的同时,也能拦截部分 SQL 注入关键字;- 参数绑定:框架封装的 CRUD 方法均使用
#{}参数绑定; - 分页插件:
PageHelper的分页参数自动处理,避免拼接风险; - 异常处理:统一的异常拦截,不会暴露原生 SQL 错误信息。
总结
- 核心防护:杜绝手动拼接 SQL,优先使用
#{}参数绑定和若依封装的QueryWrapper/BaseMapper方法; - 辅助防护:启用若依的
SQLFilter过滤器,对动态表名 / 字段名做白名单校验; - 开发规范:二次开发时遵循若依的编码规范,不随意禁用内置安全组件,对入参做严格校验。
遵循以上原则,就能最大程度规避若依框架下的 SQL 注入风险,保障系统的数据库安全。
若依最新前后端分离源码下载:
更多推荐



所有评论(0)