From 97349c2aefeb7cac20222d4cd0669e02da914d87 Mon Sep 17 00:00:00 2001 From: gaoqr <13665037151@163.com> Date: Fri, 25 Oct 2024 10:44:28 +0800 Subject: [PATCH] =?UTF-8?q?1=E3=80=81=E6=80=A7=E5=88=AB=E6=A0=A1=E9=AA=8C?= =?UTF-8?q?=E5=99=A8=E7=A9=BA=E4=B8=8D=E6=A0=A1=E9=AA=8C=EF=BC=9B2?= =?UTF-8?q?=E3=80=81=E6=8A=A5=E8=A1=A8sql=E7=9B=B8=E5=85=B3=E5=85=A5?= =?UTF-8?q?=E5=8F=82=E6=96=B0=E5=A2=9E=E6=94=AF=E6=8C=81=E6=B3=A8=E5=85=A5?= =?UTF-8?q?=E6=A0=A1=E9=AA=8C=EF=BC=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dataset/vo/ReportDatasetParameterVO.java | 2 + .../dataset/vo/ReportDatasetSaveReqVO.java | 2 + .../vo/ReportDatasourceParameterVO.java | 2 + .../vo/ReportDatasourcePreviewReqVO.java | 2 + .../datasource/SqlIllegalValid.java | 32 ++++ .../datasource/SqlIllegalValidator.java | 137 ++++++++++++++++++ .../datasource/SqlParamInjectionValid.java | 32 ++++ .../SqlParamInjectionValidator.java | 24 +++ .../system/validation/user/SexValidator.java | 4 + 9 files changed, 237 insertions(+) create mode 100644 cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/validation/datasource/SqlIllegalValid.java create mode 100644 cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/validation/datasource/SqlIllegalValidator.java create mode 100644 cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/validation/datasource/SqlParamInjectionValid.java create mode 100644 cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/validation/datasource/SqlParamInjectionValidator.java diff --git a/cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/controller/admin/dataset/vo/ReportDatasetParameterVO.java b/cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/controller/admin/dataset/vo/ReportDatasetParameterVO.java index 40c9bcd55..8191490d5 100644 --- a/cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/controller/admin/dataset/vo/ReportDatasetParameterVO.java +++ b/cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/controller/admin/dataset/vo/ReportDatasetParameterVO.java @@ -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; } diff --git a/cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/controller/admin/dataset/vo/ReportDatasetSaveReqVO.java b/cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/controller/admin/dataset/vo/ReportDatasetSaveReqVO.java index f653bcbf2..c36eba371 100644 --- a/cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/controller/admin/dataset/vo/ReportDatasetSaveReqVO.java +++ b/cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/controller/admin/dataset/vo/ReportDatasetSaveReqVO.java @@ -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数据源方法名") diff --git a/cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/controller/admin/datasource/vo/ReportDatasourceParameterVO.java b/cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/controller/admin/datasource/vo/ReportDatasourceParameterVO.java index 848580a44..b58233370 100644 --- a/cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/controller/admin/datasource/vo/ReportDatasourceParameterVO.java +++ b/cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/controller/admin/datasource/vo/ReportDatasourceParameterVO.java @@ -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; } diff --git a/cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/controller/admin/datasource/vo/ReportDatasourcePreviewReqVO.java b/cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/controller/admin/datasource/vo/ReportDatasourcePreviewReqVO.java index c533acbbd..a90a1a37c 100644 --- a/cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/controller/admin/datasource/vo/ReportDatasourcePreviewReqVO.java +++ b/cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/controller/admin/datasource/vo/ReportDatasourcePreviewReqVO.java @@ -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 = "查询参数列表") diff --git a/cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/validation/datasource/SqlIllegalValid.java b/cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/validation/datasource/SqlIllegalValid.java new file mode 100644 index 000000000..f73404f1b --- /dev/null +++ b/cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/validation/datasource/SqlIllegalValid.java @@ -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[] payload() default {}; +} diff --git a/cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/validation/datasource/SqlIllegalValidator.java b/cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/validation/datasource/SqlIllegalValidator.java new file mode 100644 index 000000000..c0dc29a31 --- /dev/null +++ b/cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/validation/datasource/SqlIllegalValidator.java @@ -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 { + + 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 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 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 matchInjectKeyword(String sensitiveKey, CharSequence sql) { + Pattern paramPattern = Pattern.compile(sensitiveKey, 32); + List paramT = new ArrayList<>(); + if (ObjectUtil.isAllNotEmpty(paramPattern, sql)) { + Matcher matcher = paramPattern.matcher(sql); + while (matcher.find()) { + paramT.add(matcher.group(0)); + } + } + return paramT; + } +} diff --git a/cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/validation/datasource/SqlParamInjectionValid.java b/cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/validation/datasource/SqlParamInjectionValid.java new file mode 100644 index 000000000..4d8a1932f --- /dev/null +++ b/cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/validation/datasource/SqlParamInjectionValid.java @@ -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[] payload() default {}; +} diff --git a/cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/validation/datasource/SqlParamInjectionValidator.java b/cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/validation/datasource/SqlParamInjectionValidator.java new file mode 100644 index 000000000..2879502f3 --- /dev/null +++ b/cf-module-report/cf-module-report-biz/src/main/java/com/cf/imes/module/report/validation/datasource/SqlParamInjectionValidator.java @@ -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 { + @Override + public boolean isValid(String value, ConstraintValidatorContext context) { + if (StringUtils.isEmpty(value)) { + return true; + } + // mybatisplus工具校验注入攻击 + return !SqlInjectionUtils.check(value); + } +} diff --git a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/validation/user/SexValidator.java b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/validation/user/SexValidator.java index 037a51365..538b7a18a 100644 --- a/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/validation/user/SexValidator.java +++ b/cf-module-system/cf-module-system-biz/src/main/java/com/cf/imes/module/system/validation/user/SexValidator.java @@ -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 { @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;