若依(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=1UNION 等),但如果开发者在自定义接口时:

  • 关闭 / 禁用了该过滤器;
  • 新增接口未纳入过滤器的拦截范围;
  • 绕过了参数校验逻辑。
3. 动态表名 / 字段名使用不当

若依中有时需要动态指定表名或字段名(如多租户、动态查询场景),如果直接用${}拼接且未做严格校验,也会引入风险。

4. 未使用框架封装的 CRUD 方法

若依的BaseMapperServiceImpl封装了大量安全的 CRUD 方法(如selectByIdlist(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 日志打印,避免敏感信息泄露。

三、若依框架自带的安全防护机制

除了上述开发者需要注意的点,若依本身已内置以下防护:

  1. XssFilter:过滤 XSS 攻击的同时,也能拦截部分 SQL 注入关键字;
  2. 参数绑定:框架封装的 CRUD 方法均使用#{}参数绑定;
  3. 分页插件:PageHelper的分页参数自动处理,避免拼接风险;
  4. 异常处理:统一的异常拦截,不会暴露原生 SQL 错误信息。

总结

  1. 核心防护:杜绝手动拼接 SQL,优先使用#{}参数绑定和若依封装的QueryWrapper/BaseMapper方法;
  2. 辅助防护:启用若依的SQLFilter过滤器,对动态表名 / 字段名做白名单校验;
  3. 开发规范:二次开发时遵循若依的编码规范,不随意禁用内置安全组件,对入参做严格校验。

遵循以上原则,就能最大程度规避若依框架下的 SQL 注入风险,保障系统的数据库安全。

若依最新前后端分离源码下载:

https://gitee.com/ruoyieleadmin/ruoyi-ele-admin

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐