1、性别校验器空不校验;2、报表sql相关入参新增支持注入校验;

This commit is contained in:
gaoqr
2024-10-25 10:44:28 +08:00
parent bcf97b6bb6
commit 97349c2aef
9 changed files with 237 additions and 0 deletions
@@ -1,5 +1,6 @@
package com.cf.imes.module.report.controller.admin.dataset.vo;
import com.cf.imes.module.report.validation.datasource.SqlParamInjectionValid;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
@@ -37,5 +38,6 @@ public class ReportDatasetParameterVO implements Serializable {
* 默认值
*/
@Schema(description = "参数默认值")
@SqlParamInjectionValid
private String defaultValue;
}
@@ -1,5 +1,6 @@
package com.cf.imes.module.report.controller.admin.dataset.vo;
import com.cf.imes.module.report.validation.datasource.SqlIllegalValid;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
@@ -42,6 +43,7 @@ public class ReportDatasetSaveReqVO implements Serializable {
private Long datasourceId;
@Schema(description = "动态查询SQL")
@SqlIllegalValid
private String sql;
@Schema(description = "bean数据源方法名")
@@ -1,5 +1,6 @@
package com.cf.imes.module.report.controller.admin.datasource.vo;
import com.cf.imes.module.report.validation.datasource.SqlParamInjectionValid;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
@@ -38,5 +39,6 @@ public class ReportDatasourceParameterVO implements Serializable {
* 默认值
*/
@Schema(description = "参数默认值")
@SqlParamInjectionValid
private String defaultValue;
}
@@ -1,5 +1,6 @@
package com.cf.imes.module.report.controller.admin.datasource.vo;
import com.cf.imes.module.report.validation.datasource.SqlIllegalValid;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
@@ -29,6 +30,7 @@ public class ReportDatasourcePreviewReqVO {
@Schema(description = "查询语句")
@NotBlank(message = "查询语句不能为空")
@SqlIllegalValid
private String sql;
@Schema(description = "查询参数列表")
@@ -0,0 +1,32 @@
package com.cf.imes.module.report.validation.datasource;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 非法sql校验注解
*
* @author Gqr
* @since 2024/10/21 18:42
*/
@Target({
ElementType.FIELD,
ElementType.PARAMETER,
})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(
validatedBy = {SqlIllegalValidator.class}
)
public @interface SqlIllegalValid {
String message() default "sql语句非法输入,请检查";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
@@ -0,0 +1,137 @@
package com.cf.imes.module.report.validation.datasource;
import cn.hutool.core.util.ObjectUtil;
import org.apache.commons.lang3.StringUtils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 非法sql校验器
*
* @author Gqr
* @since 2024/10/21 18:44
*/
public class SqlIllegalValidator implements ConstraintValidator<SqlIllegalValid, String> {
private static final Pattern annotationPattern = Pattern.compile("/\\*[\\s\\S]*\\*/");
private static final String[] someKeywordPattern = new String[]{"chr\\s*\\(", "mid\\s*\\(", " char\\s*\\(", "sleep\\s*\\(", "user\\s*\\(", "show\\s+tables", "user[\\s]*\\([\\s]*\\)", "show\\s+databases", "sleep\\(\\d*\\)", "sleep\\(.*\\)"};
private static final List<String> specialSymbolList = new ArrayList<>();
static {
specialSymbolList.add(";");
specialSymbolList.add("+");
specialSymbolList.add("--");
}
@Override
public boolean isValid(String sql, ConstraintValidatorContext context) {
if (StringUtils.isEmpty(sql)) {
return true;
}
// 校验注释
if (hasAnnotation(sql)) {
context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
context.buildConstraintViolationWithTemplate("SQL中不允许包含注释,存在安全风险").addConstraintViolation();
return false;
}
sql = sql.toLowerCase();
sql = sql.replaceAll("/\\*.*\\*/", "");
// 敏感关键字数组
String[] arrayOfSensitiveKey = "exec |peformance_schema|information_schema|extractvalue|updatexml|geohash|gtid_subset|gtid_subtract|insert |alter |delete |grant |update |drop |master |truncate |declare |--|".split("\\|");
// 校验敏感关键字
for (byte b = 0; b < arrayOfSensitiveKey.length; b++) {
if (checkSensitiveKey(sql, arrayOfSensitiveKey[b])) {
context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
context.buildConstraintViolationWithTemplate("SQL不合法,存在注入风险").addConstraintViolation();
return false;
}
}
// 校验一些函数关键字
for (String skw : someKeywordPattern) {
String someKeywordStr = ".*" + skw + ".*";
if (Pattern.matches(someKeywordStr, sql)) {
context.disableDefaultConstraintViolation(); // 禁用默认的 message 的值
context.buildConstraintViolationWithTemplate("SQL不合法,存在注入风险").addConstraintViolation();
return false;
}
}
return true;
}
/**
* 检查敏感关键字
*
* @param sql
* @param sensitiveKey
* @return true:带敏感关键字,反之不带
*/
private boolean checkSensitiveKey(String sql, String sensitiveKey) {
if (sql.startsWith(sensitiveKey.trim())) {
return true;
}
if (sql.contains(sensitiveKey)) {
String sk1 = " " + sensitiveKey;
if (specialSymbolList.contains(sensitiveKey)) {
sk1 = sensitiveKey;
}
if (sql.contains(sk1)) {
return true;
}
String sk2 = "\\s+\\S+" + sensitiveKey;
List<String> list = matchInjectKeyword(sk2, sql);
for (String keyword : list) {
if (keyword.contains("%") || keyword.contains("+") || keyword.contains("#") || keyword.contains("/") || keyword.contains(")")) {
return true;
}
}
}
return false;
}
/**
* 校验注释
*
* @param sql
* @return
*/
private boolean hasAnnotation(String sql) {
// 不允许存在注释
if (sql.contains("--")) {
return true;
} else {
Matcher var1 = annotationPattern.matcher(sql);
if (var1.find()) {
return true;
}
}
return false;
}
/**
* 匹配注入关键字
*
* @param sensitiveKey
* @param sql
* @return
*/
public List<String> matchInjectKeyword(String sensitiveKey, CharSequence sql) {
Pattern paramPattern = Pattern.compile(sensitiveKey, 32);
List<String> paramT = new ArrayList<>();
if (ObjectUtil.isAllNotEmpty(paramPattern, sql)) {
Matcher matcher = paramPattern.matcher(sql);
while (matcher.find()) {
paramT.add(matcher.group(0));
}
}
return paramT;
}
}
@@ -0,0 +1,32 @@
package com.cf.imes.module.report.validation.datasource;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* sql参数注入攻击校验注解
*
* @author Gqr
* @since 2024/10/25 10:30
*/
@Target({
ElementType.FIELD,
ElementType.PARAMETER,
})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(
validatedBy = {SqlParamInjectionValidator.class}
)
public @interface SqlParamInjectionValid {
String message() default "sql查询参数默认值非法输入,请检查";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
@@ -0,0 +1,24 @@
package com.cf.imes.module.report.validation.datasource;
import com.baomidou.mybatisplus.core.toolkit.sql.SqlInjectionUtils;
import org.apache.commons.lang3.StringUtils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
/**
* sql参数注入攻击校验器
*
* @author Gqr
* @since 2024/10/25 10:30
*/
public class SqlParamInjectionValidator implements ConstraintValidator<SqlParamInjectionValid, String> {
@Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (StringUtils.isEmpty(value)) {
return true;
}
// mybatisplus工具校验注入攻击
return !SqlInjectionUtils.check(value);
}
}
@@ -1,5 +1,6 @@
package com.cf.imes.module.system.validation.user;
import cn.hutool.core.util.ObjectUtil;
import com.cf.imes.module.system.enums.common.SexEnum;
import javax.validation.ConstraintValidator;
@@ -16,6 +17,9 @@ public class SexValidator implements ConstraintValidator<SexValid, Integer> {
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
if (ObjectUtil.isNull(value)) {
return true;
}
for (SexEnum sexEnum : SexEnum.values()) {
if (Objects.equals(sexEnum.getSex(), value)) {
return true;