package com.utils; import org.apache.commons.lang3.StringUtils; import com.entity.EIException; /** * SQL注入过滤工具类 * * 核心职责:防止SQL注入攻击,确保输入数据的安全性 * * 安全特性: * 1. 过滤危险字符:单引号、双引号、分号、反斜杠 * 2. 检测SQL关键字:防止SQL语句拼接 * 3. 抛出明确异常:发现非法字符时立即中断处理 * * 使用场景: * - 用户输入的参数验证 * - 动态SQL拼接前的参数过滤 * - 排序字段和排序方向的安全验证 * * 注意事项: * - 主要用于简单的字符串参数过滤 * - 对于复杂查询建议使用参数化查询或ORM框架 */ public class SQLFilter { /** * SQL注入过滤 * 对输入的字符串进行SQL注入检测和过滤 * * @param str 待验证的字符串 * @return 过滤后的安全字符串,如果输入为空返回null * @throws EIException 当检测到SQL注入攻击时抛出异常 */ public static String sqlInject(String str) { // 检查输入是否为空 if (StringUtils.isBlank(str)) { return null; } // 第一步:移除危险字符 // 移除单引号,防止字符串逃逸 str = StringUtils.replace(str, "'", ""); // 移除双引号,防止字符串逃逸 str = StringUtils.replace(str, "\"", ""); // 移除分号,防止多语句执行 str = StringUtils.replace(str, ";", ""); // 移除反斜杠,防止转义字符攻击 str = StringUtils.replace(str, "\\", ""); // 第二步:统一转换为小写,便于关键字检测 str = str.toLowerCase(); // 第三步:定义SQL关键字黑名单 String[] keywords = { "master", // 数据库主表操作 "truncate", // 清空表数据 "insert", // 插入数据 "select", // 查询数据 "delete", // 删除数据 "update", // 更新数据 "declare", // 声明变量 "alter", // 修改表结构 "drop" // 删除表 }; // 第四步:检测是否包含非法SQL关键字 for (String keyword : keywords) { if (str.indexOf(keyword) != -1) { // 发现非法字符,抛出安全异常 throw new EIException("包含非法字符"); } } // 返回过滤后的安全字符串 return str; } // 可以添加的增强方法: /** * 严格的SQL注入过滤(增强版) * 使用更严格的关键字检测和字符过滤 * * @param str 待验证的字符串 * @param fieldName 字段名(用于错误提示) * @return 过滤后的安全字符串 * @throws EIException 当检测到SQL注入攻击时抛出异常 */ public static String strictSqlInject(String str, String fieldName) { if (StringUtils.isBlank(str)) { return null; } // 移除更多危险字符 str = StringUtils.replace(str, "'", ""); str = StringUtils.replace(str, "\"", ""); str = StringUtils.replace(str, ";", ""); str = StringUtils.replace(str, "\\", ""); str = StringUtils.replace(str, "--", ""); // SQL注释 str = StringUtils.replace(str, "/*", ""); // 多行注释开始 str = StringUtils.replace(str, "*/", ""); // 多行注释结束 str = StringUtils.replace(str, "#", ""); // MySQL注释 // 转换为小写进行检测 String lowerStr = str.toLowerCase(); // 扩展的关键字黑名单 String[] extendedKeywords = { "master", "truncate", "insert", "select", "delete", "update", "declare", "alter", "drop", "create", "exec", "execute", "union", "join", "where", "having", "group by", "order by", "script", "javascript", "vbscript", "onload", "onerror" }; // 检测关键字 for (String keyword : extendedKeywords) { if (lowerStr.contains(keyword)) { throw new EIException("字段[" + fieldName + "]包含非法字符: " + keyword); } } // 检测常见的SQL注入模式 if (containsSqlInjectionPattern(lowerStr)) { throw new EIException("字段[" + fieldName + "]检测到SQL注入模式"); } return str; } /** * 检测SQL注入模式 * * @param str 待检测字符串 * @return 是否包含SQL注入模式 */ private static boolean containsSqlInjectionPattern(String str) { // 检测常见的SQL注入攻击模式 String[] patterns = { "1=1", "1=2", "or 1=1", "or 1=2", "and 1=1", "and 1=2", "' or '1'='1", "union select", "union all select" }; for (String pattern : patterns) { if (str.contains(pattern)) { return true; } } return false; } /** * 仅允许字母数字和下划线的安全过滤 * 适用于表名、字段名等标识符 * * @param str 待验证字符串 * @return 过滤后的安全字符串 * @throws EIException 当包含非法字符时抛出异常 */ public static String safeIdentifier(String str) { if (StringUtils.isBlank(str)) { return null; } // 只允许字母、数字、下划线 if (!str.matches("^[a-zA-Z0-9_]+$")) { throw new EIException("标识符包含非法字符,只允许字母、数字和下划线"); } return str; } /** * 数字参数安全验证 * * @param str 待验证字符串 * @return 验证后的数字字符串 * @throws EIException 当不是纯数字时抛出异常 */ public static String safeNumber(String str) { if (StringUtils.isBlank(str)) { return null; } // 验证是否为纯数字 if (!str.matches("^[0-9]+$")) { throw new EIException("参数必须为纯数字"); } return str; } /** * 排序方向安全验证 * 只允许ASC和DESC * * @param order 排序方向 * @return 安全的排序方向 * @throws EIException 当排序方向不合法时抛出异常 */ public static String safeOrder(String order) { if (StringUtils.isBlank(order)) { return "ASC"; // 默认升序 } order = order.toUpperCase(); if ("ASC".equals(order) || "DESC".equals(order)) { return order; } throw new EIException("排序方向不合法,只允许ASC或DESC"); } /** * 批量SQL注入过滤 * * @param params 参数Map * @return 过滤后的参数Map */ public static Map batchSqlInject(Map params) { if (params == null || params.isEmpty()) { return params; } Map safeParams = new HashMap<>(); for (Map.Entry entry : params.entrySet()) { Object value = entry.getValue(); if (value instanceof String) { // 对字符串参数进行过滤 safeParams.put(entry.getKey(), sqlInject((String) value)); } else { // 非字符串参数直接保留 safeParams.put(entry.getKey(), value); } } return safeParams; } }