同步 MP 3.5.3.2 sql注入检查代码

https://gitee.com/baomidou/mybatis-plus/issues/I7OCM4
This commit is contained in:
yulichang 2023-08-13 02:54:25 +08:00
parent 026c64ee59
commit a8b91edb6b
5 changed files with 65 additions and 36 deletions

View File

@ -7,12 +7,15 @@ import com.baomidou.mybatisplus.core.conditions.interfaces.Nested;
import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments; import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;
import com.baomidou.mybatisplus.core.enums.SqlKeyword; import com.baomidou.mybatisplus.core.enums.SqlKeyword;
import com.baomidou.mybatisplus.core.enums.SqlLike; import com.baomidou.mybatisplus.core.enums.SqlLike;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.*; import com.baomidou.mybatisplus.core.toolkit.*;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlUtils; import com.baomidou.mybatisplus.core.toolkit.sql.SqlUtils;
import com.baomidou.mybatisplus.core.toolkit.sql.StringEscape; import com.baomidou.mybatisplus.core.toolkit.sql.StringEscape;
import com.github.yulichang.kt.interfaces.*; import com.github.yulichang.kt.interfaces.Compare;
import com.github.yulichang.kt.interfaces.Func;
import com.github.yulichang.kt.interfaces.OnCompare;
import com.github.yulichang.toolkit.KtUtils; import com.github.yulichang.toolkit.KtUtils;
import com.github.yulichang.toolkit.MPJStringUtils; import com.github.yulichang.toolkit.MPJSqlInjectionUtils;
import com.github.yulichang.toolkit.TableList; import com.github.yulichang.toolkit.TableList;
import com.github.yulichang.toolkit.sql.SqlScriptUtils; import com.github.yulichang.toolkit.sql.SqlScriptUtils;
import com.github.yulichang.wrapper.enums.PrefixEnum; import com.github.yulichang.wrapper.enums.PrefixEnum;
@ -110,6 +113,11 @@ public abstract class KtAbstractWrapper<T, Children extends KtAbstractWrapper<T,
@Getter @Getter
protected TableList tableList; protected TableList tableList;
/**
* 检查 SQL 注入过滤
*/
protected boolean checkSqlInjection = false;
@Override @Override
public T getEntity() { public T getEntity() {
return entity; return entity;
@ -138,8 +146,16 @@ public abstract class KtAbstractWrapper<T, Children extends KtAbstractWrapper<T,
return typedThis; return typedThis;
} }
/**
* 开启检查 SQL 注入
*/
public Children checkSqlInjection() {
this.checkSqlInjection = true;
return typedThis;
}
@Override @Override
public Children allEq(boolean condition, Map<KProperty<?>, ?> params, boolean null2IsNull) { public Children allEq(boolean condition, Map<KProperty<?>, ?> params, boolean null2IsNull) {
if (condition && CollectionUtils.isNotEmpty(params)) { if (condition && CollectionUtils.isNotEmpty(params)) {
params.forEach((k, v) -> { params.forEach((k, v) -> {
if (StringUtils.checkValNotNull(v)) { if (StringUtils.checkValNotNull(v)) {
@ -155,17 +171,17 @@ public abstract class KtAbstractWrapper<T, Children extends KtAbstractWrapper<T,
} }
@Override @Override
public Children eq(boolean condition, KProperty<?> column, Object val) { public Children eq(boolean condition, KProperty<?> column, Object val) {
return addCondition(condition, column, EQ, val); return addCondition(condition, column, EQ, val);
} }
@Override @Override
public Children ne(boolean condition, KProperty<?> column, Object val) { public Children ne(boolean condition, KProperty<?> column, Object val) {
return addCondition(condition, column, NE, val); return addCondition(condition, column, NE, val);
} }
@Override @Override
public Children gt(boolean condition, KProperty<?> column, Object val) { public Children gt(boolean condition, KProperty<?> column, Object val) {
return addCondition(condition, column, GT, val); return addCondition(condition, column, GT, val);
} }
@ -175,27 +191,27 @@ public abstract class KtAbstractWrapper<T, Children extends KtAbstractWrapper<T,
} }
@Override @Override
public Children lt(boolean condition, KProperty<?> column, Object val) { public Children lt(boolean condition, KProperty<?> column, Object val) {
return addCondition(condition, column, LT, val); return addCondition(condition, column, LT, val);
} }
@Override @Override
public Children le(boolean condition, KProperty<?> column, Object val) { public Children le(boolean condition, KProperty<?> column, Object val) {
return addCondition(condition, column, LE, val); return addCondition(condition, column, LE, val);
} }
@Override @Override
public Children like(boolean condition, KProperty<?> column, Object val) { public Children like(boolean condition, KProperty<?> column, Object val) {
return likeValue(condition, LIKE, column, val, SqlLike.DEFAULT); return likeValue(condition, LIKE, column, val, SqlLike.DEFAULT);
} }
@Override @Override
public Children notLike(boolean condition, KProperty<?> column, Object val) { public Children notLike(boolean condition, KProperty<?> column, Object val) {
return likeValue(condition, NOT_LIKE, column, val, SqlLike.DEFAULT); return likeValue(condition, NOT_LIKE, column, val, SqlLike.DEFAULT);
} }
@Override @Override
public Children likeLeft(boolean condition, KProperty<?> column, Object val) { public Children likeLeft(boolean condition, KProperty<?> column, Object val) {
return likeValue(condition, LIKE, column, val, SqlLike.LEFT); return likeValue(condition, LIKE, column, val, SqlLike.LEFT);
} }
@ -205,13 +221,13 @@ public abstract class KtAbstractWrapper<T, Children extends KtAbstractWrapper<T,
} }
@Override @Override
public Children between(boolean condition, KProperty<?> column, Object val1, Object val2) { public Children between(boolean condition, KProperty<?> column, Object val1, Object val2) {
return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(index, column, false), BETWEEN, return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(index, column, false), BETWEEN,
() -> formatParam(null, val1), AND, () -> formatParam(null, val2))); () -> formatParam(null, val1), AND, () -> formatParam(null, val2)));
} }
@Override @Override
public Children notBetween(boolean condition, KProperty<?> column, Object val1, Object val2) { public Children notBetween(boolean condition, KProperty<?> column, Object val1, Object val2) {
return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(index, column, false), NOT_BETWEEN, return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(index, column, false), NOT_BETWEEN,
() -> formatParam(null, val1), AND, () -> formatParam(null, val2))); () -> formatParam(null, val1), AND, () -> formatParam(null, val2)));
} }
@ -283,7 +299,7 @@ public abstract class KtAbstractWrapper<T, Children extends KtAbstractWrapper<T,
} }
@Override @Override
public Children isNull(boolean condition, KProperty<?> column) { public Children isNull(boolean condition, KProperty<?> column) {
return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(index, column, false), IS_NULL)); return maybeDo(condition, () -> appendSqlSegments(columnToSqlSegment(index, column, false), IS_NULL));
} }
@ -677,7 +693,7 @@ public abstract class KtAbstractWrapper<T, Children extends KtAbstractWrapper<T,
} }
protected final ISqlSegment columnToSqlSegment(String column) { protected final ISqlSegment columnToSqlSegment(String column) {
return () -> (String) column; return () -> columnsToString(column);
} }
/** /**
@ -688,6 +704,9 @@ public abstract class KtAbstractWrapper<T, Children extends KtAbstractWrapper<T,
} }
protected String columnToString(String column) { protected String columnToString(String column) {
if (checkSqlInjection && MPJSqlInjectionUtils.check(column)) {
throw new MybatisPlusException("Discovering SQL injection column: " + column);
}
return column; return column;
} }
@ -941,18 +960,14 @@ public abstract class KtAbstractWrapper<T, Children extends KtAbstractWrapper<T,
public final Children orderBy(boolean condition, boolean isAsc, String column, String... columns) { public final Children orderBy(boolean condition, boolean isAsc, String column, String... columns) {
return maybeDo(condition, () -> { return maybeDo(condition, () -> {
final SqlKeyword mode = isAsc ? ASC : DESC; final SqlKeyword mode = isAsc ? ASC : DESC;
appendSqlSegments(ORDER_BY, columnToSqlSegment(columnSqlInjectFilter(column)), mode); appendSqlSegments(ORDER_BY, columnToSqlSegment(column), mode);
if (ArrayUtils.isNotEmpty(columns)) { if (ArrayUtils.isNotEmpty(columns)) {
Arrays.stream(columns).forEach(c -> appendSqlSegments(ORDER_BY, Arrays.stream(columns).forEach(c -> appendSqlSegments(ORDER_BY,
columnToSqlSegment(columnSqlInjectFilter(c)), mode)); columnToSqlSegment(c), mode));
} }
}); });
} }
protected String columnSqlInjectFilter(String column) {
return MPJStringUtils.sqlInjectionReplaceBlank(column);
}
@Override @Override
public Children groupBy(boolean condition, String column) { public Children groupBy(boolean condition, String column) {
return maybeDo(condition, () -> appendSqlSegments(GROUP_BY, () -> columnToString(column))); return maybeDo(condition, () -> appendSqlSegments(GROUP_BY, () -> columnToString(column)));
@ -965,13 +980,13 @@ public abstract class KtAbstractWrapper<T, Children extends KtAbstractWrapper<T,
@Override @Override
public Children orderBy(boolean condition, boolean isAsc, String column) { public Children orderBy(boolean condition, boolean isAsc, String column) {
return maybeDo(condition, () -> appendSqlSegments(ORDER_BY, columnToSqlSegment(columnSqlInjectFilter(column)), return maybeDo(condition, () -> appendSqlSegments(ORDER_BY, columnToSqlSegment(column),
isAsc ? ASC : DESC)); isAsc ? ASC : DESC));
} }
@Override @Override
public Children orderByStr(boolean condition, boolean isAsc, List<String> columns) { public Children orderByStr(boolean condition, boolean isAsc, List<String> columns) {
return maybeDo(condition, () -> columns.forEach(c -> appendSqlSegments(ORDER_BY, return maybeDo(condition, () -> columns.forEach(c -> appendSqlSegments(ORDER_BY,
columnToSqlSegment(columnSqlInjectFilter(c)), isAsc ? ASC : DESC))); columnToSqlSegment(c), isAsc ? ASC : DESC)));
} }
} }

View File

@ -30,11 +30,12 @@ public class MPJSqlInjectionUtils {
* SQL语法检查正则符合两个关键字有先后顺序才算匹配 * SQL语法检查正则符合两个关键字有先后顺序才算匹配
*/ */
private static final Pattern SQL_SYNTAX_PATTERN = Pattern.compile("(insert|delete|update|select|create|drop|truncate|grant|alter|deny|revoke|call|execute|exec|declare|show|rename|set)" + private static final Pattern SQL_SYNTAX_PATTERN = Pattern.compile("(insert|delete|update|select|create|drop|truncate|grant|alter|deny|revoke|call|execute|exec|declare|show|rename|set)" +
"\\s+.*(into|from|set|where|table|database|view|index|on|cursor|procedure|trigger|for|password|union|and|or)|(select\\s*\\*\\s*from\\s+)", Pattern.CASE_INSENSITIVE); "\\s+.*(into|from|set|where|table|database|view|index|on|cursor|procedure|trigger|for|password|union|and|or)|(select\\s*\\*\\s*from\\s+)|(and|or)\\s+.*(like|=|>|<|in|between|is|not|exists)", Pattern.CASE_INSENSITIVE);
/** /**
* 使用';或注释截断SQL检查正则 * 使用';或注释截断SQL检查正则
*/ */
private static final Pattern SQL_COMMENT_PATTERN = Pattern.compile("'.*(or|union|--|#|/*|;)", Pattern.CASE_INSENSITIVE); private static final Pattern SQL_COMMENT_PATTERN = Pattern.compile("'.*(or|union|--|#|/\\*|;)", Pattern.CASE_INSENSITIVE);
/** /**
* 检查参数是否存在 SQL 注入 * 检查参数是否存在 SQL 注入

View File

@ -16,6 +16,7 @@ import java.util.Optional;
* @author yulichang * @author yulichang
* @since 1.4.5 * @since 1.4.5
*/ */
@SuppressWarnings("DuplicatedCode")
public class WrapperUtils { public class WrapperUtils {
public static <T> String buildSubSqlByWrapper(Class<T> clazz, MPJLambdaWrapper<T> wrapper, String alias) { public static <T> String buildSubSqlByWrapper(Class<T> clazz, MPJLambdaWrapper<T> wrapper, String alias) {

View File

@ -7,12 +7,13 @@ import com.baomidou.mybatisplus.core.conditions.interfaces.Nested;
import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments; import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;
import com.baomidou.mybatisplus.core.enums.SqlKeyword; import com.baomidou.mybatisplus.core.enums.SqlKeyword;
import com.baomidou.mybatisplus.core.enums.SqlLike; import com.baomidou.mybatisplus.core.enums.SqlLike;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.*; import com.baomidou.mybatisplus.core.toolkit.*;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlUtils; import com.baomidou.mybatisplus.core.toolkit.sql.SqlUtils;
import com.baomidou.mybatisplus.core.toolkit.sql.StringEscape; import com.baomidou.mybatisplus.core.toolkit.sql.StringEscape;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction; import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.github.yulichang.toolkit.LambdaUtils; import com.github.yulichang.toolkit.LambdaUtils;
import com.github.yulichang.toolkit.MPJStringUtils; import com.github.yulichang.toolkit.MPJSqlInjectionUtils;
import com.github.yulichang.toolkit.TableList; import com.github.yulichang.toolkit.TableList;
import com.github.yulichang.toolkit.sql.SqlScriptUtils; import com.github.yulichang.toolkit.sql.SqlScriptUtils;
import com.github.yulichang.wrapper.enums.PrefixEnum; import com.github.yulichang.wrapper.enums.PrefixEnum;
@ -107,6 +108,11 @@ public abstract class MPJAbstractWrapper<T, Children extends MPJAbstractWrapper<
@Getter @Getter
protected TableList tableList; protected TableList tableList;
/**
* 检查 SQL 注入过滤
*/
protected boolean checkSqlInjection = false;
@Override @Override
public T getEntity() { public T getEntity() {
return entity; return entity;
@ -135,6 +141,14 @@ public abstract class MPJAbstractWrapper<T, Children extends MPJAbstractWrapper<
return typedThis; return typedThis;
} }
/**
* 开启检查 SQL 注入
*/
public Children checkSqlInjection() {
this.checkSqlInjection = true;
return typedThis;
}
@Override @Override
public <X, V> Children allEq(boolean condition, Map<SFunction<X, ?>, V> params, boolean null2IsNull) { public <X, V> Children allEq(boolean condition, Map<SFunction<X, ?>, V> params, boolean null2IsNull) {
if (condition && CollectionUtils.isNotEmpty(params)) { if (condition && CollectionUtils.isNotEmpty(params)) {
@ -692,7 +706,7 @@ public abstract class MPJAbstractWrapper<T, Children extends MPJAbstractWrapper<
} }
protected final <X> ISqlSegment columnToSqlSegment(String column) { protected final <X> ISqlSegment columnToSqlSegment(String column) {
return () -> (String) column; return () -> columnsToString(column);
} }
/** /**
@ -703,6 +717,9 @@ public abstract class MPJAbstractWrapper<T, Children extends MPJAbstractWrapper<
} }
protected String columnToString(String column) { protected String columnToString(String column) {
if (checkSqlInjection && MPJSqlInjectionUtils.check(column)) {
throw new MybatisPlusException("Discovering SQL injection column: " + column);
}
return column; return column;
} }
@ -956,18 +973,14 @@ public abstract class MPJAbstractWrapper<T, Children extends MPJAbstractWrapper<
public final Children orderBy(boolean condition, boolean isAsc, String column, String... columns) { public final Children orderBy(boolean condition, boolean isAsc, String column, String... columns) {
return maybeDo(condition, () -> { return maybeDo(condition, () -> {
final SqlKeyword mode = isAsc ? ASC : DESC; final SqlKeyword mode = isAsc ? ASC : DESC;
appendSqlSegments(ORDER_BY, columnToSqlSegment(columnSqlInjectFilter(column)), mode); appendSqlSegments(ORDER_BY, columnToSqlSegment(column), mode);
if (ArrayUtils.isNotEmpty(columns)) { if (ArrayUtils.isNotEmpty(columns)) {
Arrays.stream(columns).forEach(c -> appendSqlSegments(ORDER_BY, Arrays.stream(columns).forEach(c -> appendSqlSegments(ORDER_BY,
columnToSqlSegment(columnSqlInjectFilter(c)), mode)); columnToSqlSegment(c), mode));
} }
}); });
} }
protected String columnSqlInjectFilter(String column) {
return MPJStringUtils.sqlInjectionReplaceBlank(column);
}
@Override @Override
public Children groupBy(boolean condition, String column) { public Children groupBy(boolean condition, String column) {
return maybeDo(condition, () -> appendSqlSegments(GROUP_BY, () -> columnToString(column))); return maybeDo(condition, () -> appendSqlSegments(GROUP_BY, () -> columnToString(column)));
@ -980,13 +993,13 @@ public abstract class MPJAbstractWrapper<T, Children extends MPJAbstractWrapper<
@Override @Override
public Children orderBy(boolean condition, boolean isAsc, String column) { public Children orderBy(boolean condition, boolean isAsc, String column) {
return maybeDo(condition, () -> appendSqlSegments(ORDER_BY, columnToSqlSegment(columnSqlInjectFilter(column)), return maybeDo(condition, () -> appendSqlSegments(ORDER_BY, columnToSqlSegment(column),
isAsc ? ASC : DESC)); isAsc ? ASC : DESC));
} }
@Override @Override
public Children orderByStr(boolean condition, boolean isAsc, List<String> columns) { public Children orderByStr(boolean condition, boolean isAsc, List<String> columns) {
return maybeDo(condition, () -> columns.forEach(c -> appendSqlSegments(ORDER_BY, return maybeDo(condition, () -> columns.forEach(c -> appendSqlSegments(ORDER_BY,
columnToSqlSegment(columnSqlInjectFilter(c)), isAsc ? ASC : DESC))); columnToSqlSegment(c), isAsc ? ASC : DESC)));
} }
} }

View File

@ -57,7 +57,6 @@ public class MPJLambdaWrapper<T> extends MPJAbstractLambdaWrapper<T, MPJLambdaWr
*/ */
private SharedString unionSql; private SharedString unionSql;
/** /**
* 推荐使用 class 的构造方法 * 推荐使用 class 的构造方法
*/ */