RuoYi SQL注入:MyBatis防注入策略深度解析
引言:SQL注入的安全威胁
SQL注入(SQL Injection)是Web应用中最常见且危害极大的安全漏洞之一。攻击者通过在用户输入中注入恶意SQL代码,可以绕过应用程序的安全机制,直接操作数据库,导致数据泄漏、数据篡改甚至服务器被控制等严重后果。
在RuoYi这样基于SpringBoot的权限管理系统中,数据库安全尤为重要。本文将深入分析RuoYi框架中MyBatis的SQL注入防护策略,帮助开发者构建更加安全可靠的企业级应用。
MyBatis防注入机制解析
1. 参数化查询(Prepared Statements)
MyBatis通过预编译语句(Prepared Statements)机制有效防止SQL注入。当使用#{}占位符时,MyBatis会将参数安全地传递给数据库驱动程序,而不是直接拼接SQL字符串。
// 安全的参数化查询示例
@Select("SELECT * FROM sys_user WHERE user_name = #{userName}")
SysUser selectUserByLoginName(String userName);
2. 动态SQL的安全使用
MyBatis提供了强大的动态SQL功能,但需要正确使用以避免安全风险:
<!-- 安全的动态SQL示例 -->
<select id="selectUserList" parameterType="SysUser" resultMap="SysUserResult">
SELECT * FROM sys_user
<where>
<if test="userName != null and userName != ''">
AND user_name like concat('%', #{userName}, '%')
</if>
<if test="status != null and status != ''">
AND status = #{status}
</if>
</where>
</select>
RuoYi框架的SQL注入防护实践
1. SQL工具类的安全过滤
RuoYi在SqlUtil类中实现了严格的SQL注入检测机制:
public class SqlUtil {
// 定义SQL关键字黑名单
public static String SQL_REGEX = "\u000B|and |extractvalue|updatexml|sleep|exec |insert |select |delete |update |drop |count |chr |mid |master |truncate |char |declare |or |union |like |+|/*|user()";
// SQL注入检测方法
public static void filterKeyword(String value) {
if (StringUtils.isEmpty(value)) {
return;
}
String[] sqlKeywords = StringUtils.split(SQL_REGEX, "\\|");
for (String sqlKeyword : sqlKeywords) {
if (StringUtils.indexOfIgnoreCase(value, sqlKeyword) > -1) {
throw new UtilException("参数存在SQL注入风险");
}
}
}
}
2. Order By子句的安全处理
对于动态排序场景,RuoYi实现了严格的安全校验:
public static String escapeOrderBySql(String value) {
if (StringUtils.isNotEmpty(value) && !isValidOrderBySql(value)) {
throw new UtilException("参数不符合规范,不能进行查询");
}
if (StringUtils.length(value) > ORDER_BY_MAX_LENGTH) {
throw new UtilException("参数已超过最大限制,不能进行查询");
}
return value;
}
// 验证排序字段格式
public static boolean isValidOrderBySql(String value) {
return value.matches("[a-zA-Z0-9_\\ \\,\\.]+");
}
常见SQL注入场景与防护方案
1. 用户登录场景
2. 数据查询场景
// 不安全的写法(存在注入风险)
@Select("SELECT * FROM sys_user WHERE user_name = '${userName}'")
SysUser selectUserUnsafe(String userName);
// 安全的写法(推荐使用)
@Select("SELECT * FROM sys_user WHERE user_name = #{userName}")
SysUser selectUserSafe(String userName);
3. 动态表名和列名场景
对于必须使用动态表名或列名的场景,RuoYi建议:
// 使用白名单机制验证表名
private static final Set<String> ALLOWED_TABLES =
Set.of("sys_user", "sys_role", "sys_menu");
public void validateTableName(String tableName) {
if (!ALLOWED_TABLES.contains(tableName)) {
throw new UtilException("非法的表名");
}
}
MyBatis配置最佳实践
1. MyBatis配置类安全设置
@Configuration
public class MyBatisConfig {
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
// 设置类型别名包
sessionFactory.setTypeAliasesPackage("com.ruoyi.**.domain");
// 设置mapper文件位置
sessionFactory.setMapperLocations(resolveMapperLocations(
StringUtils.split(env.getProperty("mybatis.mapperLocations"), ",")));
return sessionFactory.getObject();
}
}
2. 日志监控配置
启用MyBatis的SQL日志监控,便于发现潜在的安全问题:
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
安全开发规范检查表
| 检查项 | 安全要求 | 风险等级 |
|---|---|---|
| 参数化查询 | 必须使用#{} | 高危 |
| 动态SQL | 避免直接拼接 | 中危 |
| Order By | 使用SqlUtil校验 | 中危 |
| Like查询 | 使用concat函数 | 低危 |
| 表名/列名 | 白名单验证 | 高危 |
实战:代码生成器的安全防护
RuoYi的代码生成器在处理自定义SQL时也实现了安全防护:
@RestController
@RequestMapping("/tool/gen")
public class GenController {
@PostMapping("/importTable")
public AjaxResult importTable(String tables, String sql) {
// SQL注入检测
SqlUtil.filterKeyword(sql);
// 执行安全的数据库操作
return success(genTableService.importGenTable(tables));
}
}
总结与建议
RuoYi框架通过多层次的防护策略有效防范SQL注入攻击:
- 核心防护:MyBatis参数化查询机制
- 补充防护:SqlUtil工具类的关键字过滤
- 边界防护:Order By等特殊场景的安全校验
- 监控防护:SQL执行日志监控
开发建议:
- 始终坚持使用
#{}而不是${} - 对用户输入进行严格的验证和过滤
- 定期进行安全代码审查
- 使用专业的SQL注入检测工具
通过遵循这些最佳实践,可以显著提升RuoYi应用的安全性,有效防范SQL注入攻击,保障企业数据安全。
安全提示:定期更新框架版本,关注安全漏洞公告,及时修补已知的安全问题。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



