Spring JDBC中如何安全传入用户输入的存储过程名避免SQL注入?
如何避免Spring JDBC调用用户输入存储过程的SQL注入风险
你当前用String.format直接拼接用户输入的存储过程名,确实会引入SQL注入风险——攻击者可以构造恶意的存储过程名,拼接后执行任意SQL语句。针对这个问题,有几个可靠的解决办法:
1. 白名单校验(最推荐)
预先维护一个允许调用的存储过程名单,用户输入的名称必须在这个名单内,否则直接拒绝请求。这种方式从根源上杜绝了注入可能,安全性最高。
示例代码:
// 预先定义允许调用的存储过程白名单 private static final Set<String> ALLOWED_PROCEDURES = Set.of("proc_get_user", "proc_update_status"); public Map<String, Object> callUserInputProcedure(String procedureName, String arg) { if (!ALLOWED_PROCEDURES.contains(procedureName)) { throw new IllegalArgumentException("不允许调用该存储过程"); } // 校验通过后再构造调用语句 String callStatement = String.format("ECHO %s (?)", procedureName); return jdbcTemplate.queryForMap(callStatement, arg); }
2. 基于数据库元数据校验
如果白名单不好维护,可以通过查询数据库的系统元数据,验证用户输入的存储过程是否真实存在。SingleStore的系统表information_schema.routines存储了所有存储过程信息,你可以查询这个表来校验:
示例代码:
public Map<String, Object> callUserInputProcedure(String procedureName, String arg) { // 查询数据库中是否存在该存储过程 String checkSql = "SELECT 1 FROM information_schema.routines WHERE routine_name = ? AND routine_type = 'PROCEDURE'"; Integer exists = jdbcTemplate.queryForObject(checkSql, Integer.class, procedureName); if (exists == null) { throw new IllegalArgumentException("存储过程不存在"); } // 校验通过后执行调用 String callStatement = String.format("ECHO %s (?)", procedureName); return jdbcTemplate.queryForMap(callStatement, arg); }
3. 严格的名称格式校验(备选方案)
如果上述两种方式都不适用,可以对用户输入的存储过程名做格式限制,只允许字母、数字和下划线这类合法标识符,用正则表达式校验:
示例代码:
public Map<String, Object> callUserInputProcedure(String procedureName, String arg) { // 正则匹配合法的存储过程名(可根据SingleStore的命名规则调整) if (!procedureName.matches("^[a-zA-Z0-9_]+$")) { throw new IllegalArgumentException("存储过程名格式非法"); } String callStatement = String.format("ECHO %s (?)", procedureName); return jdbcTemplate.queryForMap(callStatement, arg); }
注意:格式校验的安全性不如前两种,因为可能存在绕过正则的情况(比如某些数据库允许特殊字符的标识符,只要用引号包裹),所以优先用白名单或元数据校验。
内容的提问来源于stack exchange,提问作者ashu.imgud
相关产品推荐
相关产品推荐

