diff --git a/linkis-commons/linkis-common/src/main/java/org/apache/linkis/common/utils/SecurityUtils.java b/linkis-commons/linkis-common/src/main/java/org/apache/linkis/common/utils/SecurityUtils.java index 6716fc93950..bddc2300b7d 100644 --- a/linkis-commons/linkis-common/src/main/java/org/apache/linkis/common/utils/SecurityUtils.java +++ b/linkis-commons/linkis-common/src/main/java/org/apache/linkis/common/utils/SecurityUtils.java @@ -272,12 +272,18 @@ private static void checkParams(Map paramsMap) { return; } - // deal with url encode + // deal with url encode - loop until stable to prevent double-encoding bypass String paramUrl = parseParamsMapToMysqlParamUrl(paramsMap); try { - paramUrl = URLDecoder.decode(paramUrl, "UTF-8"); + while (paramUrl.contains("%")) { + String decodedParamUrl = URLDecoder.decode(paramUrl, "UTF-8"); + if (decodedParamUrl.equals(paramUrl)) { + break; + } + paramUrl = decodedParamUrl; + } } catch (UnsupportedEncodingException e) { - throw new LinkisSecurityException(35000, "mysql connection cul decode error: " + e); + throw new LinkisSecurityException(35000, "mysql connection url decode error: " + e); } Map newParamsMap = parseMysqlUrlParamsToMap(paramUrl); diff --git a/linkis-public-enhancements/linkis-datasource/linkis-datasource-manager/service/jdbc/src/main/java/org/apache/linkis/metadata/query/service/mysql/SqlConnection.java b/linkis-public-enhancements/linkis-datasource/linkis-datasource-manager/service/jdbc/src/main/java/org/apache/linkis/metadata/query/service/mysql/SqlConnection.java index d35ae7f2a95..d907d084662 100644 --- a/linkis-public-enhancements/linkis-datasource/linkis-datasource-manager/service/jdbc/src/main/java/org/apache/linkis/metadata/query/service/mysql/SqlConnection.java +++ b/linkis-public-enhancements/linkis-datasource/linkis-datasource-manager/service/jdbc/src/main/java/org/apache/linkis/metadata/query/service/mysql/SqlConnection.java @@ -26,7 +26,6 @@ import java.io.IOException; import java.sql.*; import java.util.*; -import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -214,19 +213,23 @@ public void close() throws IOException { */ private Connection getDBConnection(ConnectMessage connectMessage, String database) throws ClassNotFoundException, SQLException { - String extraParamString = - connectMessage.extraParams.entrySet().stream() - .map(e -> String.join("=", e.getKey(), String.valueOf(e.getValue()))) - .collect(Collectors.joining("&")); Class.forName(SQL_DRIVER_CLASS.getValue()); - String url = + String baseUrl = String.format( SQL_CONNECT_URL.getValue(), connectMessage.host, connectMessage.port, database); - if (!connectMessage.extraParams.isEmpty()) { - url += "?" + extraParamString; + + // Use Properties-based connection to enforce security params override. + // Per MySQL Connector/J docs, Properties override URL query params on conflict. + Properties props = SecurityUtils.getMysqlSecurityParams(); + props.setProperty("user", connectMessage.username); + props.setProperty("password", AESUtils.isDecryptByConf(connectMessage.password)); + for (Map.Entry entry : connectMessage.extraParams.entrySet()) { + if (!props.containsKey(entry.getKey())) { + props.setProperty(entry.getKey(), String.valueOf(entry.getValue())); + } } - return DriverManager.getConnection( - url, connectMessage.username, AESUtils.isDecryptByConf(connectMessage.password)); + LOG.info("jdbc connection url: {}", baseUrl); + return DriverManager.getConnection(baseUrl, props); } /** Connect message */ diff --git a/linkis-public-enhancements/linkis-datasource/linkis-datasource-manager/service/jdbc/src/main/java/org/apache/linkis/metadata/query/service/starrocks/SqlConnection.java b/linkis-public-enhancements/linkis-datasource/linkis-datasource-manager/service/jdbc/src/main/java/org/apache/linkis/metadata/query/service/starrocks/SqlConnection.java index 71f29c0d19e..057d9b04d88 100644 --- a/linkis-public-enhancements/linkis-datasource/linkis-datasource-manager/service/jdbc/src/main/java/org/apache/linkis/metadata/query/service/starrocks/SqlConnection.java +++ b/linkis-public-enhancements/linkis-datasource/linkis-datasource-manager/service/jdbc/src/main/java/org/apache/linkis/metadata/query/service/starrocks/SqlConnection.java @@ -26,7 +26,6 @@ import java.io.IOException; import java.sql.*; import java.util.*; -import java.util.stream.Collectors; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -206,19 +205,23 @@ public void close() throws IOException { */ private Connection getDBConnection(ConnectMessage connectMessage, String database) throws ClassNotFoundException, SQLException { - String extraParamString = - connectMessage.extraParams.entrySet().stream() - .map(e -> String.join("=", e.getKey(), String.valueOf(e.getValue()))) - .collect(Collectors.joining("&")); Class.forName(SQL_DRIVER_CLASS.getValue()); - String url = + String baseUrl = String.format( SQL_CONNECT_URL.getValue(), connectMessage.host, connectMessage.port, database); - if (!connectMessage.extraParams.isEmpty()) { - url += "?" + extraParamString; + + // Use Properties-based connection to enforce security params override. + // Per MySQL Connector/J docs, Properties override URL query params on conflict. + Properties props = SecurityUtils.getMysqlSecurityParams(); + props.setProperty("user", connectMessage.username); + props.setProperty("password", AESUtils.isDecryptByConf(connectMessage.password)); + for (Map.Entry entry : connectMessage.extraParams.entrySet()) { + if (!props.containsKey(entry.getKey())) { + props.setProperty(entry.getKey(), String.valueOf(entry.getValue())); + } } - return DriverManager.getConnection( - url, connectMessage.username, AESUtils.isDecryptByConf(connectMessage.password)); + LOG.info("starrocks jdbc connection url: {}", baseUrl); + return DriverManager.getConnection(baseUrl, props); } /** Connect message */ diff --git a/linkis-public-enhancements/linkis-datasource/linkis-metadata-query/service/jdbc/src/main/java/org/apache/linkis/metadata/query/service/mysql/SqlConnection.java b/linkis-public-enhancements/linkis-datasource/linkis-metadata-query/service/jdbc/src/main/java/org/apache/linkis/metadata/query/service/mysql/SqlConnection.java index 8c081e61fab..ae4605176e8 100644 --- a/linkis-public-enhancements/linkis-datasource/linkis-metadata-query/service/jdbc/src/main/java/org/apache/linkis/metadata/query/service/mysql/SqlConnection.java +++ b/linkis-public-enhancements/linkis-datasource/linkis-metadata-query/service/jdbc/src/main/java/org/apache/linkis/metadata/query/service/mysql/SqlConnection.java @@ -193,23 +193,27 @@ public void close() throws IOException { */ private Connection getDBConnection(ConnectMessage connectMessage, String database) throws ClassNotFoundException, SQLException { - String extraParamString = - connectMessage.extraParams.entrySet().stream() - .map(e -> String.join("=", e.getKey(), String.valueOf(e.getValue()))) - .collect(Collectors.joining("&")); Class.forName(SQL_DRIVER_CLASS.getValue()); - String url = + String baseUrl = String.format( SQL_CONNECT_URL.getValue(), connectMessage.host, connectMessage.port, database); // deal with empty database if (StringUtils.isBlank(database)) { - url = url.substring(0, url.length() - 1); + baseUrl = baseUrl.substring(0, baseUrl.length() - 1); } - if (!connectMessage.extraParams.isEmpty()) { - url += "?" + extraParamString; + + // Use Properties-based connection to enforce security params override. + // Per MySQL Connector/J docs, Properties override URL query params on conflict. + Properties props = SecurityUtils.getMysqlSecurityParams(); + props.setProperty("user", connectMessage.username); + props.setProperty("password", connectMessage.password); + for (Map.Entry entry : connectMessage.extraParams.entrySet()) { + if (!props.containsKey(entry.getKey())) { + props.setProperty(entry.getKey(), String.valueOf(entry.getValue())); + } } - LOG.info("jdbc connection url: {}", url); - return DriverManager.getConnection(url, connectMessage.username, connectMessage.password); + LOG.info("jdbc connection url: {}", baseUrl); + return DriverManager.getConnection(baseUrl, props); } /** Connect message */ diff --git a/linkis-public-enhancements/linkis-datasource/linkis-metadata-query/service/jdbc/src/main/java/org/apache/linkis/metadata/query/service/starrocks/SqlConnection.java b/linkis-public-enhancements/linkis-datasource/linkis-metadata-query/service/jdbc/src/main/java/org/apache/linkis/metadata/query/service/starrocks/SqlConnection.java index 71f29c0d19e..20bc8fd68d3 100644 --- a/linkis-public-enhancements/linkis-datasource/linkis-metadata-query/service/jdbc/src/main/java/org/apache/linkis/metadata/query/service/starrocks/SqlConnection.java +++ b/linkis-public-enhancements/linkis-datasource/linkis-metadata-query/service/jdbc/src/main/java/org/apache/linkis/metadata/query/service/starrocks/SqlConnection.java @@ -206,19 +206,23 @@ public void close() throws IOException { */ private Connection getDBConnection(ConnectMessage connectMessage, String database) throws ClassNotFoundException, SQLException { - String extraParamString = - connectMessage.extraParams.entrySet().stream() - .map(e -> String.join("=", e.getKey(), String.valueOf(e.getValue()))) - .collect(Collectors.joining("&")); Class.forName(SQL_DRIVER_CLASS.getValue()); - String url = + String baseUrl = String.format( SQL_CONNECT_URL.getValue(), connectMessage.host, connectMessage.port, database); - if (!connectMessage.extraParams.isEmpty()) { - url += "?" + extraParamString; + + // Use Properties-based connection to enforce security params override. + // Per MySQL Connector/J docs, Properties override URL query params on conflict. + Properties props = SecurityUtils.getMysqlSecurityParams(); + props.setProperty("user", connectMessage.username); + props.setProperty("password", AESUtils.isDecryptByConf(connectMessage.password)); + for (Map.Entry entry : connectMessage.extraParams.entrySet()) { + if (!props.containsKey(entry.getKey())) { + props.setProperty(entry.getKey(), String.valueOf(entry.getValue())); + } } - return DriverManager.getConnection( - url, connectMessage.username, AESUtils.isDecryptByConf(connectMessage.password)); + LOG.info("starrocks jdbc connection url: {}", baseUrl); + return DriverManager.getConnection(baseUrl, props); } /** Connect message */ diff --git a/linkis-public-enhancements/linkis-pes-common/src/main/java/org/apache/linkis/datasourcemanager/common/util/CryptoUtils.java b/linkis-public-enhancements/linkis-pes-common/src/main/java/org/apache/linkis/datasourcemanager/common/util/CryptoUtils.java index b60e2bd8970..5e030620eec 100644 --- a/linkis-public-enhancements/linkis-pes-common/src/main/java/org/apache/linkis/datasourcemanager/common/util/CryptoUtils.java +++ b/linkis-public-enhancements/linkis-pes-common/src/main/java/org/apache/linkis/datasourcemanager/common/util/CryptoUtils.java @@ -23,8 +23,10 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.InvalidClassException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.io.ObjectStreamClass; import java.io.Serializable; import java.security.MessageDigest; @@ -62,7 +64,18 @@ public static String object2String(Serializable o) { public static Object string2Object(String str) { try { ByteArrayInputStream bis = new ByteArrayInputStream(new Base64().decode(str.getBytes())); - ObjectInputStream ois = new ObjectInputStream(bis); + ObjectInputStream ois = + new ObjectInputStream(bis) { + @Override + protected Class resolveClass(ObjectStreamClass desc) + throws IOException, ClassNotFoundException { + if (!desc.getName().equals("java.lang.String")) { + throw new InvalidClassException( + "Unauthorized deserialization attempt", desc.getName()); + } + return super.resolveClass(desc); + } + }; Object o = ois.readObject(); bis.close(); ois.close();