|
|
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<String, Object> batchSqlInject(Map<String, Object> params) {
|
|
|
if (params == null || params.isEmpty()) {
|
|
|
return params;
|
|
|
}
|
|
|
|
|
|
Map<String, Object> safeParams = new HashMap<>();
|
|
|
for (Map.Entry<String, Object> 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;
|
|
|
}
|
|
|
} |