diff --git a/core/core-backend/src/main/java/io/dataease/datasource/provider/CalciteProvider.java b/core/core-backend/src/main/java/io/dataease/datasource/provider/CalciteProvider.java index 14a8114f31..4cf67c124a 100644 --- a/core/core-backend/src/main/java/io/dataease/datasource/provider/CalciteProvider.java +++ b/core/core-backend/src/main/java/io/dataease/datasource/provider/CalciteProvider.java @@ -7,8 +7,10 @@ import io.dataease.dataset.utils.FieldUtils; import io.dataease.datasource.dao.auto.entity.CoreDatasource; import io.dataease.datasource.dao.auto.entity.CoreDriver; import io.dataease.datasource.dao.auto.mapper.CoreDatasourceMapper; +import io.dataease.datasource.dao.auto.mapper.CoreDriverMapper; import io.dataease.datasource.manage.EngineManage; import io.dataease.datasource.request.EngineRequest; +import io.dataease.datasource.security.JdbcUrlSecurityPolicy; import io.dataease.datasource.type.*; import io.dataease.exception.DEException; import io.dataease.extensions.datasource.dto.*; @@ -59,6 +61,8 @@ public class CalciteProvider extends Provider { @Resource protected CoreDatasourceMapper coreDatasourceMapper; @Resource + protected CoreDriverMapper coreDriverMapper; + @Resource private EngineManage engineManage; protected ExtendedJdbcClassLoader extendedJdbcClassLoader; private Map customJdbcClassLoaders = new HashMap<>(); @@ -407,44 +411,11 @@ public class CalciteProvider extends Provider { @Override public ConnectionObj getConnection(DatasourceDTO coreDatasource) throws Exception { ConnectionObj connectionObj = new ConnectionObj(); - DatasourceConfiguration configuration = null; DatasourceConfiguration.DatasourceType datasourceType = DatasourceConfiguration.DatasourceType.valueOf(coreDatasource.getType()); - switch (datasourceType) { - case mysql: - case mongo: - case StarRocks: - case doris: - case TiDB: - case mariadb: - configuration = JsonUtil.parseObject(coreDatasource.getConfiguration(), Mysql.class); - break; - case impala: - configuration = JsonUtil.parseObject(coreDatasource.getConfiguration(), Impala.class); - break; - case sqlServer: - configuration = JsonUtil.parseObject(coreDatasource.getConfiguration(), Sqlserver.class); - break; - case oracle: - configuration = JsonUtil.parseObject(coreDatasource.getConfiguration(), Oracle.class); - break; - case db2: - configuration = JsonUtil.parseObject(coreDatasource.getConfiguration(), Db2.class); - break; - case pg: - configuration = JsonUtil.parseObject(coreDatasource.getConfiguration(), Pg.class); - break; - case redshift: - configuration = JsonUtil.parseObject(coreDatasource.getConfiguration(), Redshift.class); - break; - case h2: - configuration = JsonUtil.parseObject(coreDatasource.getConfiguration(), H2.class); - break; - case ck: - configuration = JsonUtil.parseObject(coreDatasource.getConfiguration(), CK.class); - break; - default: - configuration = JsonUtil.parseObject(coreDatasource.getConfiguration(), Mysql.class); - } + DatasourceConfiguration configuration = parseDatasourceConfiguration(coreDatasource.getConfiguration(), datasourceType); + CoreDriver customDriver = resolveCustomDriver(coreDatasource.getType(), configuration.getCustomDriver()); + String driverClassName = JdbcUrlSecurityPolicy.resolveDriverClass(coreDatasource.getType(), configuration.getDriver(), configuration.getCustomDriver(), customDriver); + configuration.setDriver(driverClassName); startSshSession(configuration, connectionObj, null); Properties props = new Properties(); if (StringUtils.isNotBlank(configuration.getUsername())) { @@ -453,8 +424,7 @@ public class CalciteProvider extends Provider { if (StringUtils.isNotBlank(configuration.getPassword())) { props.setProperty("password", configuration.getPassword()); } - String driverClassName = configuration.getDriver(); - ExtendedJdbcClassLoader jdbcClassLoader = extendedJdbcClassLoader; + ExtendedJdbcClassLoader jdbcClassLoader = JdbcUrlSecurityPolicy.isDefaultCustomDriver(configuration.getCustomDriver()) ? extendedJdbcClassLoader : getCustomJdbcClassLoader(customDriver); Connection conn = null; try { Driver driverClass = (Driver) jdbcClassLoader.loadClass(driverClassName).newInstance(); @@ -467,6 +437,40 @@ public class CalciteProvider extends Provider { return connectionObj; } + private DatasourceConfiguration parseDatasourceConfiguration(String config, DatasourceConfiguration.DatasourceType datasourceType) { + return switch (datasourceType) { + case mysql, StarRocks, doris, TiDB, mariadb -> JsonUtil.parseObject(config, Mysql.class); + case mongo -> JsonUtil.parseObject(config, Mongo.class); + case impala -> JsonUtil.parseObject(config, Impala.class); + case sqlServer -> JsonUtil.parseObject(config, Sqlserver.class); + case oracle -> JsonUtil.parseObject(config, Oracle.class); + case db2 -> JsonUtil.parseObject(config, Db2.class); + case pg -> JsonUtil.parseObject(config, Pg.class); + case redshift -> JsonUtil.parseObject(config, Redshift.class); + case h2 -> JsonUtil.parseObject(config, H2.class); + case ck -> JsonUtil.parseObject(config, CK.class); + default -> JsonUtil.parseObject(config, Mysql.class); + }; + } + + private CoreDriver resolveCustomDriver(String datasourceType, String customDriver) { + if (JdbcUrlSecurityPolicy.isDefaultCustomDriver(customDriver)) { + return null; + } + Long customDriverId; + try { + customDriverId = Long.valueOf(customDriver); + } catch (NumberFormatException e) { + DEException.throwException("invalid driver"); + return null; + } + CoreDriver coreDriver = coreDriverMapper.selectById(customDriverId); + if (coreDriver == null || !StringUtils.equalsIgnoreCase(coreDriver.getType(), datasourceType)) { + DEException.throwException("invalid driver"); + } + return coreDriver; + } + private DatasetTableDTO getTableDesc(DatasourceRequest datasourceRequest, ResultSet resultSet) throws SQLException { DatasetTableDTO tableDesc = new DatasetTableDTO(); tableDesc.setDatasourceId(datasourceRequest.getDatasource().getId()); diff --git a/core/core-backend/src/main/java/io/dataease/datasource/security/JdbcUrlSecurityPolicy.java b/core/core-backend/src/main/java/io/dataease/datasource/security/JdbcUrlSecurityPolicy.java new file mode 100644 index 0000000000..c69e0c127b --- /dev/null +++ b/core/core-backend/src/main/java/io/dataease/datasource/security/JdbcUrlSecurityPolicy.java @@ -0,0 +1,175 @@ +package io.dataease.datasource.security; + +import io.dataease.datasource.dao.auto.entity.CoreDriver; +import io.dataease.exception.DEException; +import org.apache.commons.lang3.StringUtils; + +import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; +import java.text.Normalizer; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + +public final class JdbcUrlSecurityPolicy { + + private static final String DEFAULT_CUSTOM_DRIVER = "default"; + + private static final Map JDBC_PREFIXES = Map.ofEntries( + Map.entry("mysql", "jdbc:mysql"), + Map.entry("mongo", "jdbc:mysql"), + Map.entry("mariadb", "jdbc:mysql"), + Map.entry("starrocks", "jdbc:mysql"), + Map.entry("doris", "jdbc:mysql"), + Map.entry("tidb", "jdbc:mysql"), + Map.entry("impala", "jdbc:impala"), + Map.entry("sqlserver", "jdbc:sqlserver"), + Map.entry("oracle", "jdbc:oracle"), + Map.entry("db2", "jdbc:db2"), + Map.entry("pg", "jdbc:postgresql"), + Map.entry("redshift", "jdbc:redshift"), + Map.entry("h2", "jdbc:h2"), + Map.entry("ck", "jdbc:clickhouse"), + Map.entry("sqlite", "jdbc:sqlite:") + ); + + private static final Map DEFAULT_DRIVERS = Map.ofEntries( + Map.entry("mysql", "com.mysql.cj.jdbc.Driver"), + Map.entry("mongo", "com.mysql.cj.jdbc.Driver"), + Map.entry("mariadb", "com.mysql.cj.jdbc.Driver"), + Map.entry("starrocks", "com.mysql.cj.jdbc.Driver"), + Map.entry("doris", "com.mysql.cj.jdbc.Driver"), + Map.entry("tidb", "com.mysql.cj.jdbc.Driver"), + Map.entry("impala", "com.cloudera.impala.jdbc.Driver"), + Map.entry("sqlserver", "com.microsoft.sqlserver.jdbc.SQLServerDriver"), + Map.entry("oracle", "oracle.jdbc.driver.OracleDriver"), + Map.entry("db2", "com.ibm.db2.jcc.DB2Driver"), + Map.entry("pg", "org.postgresql.Driver"), + Map.entry("redshift", "com.amazon.redshift.jdbc42.Driver"), + Map.entry("h2", "org.h2.Driver"), + Map.entry("ck", "com.clickhouse.jdbc.ClickHouseDriver"), + Map.entry("sqlite", "org.sqlite.JDBC") + ); + + private static final Set COMMON_DANGEROUS_FRAGMENTS = Set.of( + "jndi:", + "rmi:", + "ldap:", + "ldaps:", + "dns:", + "file:", + "ftp:", + "nis:", + "corba:", + "corbaname", + "iiop", + "iiopname", + "java.naming.factory.initial", + "java.naming.provider.url", + "java.naming.factory.object", + "java.naming.factory.state", + "autodeserialize", + "queryinterceptors", + "statementinterceptors", + "detectcustomcollations", + "connectionproperties", + "initsql" + ); + + private static final Map> TYPE_DANGEROUS_FRAGMENTS = Map.ofEntries( + Map.entry("mysql", Set.of("maxallowedpacket", "allowloadlocalinfile", "allowurlinlocalinfile", "allowloadlocalinfileinpath", "allowmultiqueries")), + Map.entry("mongo", Set.of()), + Map.entry("mariadb", Set.of("maxallowedpacket", "allowloadlocalinfile", "allowurlinlocalinfile", "allowloadlocalinfileinpath", "allowmultiqueries")), + Map.entry("starrocks", Set.of("maxallowedpacket", "allowloadlocalinfile", "allowurlinlocalinfile", "allowloadlocalinfileinpath", "allowmultiqueries")), + Map.entry("doris", Set.of("maxallowedpacket", "allowloadlocalinfile", "allowurlinlocalinfile", "allowloadlocalinfileinpath", "allowmultiqueries")), + Map.entry("tidb", Set.of("maxallowedpacket", "allowloadlocalinfile", "allowurlinlocalinfile", "allowloadlocalinfileinpath", "allowmultiqueries")), + Map.entry("impala", Set.of("krbjaasfile", "krb5.conf")), + Map.entry("sqlserver", Set.of()), + Map.entry("oracle", Set.of()), + Map.entry("db2", Set.of()), + Map.entry("pg", Set.of("socketfactory", "socketfactoryarg", "sslfactory", "sslhostnameverifier", "sslpasswordcallback", "authenticationpluginclassname")), + Map.entry("redshift", Set.of("socketfactory", "socketfactoryarg", "sslfactory", "sslhostnameverifier", "sslpasswordcallback", "authenticationpluginclassname", "inifile")), + Map.entry("h2", Set.of("init=", "runscript")), + Map.entry("ck", Set.of()) + ); + + private JdbcUrlSecurityPolicy() { + } + + public static String validate(String type, String driver, String jdbcUrl, String extraParams) { + if (StringUtils.isBlank(jdbcUrl)) { + DEException.throwException("Illegal jdbcUrl: " + jdbcUrl); + } + String normalizedType = normalizeType(type); + String normalizedUrl = canonicalize(jdbcUrl); + String normalizedExtraParams = canonicalize(extraParams); + String expectedPrefix = JDBC_PREFIXES.get(normalizedType); + if (StringUtils.isBlank(expectedPrefix) || !normalizedUrl.startsWith(expectedPrefix)) { + DEException.throwException("Illegal jdbcUrl: " + jdbcUrl); + } + Set dangerousFragments = new LinkedHashSet<>(COMMON_DANGEROUS_FRAGMENTS); + dangerousFragments.addAll(TYPE_DANGEROUS_FRAGMENTS.getOrDefault(normalizedType, Set.of())); + for (String fragment : dangerousFragments) { + if (normalizedUrl.contains(fragment) || normalizedExtraParams.contains(fragment)) { + DEException.throwException("Illegal parameter: " + fragment); + } + } + return jdbcUrl; + } + + public static String trustedDriverClass(String type) { + String driverClass = DEFAULT_DRIVERS.get(normalizeType(type)); + if (StringUtils.isBlank(driverClass)) { + DEException.throwException("invalid driver"); + } + return driverClass; + } + + public static String resolveDriverClass(String type, String requestedDriverClass, String customDriver, CoreDriver registeredDriver) { + if (!isDefaultCustomDriver(customDriver)) { + if (registeredDriver == null + || StringUtils.isBlank(registeredDriver.getDriverClass()) + || !StringUtils.equalsIgnoreCase(normalizeType(type), normalizeType(registeredDriver.getType()))) { + DEException.throwException("invalid driver"); + } + return registeredDriver.getDriverClass(); + } + String trustedDriverClass = trustedDriverClass(type); + if (StringUtils.isNotBlank(requestedDriverClass) && !StringUtils.equalsIgnoreCase(requestedDriverClass, trustedDriverClass)) { + DEException.throwException("invalid driver"); + } + return trustedDriverClass; + } + + public static boolean isDefaultCustomDriver(String customDriver) { + return StringUtils.isBlank(customDriver) || StringUtils.equalsIgnoreCase(customDriver, DEFAULT_CUSTOM_DRIVER); + } + + private static String canonicalize(String value) { + if (StringUtils.isBlank(value)) { + return ""; + } + String normalized = value; + for (int i = 0; i < 3; i++) { + try { + String decoded = URLDecoder.decode(normalized, StandardCharsets.UTF_8); + if (StringUtils.equals(decoded, normalized)) { + normalized = decoded; + break; + } + normalized = decoded; + } catch (IllegalArgumentException e) { + break; + } + } + normalized = Normalizer.normalize(normalized, Normalizer.Form.NFKC); + normalized = normalized.replace("\\", ""); + return normalized.toLowerCase(Locale.ROOT); + } + + private static String normalizeType(String type) { + return StringUtils.defaultString(type).toLowerCase(Locale.ROOT); + } +} diff --git a/core/core-backend/src/main/java/io/dataease/datasource/type/CK.java b/core/core-backend/src/main/java/io/dataease/datasource/type/CK.java index 9c2f255ec3..6f4f7f8e29 100644 --- a/core/core-backend/src/main/java/io/dataease/datasource/type/CK.java +++ b/core/core-backend/src/main/java/io/dataease/datasource/type/CK.java @@ -1,6 +1,6 @@ package io.dataease.datasource.type; -import com.fasterxml.jackson.annotation.JsonIgnore; +import io.dataease.datasource.security.JdbcUrlSecurityPolicy; import io.dataease.exception.DEException; import io.dataease.extensions.datasource.vo.DatasourceConfiguration; import lombok.Data; @@ -15,8 +15,6 @@ import java.nio.file.Paths; import java.nio.file.StandardOpenOption; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; -import java.util.Arrays; -import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Pattern; @@ -32,16 +30,9 @@ public class CK extends DatasourceConfiguration { private String sslCert; private String sslKey; - @JsonIgnore - private List ILLEGAL_PARAMETERS = Arrays.asList("jndi:", "rmi:", "ldap:", "ldaps:", "dns:", "nis:", "corba:", - "java.naming.factory.initial", "java.naming.provider.url"); - public String getJdbc() { String jdbcUrl; if (StringUtils.isNotEmpty(getUrlType()) && !getUrlType().equalsIgnoreCase("hostName")) { - if (!getJdbcUrl().startsWith("jdbc:clickhouse")) { - DEException.throwException("Illegal jdbcUrl: " + getJdbcUrl()); - } jdbcUrl = getJdbcUrl(); } else { StringBuilder builder = new StringBuilder(); @@ -76,8 +67,7 @@ public class CK extends DatasourceConfiguration { jdbcUrl = appendCertParam(jdbcUrl, "sslCert", sslCert, "cert"); jdbcUrl = appendCertParam(jdbcUrl, "sslKey", sslKey, "key"); } - checkIllegalParameters(jdbcUrl); - return jdbcUrl; + return JdbcUrlSecurityPolicy.validate("ck", getDriver(), jdbcUrl, getExtraParams()); } private String appendCertParam(String jdbcUrl, String paramName, String certContent, String filePrefix) { @@ -135,14 +125,4 @@ public class CK extends DatasourceConfiguration { private boolean containsParam(String jdbcUrl, String paramName) { return Pattern.compile("(?i)([?&])" + Pattern.quote(paramName) + "=").matcher(jdbcUrl).find(); } - - private void checkIllegalParameters(String jdbcUrl) { - String lowerUrl = jdbcUrl.toLowerCase(); - for (String illegalParam : ILLEGAL_PARAMETERS) { - if (lowerUrl.contains(illegalParam.toLowerCase())) { - throw new SecurityException("Illegal parameter detected in JDBC URL: " + illegalParam); - } - } - } - } diff --git a/core/core-backend/src/main/java/io/dataease/datasource/type/Db2.java b/core/core-backend/src/main/java/io/dataease/datasource/type/Db2.java index cd5a09a5ab..9c88d3b3db 100644 --- a/core/core-backend/src/main/java/io/dataease/datasource/type/Db2.java +++ b/core/core-backend/src/main/java/io/dataease/datasource/type/Db2.java @@ -1,44 +1,22 @@ package io.dataease.datasource.type; -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.dataease.exception.DEException; +import io.dataease.datasource.security.JdbcUrlSecurityPolicy; import io.dataease.extensions.datasource.vo.DatasourceConfiguration; import lombok.Data; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; -import java.util.Arrays; -import java.util.List; - @Data @Component("db2") public class Db2 extends DatasourceConfiguration { private String driver = "com.ibm.db2.jcc.DB2Driver"; private String extraParams = ""; - @JsonIgnore - private List illegalParameters = Arrays.asList( - // 原有参数(如RMI相关) - "java.naming.factory.initial", "java.naming.provider.url", "rmi", - // 新增:LDAP协议及相关危险参数 - "ldap://", "ldaps://", "java.naming.factory.object", "java.naming.factory.state", - // 其他JDBC危险参数 - "autoDeserialize", "connectionProperties", "initSQL", "dns", "file", "ftp", "iiop", "corbaname", "iiopname" - ); public String getJdbc() { - if (StringUtils.isNoneEmpty(getUrlType()) && !getUrlType().equalsIgnoreCase("hostName")) { - for (String illegalParameter : illegalParameters) { - if (getJdbcUrl().toLowerCase().contains(illegalParameter.toLowerCase())) { - DEException.throwException("Illegal parameter: " + illegalParameter); - } - } - if (!getJdbcUrl().startsWith("jdbc:db2")) { - DEException.throwException("Illegal jdbcUrl: " + getJdbcUrl()); - } - return getJdbcUrl(); - } String url = ""; - if (StringUtils.isEmpty(extraParams.trim())) { + if (StringUtils.isNoneEmpty(getUrlType()) && !getUrlType().equalsIgnoreCase("hostName")) { + url = getJdbcUrl(); + } else if (StringUtils.isEmpty(extraParams.trim())) { if (StringUtils.isEmpty(getSchema())) { url = "jdbc:db2://HOSTNAME:PORT/DATABASE" .replace("HOSTNAME", getLHost().trim()) @@ -58,11 +36,6 @@ public class Db2 extends DatasourceConfiguration { .replace("DATABASE", getDataBase().trim()) .replace("EXTRA_PARAMS", getExtraParams().trim()); } - for (String illegalParameter : illegalParameters) { - if (url.toLowerCase().contains(illegalParameter.toLowerCase())) { - DEException.throwException("Illegal parameter: " + illegalParameter); - } - } - return url; + return JdbcUrlSecurityPolicy.validate("db2", getDriver(), url, getExtraParams()); } } diff --git a/core/core-backend/src/main/java/io/dataease/datasource/type/H2.java b/core/core-backend/src/main/java/io/dataease/datasource/type/H2.java index fdc5bf2e4d..f4e22ccf1f 100644 --- a/core/core-backend/src/main/java/io/dataease/datasource/type/H2.java +++ b/core/core-backend/src/main/java/io/dataease/datasource/type/H2.java @@ -1,6 +1,6 @@ package io.dataease.datasource.type; -import com.fasterxml.jackson.annotation.JsonIgnore; +import io.dataease.datasource.security.JdbcUrlSecurityPolicy; import io.dataease.exception.DEException; import io.dataease.extensions.datasource.vo.DatasourceConfiguration; import lombok.Data; @@ -8,10 +8,6 @@ import lombok.EqualsAndHashCode; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; -import java.util.Arrays; -import java.util.List; -import java.util.Locale; - @EqualsAndHashCode(callSuper = true) @Data @Component("h2") @@ -19,20 +15,11 @@ public class H2 extends DatasourceConfiguration { private String driver = "org.h2.Driver"; public String getJdbc() { - for (String illegalParameter : getH2IllegalParameters()) { - if (jdbc.toUpperCase(Locale.ENGLISH).replace("\\", "").contains(illegalParameter)) { - DEException.throwException("Has illegal parameter: " + jdbc); - } - } - if (StringUtils.isNotEmpty(jdbc) && !jdbc.startsWith("jdbc:h2")) { + String jdbcUrl = StringUtils.defaultIfBlank(jdbc, getJdbcUrl()); + if (StringUtils.isBlank(jdbcUrl)) { DEException.throwException("Illegal jdbcUrl: " + jdbc); } - return jdbc; - } - - @JsonIgnore - private List getH2IllegalParameters() { - return Arrays.asList("INIT", "RUNSCRIPT"); + return JdbcUrlSecurityPolicy.validate("h2", getDriver(), jdbcUrl, getExtraParams()); } } diff --git a/core/core-backend/src/main/java/io/dataease/datasource/type/Impala.java b/core/core-backend/src/main/java/io/dataease/datasource/type/Impala.java index d63078de96..ba6d313754 100644 --- a/core/core-backend/src/main/java/io/dataease/datasource/type/Impala.java +++ b/core/core-backend/src/main/java/io/dataease/datasource/type/Impala.java @@ -1,15 +1,11 @@ package io.dataease.datasource.type; -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.dataease.exception.DEException; +import io.dataease.datasource.security.JdbcUrlSecurityPolicy; import io.dataease.extensions.datasource.vo.DatasourceConfiguration; import lombok.Data; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; -import java.net.URLDecoder; -import java.util.Arrays; -import java.util.List; import java.util.regex.Pattern; @Data @@ -17,34 +13,12 @@ import java.util.regex.Pattern; public class Impala extends DatasourceConfiguration { private String driver = "com.cloudera.impala.jdbc.Driver"; private String extraParams = ""; - @JsonIgnore - private List illegalParameters = Arrays.asList( - // 原有非法参数 - "autoDeserialize", "queryInterceptors", "statementInterceptors", "detectCustomCollations", - // 新增:Kerberos认证相关危险参数(漏洞利用核心参数) - "krbJAASFile", "KrbJAASFile", "krb5.conf", "Krb5Conf", - // 新增:JDNI/反序列化相关危险参数 - "jndi", "JNDI", "java.naming.factory.initial", "java.naming.provider.url", - // 新增:其他JDBC危险参数 - "connectionProperties", "ConnectionProperties", "initSQL", "InitSQL" - ); - private List showTableSqls = Arrays.asList("show tables"); public String getJdbc() { - if (StringUtils.isNoneEmpty(getUrlType()) && !getUrlType().equalsIgnoreCase("hostName")) { - for (String illegalParameter : illegalParameters) { - if (URLDecoder.decode(getJdbcUrl()).toLowerCase().contains(illegalParameter.toLowerCase()) || URLDecoder.decode(getExtraParams()).contains(illegalParameter.toLowerCase())) { - DEException.throwException("Illegal parameter: " + illegalParameter); - } - } - - if (!getJdbcUrl().startsWith("jdbc:impala")) { - DEException.throwException("Illegal jdbcUrl: " + getJdbcUrl()); - } - return getJdbcUrl(); - } String jdbcUrl = ""; - if (StringUtils.isEmpty(extraParams.trim())) { + if (StringUtils.isNoneEmpty(getUrlType()) && !getUrlType().equalsIgnoreCase("hostName")) { + jdbcUrl = getJdbcUrl(); + } else if (StringUtils.isEmpty(extraParams.trim())) { jdbcUrl = "jdbc:impala://HOSTNAME:PORT/DATABASE" .replace("HOSTNAME", getLHost().trim()) .replace("PORT", getLPort().toString().trim()) @@ -56,12 +30,7 @@ public class Impala extends DatasourceConfiguration { .replace("DATABASE", getDataBase().trim()) .replace("EXTRA_PARAMS", getExtraParams().trim()); } - for (String illegalParameter : illegalParameters) { - if (URLDecoder.decode(jdbcUrl).toLowerCase().contains(illegalParameter.toLowerCase()) || URLDecoder.decode(jdbcUrl).contains(illegalParameter.toLowerCase())) { - DEException.throwException("Illegal parameter: " + illegalParameter); - } - } - return jdbcUrl; + return JdbcUrlSecurityPolicy.validate("impala", getDriver(), jdbcUrl, getExtraParams()); } private static final Pattern DB_NAME_PATTERN = Pattern.compile("//[^/]+/([^?;]+)"); diff --git a/core/core-backend/src/main/java/io/dataease/datasource/type/Mongo.java b/core/core-backend/src/main/java/io/dataease/datasource/type/Mongo.java index f8445841fe..53d67adc8d 100644 --- a/core/core-backend/src/main/java/io/dataease/datasource/type/Mongo.java +++ b/core/core-backend/src/main/java/io/dataease/datasource/type/Mongo.java @@ -1,33 +1,22 @@ package io.dataease.datasource.type; -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.dataease.exception.DEException; +import io.dataease.datasource.security.JdbcUrlSecurityPolicy; import io.dataease.extensions.datasource.vo.DatasourceConfiguration; import lombok.Data; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; -import java.util.Arrays; -import java.util.List; - @Data @Component("mongo") public class Mongo extends DatasourceConfiguration { private String driver = "com.mysql.cj.jdbc.Driver"; private String extraParams = "characterEncoding=UTF-8&connectTimeout=5000&useSSL=false&allowPublicKeyRetrieval=true&zeroDateTimeBehavior=convertToNull"; - @JsonIgnore - private List illegalParameters = Arrays.asList("autoDeserialize", "queryInterceptors", "statementInterceptors", "detectCustomCollations"); - private List showTableSqls = Arrays.asList("show tables"); public String getJdbc() { - if (StringUtils.isNoneEmpty(getUrlType()) && !getUrlType().equalsIgnoreCase("hostName")) { - if (!getJdbcUrl().startsWith("jdbc:mysql")) { - DEException.throwException("Illegal jdbcUrl: " + getJdbcUrl()); - } - return getJdbcUrl(); - } String jdbcUrl = ""; - if (StringUtils.isEmpty(extraParams.trim())) { + if (StringUtils.isNoneEmpty(getUrlType()) && !getUrlType().equalsIgnoreCase("hostName")) { + jdbcUrl = getJdbcUrl(); + } else if (StringUtils.isEmpty(extraParams.trim())) { jdbcUrl = "jdbc:mysql://HOSTNAME:PORT/DATABASE" .replace("HOSTNAME", getLHost().trim()) .replace("PORT", getLPort().toString().trim()) @@ -39,11 +28,6 @@ public class Mongo extends DatasourceConfiguration { .replace("DATABASE", getDataBase().trim()) .replace("EXTRA_PARAMS", getExtraParams().trim()); } - for (String illegalParameter : illegalParameters) { - if (jdbcUrl.contains(illegalParameter)) { - throw new RuntimeException("Illegal parameter: " + illegalParameter); - } - } - return jdbcUrl; + return JdbcUrlSecurityPolicy.validate("mongo", getDriver(), jdbcUrl, getExtraParams()); } } diff --git a/core/core-backend/src/main/java/io/dataease/datasource/type/Mysql.java b/core/core-backend/src/main/java/io/dataease/datasource/type/Mysql.java index 12bd0fbbcc..77e18e5a25 100644 --- a/core/core-backend/src/main/java/io/dataease/datasource/type/Mysql.java +++ b/core/core-backend/src/main/java/io/dataease/datasource/type/Mysql.java @@ -1,39 +1,22 @@ package io.dataease.datasource.type; -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.dataease.exception.DEException; +import io.dataease.datasource.security.JdbcUrlSecurityPolicy; import io.dataease.extensions.datasource.vo.DatasourceConfiguration; import lombok.Data; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; -import java.net.URLDecoder; -import java.util.Arrays; -import java.util.List; - @Data @Component("mysql") public class Mysql extends DatasourceConfiguration { private String driver = "com.mysql.cj.jdbc.Driver"; private String extraParams = "characterEncoding=UTF-8&connectTimeout=5000&useSSL=false&allowPublicKeyRetrieval=true&zeroDateTimeBehavior=convertToNull"; - @JsonIgnore - private List illegalParameters = Arrays.asList("maxAllowedPacket", "autoDeserialize", "queryInterceptors", "statementInterceptors", "detectCustomCollations", "allowloadlocalinfile", "allowUrlInLocalInfile", "allowLoadLocalInfileInPath", "allowMultiQueries"); - private List showTableSqls = Arrays.asList("show tables"); public String getJdbc() { - if (StringUtils.isNoneEmpty(getUrlType()) && !getUrlType().equalsIgnoreCase("hostName")) { - for (String illegalParameter : illegalParameters) { - if (URLDecoder.decode(getJdbcUrl()).toLowerCase().contains(illegalParameter.toLowerCase()) || URLDecoder.decode(getExtraParams()).contains(illegalParameter.toLowerCase())) { - DEException.throwException("Illegal parameter: " + illegalParameter); - } - } - if (!getJdbcUrl().startsWith("jdbc:mysql")) { - DEException.throwException("Illegal jdbcUrl: " + getJdbcUrl()); - } - return getJdbcUrl(); - } String jdbcUrl = ""; - if (StringUtils.isEmpty(extraParams.trim())) { + if (StringUtils.isNoneEmpty(getUrlType()) && !getUrlType().equalsIgnoreCase("hostName")) { + jdbcUrl = getJdbcUrl(); + } else if (StringUtils.isEmpty(extraParams.trim())) { jdbcUrl = "jdbc:mysql://HOSTNAME:PORT/DATABASE" .replace("HOSTNAME", getLHost().trim()) .replace("PORT", getLPort().toString().trim()) @@ -45,11 +28,6 @@ public class Mysql extends DatasourceConfiguration { .replace("DATABASE", getDataBase().trim()) .replace("EXTRA_PARAMS", getExtraParams().trim()); } - for (String illegalParameter : illegalParameters) { - if (URLDecoder.decode(jdbcUrl).toLowerCase().contains(illegalParameter.toLowerCase()) || URLDecoder.decode(jdbcUrl).contains(illegalParameter.toLowerCase())) { - DEException.throwException("Illegal parameter: " + illegalParameter); - } - } - return jdbcUrl; + return JdbcUrlSecurityPolicy.validate("mysql", getDriver(), jdbcUrl, getExtraParams()); } } diff --git a/core/core-backend/src/main/java/io/dataease/datasource/type/Oracle.java b/core/core-backend/src/main/java/io/dataease/datasource/type/Oracle.java index ae26ce10cd..796bff93a4 100644 --- a/core/core-backend/src/main/java/io/dataease/datasource/type/Oracle.java +++ b/core/core-backend/src/main/java/io/dataease/datasource/type/Oracle.java @@ -1,13 +1,11 @@ package io.dataease.datasource.type; -import io.dataease.exception.DEException; +import io.dataease.datasource.security.JdbcUrlSecurityPolicy; import io.dataease.extensions.datasource.vo.DatasourceConfiguration; import lombok.Data; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; -import java.util.Arrays; -import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -16,41 +14,23 @@ import java.util.regex.Pattern; public class Oracle extends DatasourceConfiguration { private String driver = "oracle.jdbc.driver.OracleDriver"; private String extraParams = ""; - private List getOracleIllegalParameters() { - return Arrays.asList( - // 原有参数(如RMI相关) - "java.naming.factory.initial", "java.naming.provider.url", "rmi", - // 新增:LDAP协议及相关危险参数 - "ldap://", "ldaps://", "java.naming.factory.object", "java.naming.factory.state", - // 其他JDBC危险参数 - "autoDeserialize", "connectionProperties", "initSQL", "dns", "file", "ftp" - ); - } - public String getJdbc() { + String jdbcUrl; if(StringUtils.isNoneEmpty(getUrlType()) && !getUrlType().equalsIgnoreCase("hostName")){ - if (!getJdbcUrl().startsWith("jdbc:oracle")) { - DEException.throwException("Illegal jdbcUrl: " + getJdbcUrl()); - } - for (String illegalParameter : getOracleIllegalParameters()) { - if (getJdbcUrl().toLowerCase().contains(illegalParameter.toLowerCase())) { - DEException.throwException("Illegal jdbcUrl: " + illegalParameter); - } - } - return getJdbcUrl(); - } - if (StringUtils.isNotEmpty(getConnectionType()) && getConnectionType().equalsIgnoreCase("serviceName")) { - return "jdbc:oracle:thin:@HOSTNAME:PORT/DATABASE" + jdbcUrl = getJdbcUrl(); + } else if (StringUtils.isNotEmpty(getConnectionType()) && getConnectionType().equalsIgnoreCase("serviceName")) { + jdbcUrl = "jdbc:oracle:thin:@HOSTNAME:PORT/DATABASE" .replace("HOSTNAME", getLHost().trim()) .replace("PORT", getLPort().toString().trim()) .replace("DATABASE", getDataBase().trim()); - }else { - return "jdbc:oracle:thin:@HOSTNAME:PORT:DATABASE" + } else { + jdbcUrl = "jdbc:oracle:thin:@HOSTNAME:PORT:DATABASE" .replace("HOSTNAME", getLHost().trim()) .replace("PORT", getLPort().toString().trim()) .replace("DATABASE", getDataBase().trim()); } + return JdbcUrlSecurityPolicy.validate("oracle", getDriver(), jdbcUrl, getExtraParams()); } private static final Pattern SERVICE_PATTERN = Pattern.compile(":@//[^/]+/([^?]+)"); diff --git a/core/core-backend/src/main/java/io/dataease/datasource/type/Pg.java b/core/core-backend/src/main/java/io/dataease/datasource/type/Pg.java index 94220d124b..2b03503c69 100644 --- a/core/core-backend/src/main/java/io/dataease/datasource/type/Pg.java +++ b/core/core-backend/src/main/java/io/dataease/datasource/type/Pg.java @@ -1,15 +1,11 @@ package io.dataease.datasource.type; -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.dataease.exception.DEException; +import io.dataease.datasource.security.JdbcUrlSecurityPolicy; import io.dataease.extensions.datasource.vo.DatasourceConfiguration; import lombok.Data; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; -import java.net.URLDecoder; -import java.util.Arrays; -import java.util.List; import java.util.regex.Pattern; @Data @@ -17,23 +13,12 @@ import java.util.regex.Pattern; public class Pg extends DatasourceConfiguration { private String driver = "org.postgresql.Driver"; private String extraParams = ""; - @JsonIgnore - private List illegalParameters = Arrays.asList("socketFactory", "socketFactoryArg", "sslfactory", "sslhostnameverifier", "sslpasswordcallback", "authenticationPluginClassName"); public String getJdbc() { - if (StringUtils.isNoneEmpty(getUrlType()) && !getUrlType().equalsIgnoreCase("hostName")) { - for (String illegalParameter : illegalParameters) { - if (URLDecoder.decode(getJdbcUrl()).contains(illegalParameter)) { - DEException.throwException("Illegal parameter: " + illegalParameter); - } - } - if (!getJdbcUrl().startsWith("jdbc:postgresql")) { - DEException.throwException("Illegal jdbcUrl: " + getJdbcUrl()); - } - return getJdbcUrl(); - } String jdbcUrl = ""; - if (StringUtils.isEmpty(extraParams.trim())) { + if (StringUtils.isNoneEmpty(getUrlType()) && !getUrlType().equalsIgnoreCase("hostName")) { + jdbcUrl = getJdbcUrl(); + } else if (StringUtils.isEmpty(extraParams.trim())) { if (StringUtils.isEmpty(getSchema())) { jdbcUrl = "jdbc:postgresql://HOSTNAME:PORT/DATABASE" .replace("HOSTNAME", getLHost().trim()) @@ -54,12 +39,7 @@ public class Pg extends DatasourceConfiguration { .replace("EXTRA_PARAMS", getExtraParams().trim()); } - for (String illegalParameter : illegalParameters) { - if (URLDecoder.decode(jdbcUrl).toLowerCase().contains(illegalParameter.toLowerCase()) || URLDecoder.decode(jdbcUrl).contains(illegalParameter.toLowerCase())) { - DEException.throwException("Illegal parameter: " + illegalParameter); - } - } - return jdbcUrl; + return JdbcUrlSecurityPolicy.validate("pg", getDriver(), jdbcUrl, getExtraParams()); } private static final Pattern DB_NAME_PATTERN = Pattern.compile("//[^/]+/([^?]+)"); diff --git a/core/core-backend/src/main/java/io/dataease/datasource/type/Redshift.java b/core/core-backend/src/main/java/io/dataease/datasource/type/Redshift.java index dc7770c23c..f2234ccffb 100644 --- a/core/core-backend/src/main/java/io/dataease/datasource/type/Redshift.java +++ b/core/core-backend/src/main/java/io/dataease/datasource/type/Redshift.java @@ -1,50 +1,31 @@ package io.dataease.datasource.type; -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.dataease.exception.DEException; +import io.dataease.datasource.security.JdbcUrlSecurityPolicy; import io.dataease.extensions.datasource.vo.DatasourceConfiguration; import lombok.Data; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; -import java.net.URLDecoder; -import java.util.Arrays; -import java.util.List; import java.util.Map; import java.util.regex.Pattern; -import static java.awt.SystemColor.info; - @Data @Component("redshift") public class Redshift extends DatasourceConfiguration { private String driver = "com.amazon.redshift.jdbc42.Driver"; private String extraParams = ""; - @JsonIgnore - private List illegalParameters = Arrays.asList("socketFactory", "socketFactoryArg", "sslfactory", "sslhostnameverifier", "sslpasswordcallback", "authenticationPluginClassName", "IniFile"); public String getJdbc() { + String jdbcUrl; if (StringUtils.isNoneEmpty(getUrlType()) && !getUrlType().equalsIgnoreCase("hostName")) { - for (String illegalParameter : illegalParameters) { - if (URLDecoder.decode(getJdbcUrl()).contains(illegalParameter)) { - DEException.throwException("Illegal parameter: " + illegalParameter); - } - } - if (!getJdbcUrl().startsWith("jdbc:redshift")) { - DEException.throwException("Illegal jdbcUrl: " + getJdbcUrl()); - } - return getJdbcUrl(); + jdbcUrl = getJdbcUrl(); + } else { + jdbcUrl = "jdbc:redshift://HOSTNAME:PORT/DATABASE" + .replace("HOSTNAME", getLHost().trim()) + .replace("PORT", getLPort().toString().trim()) + .replace("DATABASE", getDataBase().trim()); } - String jdbcUrl = "jdbc:redshift://HOSTNAME:PORT/DATABASE" - .replace("HOSTNAME", getLHost().trim()) - .replace("PORT", getLPort().toString().trim()) - .replace("DATABASE", getDataBase().trim()); - for (String illegalParameter : illegalParameters) { - if (URLDecoder.decode(jdbcUrl).contains(illegalParameter)) { - DEException.throwException("Illegal parameter: " + illegalParameter); - } - } - return jdbcUrl; + return JdbcUrlSecurityPolicy.validate("redshift", getDriver(), jdbcUrl, getExtraParams()); } private static final Pattern DB_NAME_PATTERN = Pattern.compile("//[^/]+/([^?]+)"); diff --git a/core/core-backend/src/main/java/io/dataease/datasource/type/Sqlserver.java b/core/core-backend/src/main/java/io/dataease/datasource/type/Sqlserver.java index a940c6f580..980dab5337 100644 --- a/core/core-backend/src/main/java/io/dataease/datasource/type/Sqlserver.java +++ b/core/core-backend/src/main/java/io/dataease/datasource/type/Sqlserver.java @@ -1,15 +1,11 @@ package io.dataease.datasource.type; -import com.fasterxml.jackson.annotation.JsonIgnore; -import io.dataease.exception.DEException; +import io.dataease.datasource.security.JdbcUrlSecurityPolicy; import io.dataease.extensions.datasource.vo.DatasourceConfiguration; import lombok.Data; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Component; -import java.net.URLDecoder; -import java.util.Arrays; -import java.util.List; import java.util.regex.Pattern; @Data @@ -17,19 +13,12 @@ import java.util.regex.Pattern; public class Sqlserver extends DatasourceConfiguration { private String driver = "com.microsoft.sqlserver.jdbc.SQLServerDriver"; private String extraParams = ""; - @JsonIgnore - private List illegalParameters = Arrays.asList("autoDeserialize", "queryInterceptors", "statementInterceptors", "detectCustomCollations", "jndi:", "rmi:", "ldap:", "ldaps:", "java.naming.factory.initial"); - private List showTableSqls = Arrays.asList("show tables"); public String getJdbc() { - if (StringUtils.isNoneEmpty(getUrlType()) && !getUrlType().equalsIgnoreCase("hostName")) { - if (!getJdbcUrl().startsWith("jdbc:sqlserver")) { - DEException.throwException("Illegal jdbcUrl: " + getJdbcUrl()); - } - return getJdbcUrl(); - } String jdbcUrl = ""; - if (StringUtils.isEmpty(extraParams.trim())) { + if (StringUtils.isNoneEmpty(getUrlType()) && !getUrlType().equalsIgnoreCase("hostName")) { + jdbcUrl = getJdbcUrl(); + } else if (StringUtils.isEmpty(extraParams.trim())) { jdbcUrl = "jdbc:sqlserver://HOSTNAME:PORT;DatabaseName=DATABASE" .replace("HOSTNAME", getLHost().trim()) .replace("PORT", getLPort().toString().trim()) @@ -41,12 +30,7 @@ public class Sqlserver extends DatasourceConfiguration { .replace("DATABASE", getDataBase().trim()) .replace("EXTRA_PARAMS", getExtraParams().trim()); } - for (String illegalParameter : illegalParameters) { - if (URLDecoder.decode(jdbcUrl).toLowerCase().contains(illegalParameter.toLowerCase()) || URLDecoder.decode(jdbcUrl).contains(illegalParameter.toLowerCase())) { - DEException.throwException("Illegal parameter: " + illegalParameter); - } - } - return jdbcUrl; + return JdbcUrlSecurityPolicy.validate("sqlServer", getDriver(), jdbcUrl, getExtraParams()); } private static final Pattern DB_NAME_PATTERN = Pattern.compile(";databaseName=([^;]+)");