对https://blog.csdn.net/qq_37148232/article/details/135238622?spm=1001.2014.3001.5501作了下升级。使用切面对入参作全面检查。以解决低版本mybatisPlus所带来的sql注入问题。
public class IllegalSQLCheckExecutor {
private static JSONConfig jsonConfig;
private static Set<String> IGNORE_FIELDS = new HashSet<>();
static {
jsonConfig = JSONConfig.create();
// 使用的是hutool json工具
jsonConfig.setIgnoreNullValue(true);
jsonConfig.setIgnoreError(true);
IGNORE_FIELDS.add("pageNo");
IGNORE_FIELDS.add("pageSize");
// 跳过一些常用字段...
}
// springAOP前置通知,拦截controller
public void before(Object[] args, Signature signature) {
for (Object arg : args) {
if (null == arg) {
continue;
}
Class<?> argClass = arg.getClass();
String className = argClass.getName();
// 只检测本公司包名下的参数类型
if (!className.contains("zysoft")) {
continue;
}
try {
checkIllegalSQL(JSONUtil.parseObj(arg, jsonConfig));
} catch (IllegalSqlException ie) {
// sql检测异常
throw new LocalException("illegal parameters has been detected!");
} catch (Exception e) {
// 其他异常跳过,不影响正常逻辑
log.warn(e.getMessage());
}
}
}
private void checkIllegalSQL(JSONObject jsonObject) {
Set<String> keys = jsonObject.keySet();
if (keys.isEmpty()) {
return;
}
for (String key : keys) {
// 跳过忽略的通用Key
if (IGNORE_FIELDS.contains(key)) {
continue;
}
Object valueObj = jsonObject.getObj(key);
if (null == valueObj) {
continue;
}
// 只检测字符类型
if (valueObj instanceof String) {
String str = String.valueOf(valueObj);
SQLParamChecker.checkExistsIllegalSQL(str);
continue;
}
// 递归检测对象
if (valueObj instanceof JSONObject) {
JSONObject paramObject = (JSONObject) valueObj;
checkIllegalSQL(paramObject);
continue;
}
// 递归检测数组
if (valueObj instanceof JSONArray) {
JSONArray jsonArray = (JSONArray) valueObj;
for (int i = 0; i < jsonArray.size(); i++) {
JSONObject arrayObject = jsonArray.getJSONObject(i);
checkIllegalSQL(arrayObject);
}
}
}
}
}
检测工具类改进:
public class SQLParamChecker {
// 关键字
private static List<String> SPECIAL_WORDS;
// 特殊符号
private static List<String> SPECIAL_SYMBOL;
static {
SPECIAL_WORDS = ZYFileUtils.readClassPathFile2List("sql_special_key.txt");
SPECIAL_SYMBOL = ZYFileUtils.readClassPathFile2List("sql_special_symbol.txt");
}
public static void checkExistsIllegalSQL(String context) {
if (ZYStrUtils.isNull(context)) {
return;
}
// 统一转为小写
String text = context.toLowerCase();
// 检查是否存在关键字
boolean hasSpecialWord = ZYListUtils.anyMatch(SPECIAL_WORDS, text::contains);
boolean hasSpecialSymbol = ZYListUtils.anyMatch(SPECIAL_SYMBOL, text::contains);
// 同时带有关键字和SQL符号的注入才是完整的注入,否则不会生效。会被''符号视为字符串,
// 本判断依本公司实际情况和等保测评公司常用检测手段得来的,该判断并非绝对,应对等保公司测评应该问题不大。也能防止一些参数误判。
// 如后续有漏洞再更新本文章,
if (hasSpecialWord && hasSpecialSymbol) {
throw new IllegalSqlException();
}
}
}
sql_special_key.txt
add
modify
values
set
rollback
commit
database
in
on
alter
create
select
update
and
or
delete
insert
truncate
substr
ascii
exec
count
count
master
into
drop
execute
table
char
declare
sitename
xp_cmdshell
like
from
grant
use
group_concat
column_name
information_schema.columns
table_schema
union
where
order
by
sql_special_symbol.txt
*
;
--
+
,
%
)
(
'
=
public static List<String> readClassPathFile2List(String fileName) {
String context = readClassPathFile2String(fileName);
List<String> list = new ArrayList<>();
if (ZYStrUtils.isNull(context)) {
return list;
}
StringTokenizer stringTokenizer = new StringTokenizer(context);
while (stringTokenizer.hasMoreElements()) {
String nextToken = stringTokenizer.nextToken();
if (ZYStrUtils.isNotNull(nextToken)) {
list.add(nextToken);
}
}
return list;
}
public static String readClassPathFile2String(String fileName) {
ClassPathResource resource = new ClassPathResource(fileName);
try (InputStream inputStream = resource.getStream()) {
return IOUtils.toString(inputStream, StandardCharsets.UTF_8);
} catch (IOException e) {
return null;
}
}