You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@linkis.apache.org by ki...@apache.org on 2026/06/03 10:01:14 UTC

(linkis) branch fix/security-jdbc-deser-vuln created (now b0ab51b8b8)

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

aiceflower pushed a change to branch fix/security-jdbc-deser-vuln
in repository https://gitbox.apache.org/repos/asf/linkis.git


      at b0ab51b8b8 #AI COMMIT# Fix JDBC URL encoding bypass and unsafe deserialization vulnerabilities

This branch includes the following new commits:

     new b0ab51b8b8 #AI COMMIT# Fix JDBC URL encoding bypass and unsafe deserialization vulnerabilities

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@linkis.apache.org
For additional commands, e-mail: commits-help@linkis.apache.org


(linkis) 01/01: #AI COMMIT# Fix JDBC URL encoding bypass and unsafe deserialization vulnerabilities

Posted by ki...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

aiceflower pushed a commit to branch fix/security-jdbc-deser-vuln
in repository https://gitbox.apache.org/repos/asf/linkis.git

commit b0ab51b8b8abfb136fd7efdc416193239be962a7
Author: aiceflower <ai...@sina.com>
AuthorDate: Wed Jun 3 17:59:40 2026 +0800

    #AI COMMIT# Fix JDBC URL encoding bypass and unsafe deserialization vulnerabilities
    
    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 <no...@anthropic.com>
---
 .../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: commits-unsubscribe@linkis.apache.org
For additional commands, e-mail: commits-help@linkis.apache.org