This is an automated email from the ASF dual-hosted git repository.

casionone pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/linkis.git


The following commit(s) were added to refs/heads/master by this push:
     new 44c38c4bfe #AI COMMIT# Fix JDBC URL encoding bypass and unsafe 
deserialization vulnerabilities (#5438)
44c38c4bfe is described below

commit 44c38c4bfe29f42a27acae27fcfd0676578b65c4
Author: aiceflower <[email protected]>
AuthorDate: Thu Jun 11 15:22:38 2026 +0800

    #AI COMMIT# Fix JDBC URL encoding bypass and unsafe deserialization 
vulnerabilities (#5438)
    
    1. SecurityUtils.checkParams: Replace single URL decode with while-loop
       decode (consistent with checkJdbcConnParams) to prevent double-encoding
       bypass that allows attackers to smuggle sensitive params like
       allowLoadLocalInfile past the blacklist.
    
    2. SqlConnection (4 files): Replace 
DriverManager.getConnection(url,user,pwd)
       with getConnection(baseUrl, Properties). Security params are set first
       via SecurityUtils.getMysqlSecurityParams() and cannot be overridden by
       user-supplied extra params, providing defense-in-depth against URL
       parameter injection.
    
    3. CryptoUtils.string2Object: Add resolveClass whitelist allowing only
       java.lang.String, blocking all gadget chain deserialization attacks
       (CWE-502) while maintaining backward compatibility since passwords
       are stored as String objects.
    
    Co-authored-by: Claude Opus 4.7 <[email protected]>
---
 .../apache/linkis/common/utils/SecurityUtils.java  | 12 ++++++++---
 .../query/service/mysql/SqlConnection.java         | 23 ++++++++++++---------
 .../query/service/starrocks/SqlConnection.java     | 23 ++++++++++++---------
 .../query/service/mysql/SqlConnection.java         | 24 +++++++++++++---------
 .../query/service/starrocks/SqlConnection.java     | 22 ++++++++++++--------
 .../datasourcemanager/common/util/CryptoUtils.java | 15 +++++++++++++-
 6 files changed, 76 insertions(+), 43 deletions(-)

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 6716fc9395..bddc2300b7 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 @@ public abstract class SecurityUtils {
       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<String, Object> 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 d35ae7f2a9..d907d08466 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.Closeable;
 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 class SqlConnection implements Closeable {
    */
   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<String, Object> 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 71f29c0d19..057d9b04d8 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.Closeable;
 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 class SqlConnection implements Closeable {
    */
   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<String, Object> 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 8c081e61fa..ae4605176e 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 class SqlConnection implements Closeable {
    */
   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<String, Object> 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 71f29c0d19..20bc8fd68d 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 class SqlConnection implements Closeable {
    */
   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<String, Object> 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 b60e2bd897..5e030620ee 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 org.apache.commons.lang3.StringUtils;
 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 class CryptoUtils {
   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();


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to