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