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]