Skip to content

Commit 584ea70

Browse files
authored
Handle url contains # and unsafe parameters (#4105)
1 parent a3d4003 commit 584ea70

6 files changed

Lines changed: 139 additions & 21 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
.scala_dependencies
1616
.settings
1717
.classpath
18+
.flattened-pom.xml
1819

1920
# For SBT
2021
.jvmopts

docs/info-1.3.1.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
## 参数变化
22

3-
| 模块名(服务名) | 类型 | 参数名 | 默认值 | 描述 |
4-
|------------------| ----- |----------------------------------------------------------------------|------| ------------------------------------------------------- |
5-
| ps-linkismanager | 修改 | pipeline.output.isoverwtite <br/>-><br/> pipeline.output.isoverwrite | true |取值范围:true或false|
3+
| 模块名(服务名) | 类型 | 参数名 | 默认值 | 描述 |
4+
|---------------------------------------------------|-----|----------------------------------------------------------------------|-------| ------------------------------------------------------- |
5+
| ps-linkismanager | 修改 | pipeline.output.isoverwtite <br/>-><br/> pipeline.output.isoverwrite | true |取值范围:true或false|
6+
| linkis-engineconn-plugins <br/> linkis-datasource | 新增 | linkis.mysql.strong.security.enable | false |取值范围:true或false|

linkis-dist/package/conf/linkis.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ wds.linkis.server.mybatis.datasource.username=
2727
wds.linkis.server.mybatis.datasource.password=
2828
# mysql
2929
wds.linkis.mysql.is.encrypt=false
30+
linkis.mysql.strong.security.enable=false
3031

3132
#hadoop/hive/spark config
3233
#hadoop.config.dir=/appcom/config/hadoop-config

linkis-engineconn-plugins/jdbc/src/main/java/org/apache/linkis/manager/engineplugin/jdbc/utils/JdbcParamUtils.java

Lines changed: 71 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,17 @@
1717

1818
package org.apache.linkis.manager.engineplugin.jdbc.utils;
1919

20+
import org.apache.linkis.common.conf.CommonVars;
21+
import org.apache.linkis.common.conf.CommonVars$;
2022
import org.apache.linkis.manager.engineplugin.jdbc.JDBCPropertiesParser;
2123
import org.apache.linkis.manager.engineplugin.jdbc.constant.JDBCEngineConnConstant;
2224
import org.apache.linkis.manager.engineplugin.jdbc.exception.JDBCParamsIllegalException;
2325

2426
import org.apache.commons.lang3.StringUtils;
2527

28+
import java.util.HashMap;
2629
import java.util.Map;
30+
import java.util.stream.Collectors;
2731

2832
import org.slf4j.Logger;
2933
import org.slf4j.LoggerFactory;
@@ -43,6 +47,9 @@ public class JdbcParamUtils {
4347
private static final String APPEND_PARAMS =
4448
"allowLoadLocalInfile=false&autoDeserialize=false&allowLocalInfile=false&allowUrlInLocalInfile=false";
4549

50+
public static final CommonVars<String> MYSQL_STRONG_SECURITY_ENABLE =
51+
CommonVars$.MODULE$.apply("linkis.mysql.strong.security.enable", "false");
52+
4653
private static final char AND_SYMBOL = '&';
4754

4855
private static final String QUOTATION_MARKS = "\"";
@@ -64,20 +71,62 @@ public static void validateJdbcUrl(String url) {
6471
}
6572

6673
public static String filterJdbcUrl(String url) {
74+
if (StringUtils.isBlank(url)) {
75+
return url;
76+
}
6777
// temporarily filter only mysql jdbc url. & Handles cases that start with JDBC
68-
if (!url.startsWith(JDBC_MYSQL_PROTOCOL) && !url.toLowerCase().contains(JDBC_MYSQL_PROTOCOL)) {
78+
if (!url.toLowerCase().contains(JDBC_MYSQL_PROTOCOL)) {
6979
return url;
7080
}
71-
if (url.contains(SENSITIVE_PARAM)) {
72-
int index = url.indexOf(SENSITIVE_PARAM);
73-
String tmp = SENSITIVE_PARAM;
74-
if (url.charAt(index - 1) == AND_SYMBOL) {
75-
tmp = AND_SYMBOL + tmp;
76-
} else if (url.charAt(index + 1) == AND_SYMBOL) {
77-
tmp = tmp + AND_SYMBOL;
81+
82+
// no params
83+
if (!url.contains(String.valueOf(QUESTION_MARK))) {
84+
return url + QUESTION_MARK + APPEND_PARAMS;
85+
}
86+
87+
// enable strong security
88+
if (Boolean.valueOf(MYSQL_STRONG_SECURITY_ENABLE.getValue())) {
89+
LOG.info("mysql engine use strong security configuration. Remove all connection parameters.");
90+
return url + QUESTION_MARK + APPEND_PARAMS;
91+
}
92+
93+
// deal with params
94+
String[] items = url.split("\\?");
95+
// params error: multiple question marks
96+
if (items.length != 2) {
97+
LOG.warn("JDBC params error, the url is : " + url);
98+
return items[0];
99+
}
100+
101+
String[] params = items[1].split("&");
102+
Map<String, String> paramsMap = new HashMap<>(params.length);
103+
for (String param : params) {
104+
String[] keyAndValues = param.split("=");
105+
// params error: key and value error
106+
if (keyAndValues.length != 2) {
107+
continue;
78108
}
79-
LOG.warn("Sensitive param: {} in jdbc url is filtered.", tmp);
80-
url = url.replace(tmp, "");
109+
String key = keyAndValues[0];
110+
String value = keyAndValues[1];
111+
// key and value is blank
112+
if (StringUtils.isBlank(key) || StringUtils.isBlank(value)) {
113+
continue;
114+
}
115+
if (isSecurity(key, value)) {
116+
paramsMap.put(key, value);
117+
} else {
118+
LOG.warn("Sensitive param : key={} and value={}", key, value);
119+
}
120+
}
121+
String extraParamString =
122+
paramsMap.entrySet().stream()
123+
.map(e -> String.join("=", e.getKey(), String.valueOf(e.getValue())))
124+
.collect(Collectors.joining("&"));
125+
126+
if (StringUtils.isBlank(extraParamString)) {
127+
url = items[0];
128+
} else {
129+
url = items[0] + String.valueOf(QUESTION_MARK) + extraParamString;
81130
}
82131
if (url.endsWith(String.valueOf(QUESTION_MARK))) {
83132
url = url + APPEND_PARAMS;
@@ -90,6 +139,18 @@ public static String filterJdbcUrl(String url) {
90139
return url;
91140
}
92141

142+
private static boolean isSecurity(String key, String value) {
143+
return !(isNotSecurity(key) || isNotSecurity(value));
144+
}
145+
146+
private static boolean isNotSecurity(String key) {
147+
return key.toLowerCase().contains("allowLoadLocalInfile".toLowerCase())
148+
|| key.toLowerCase().contains("autoDeserialize".toLowerCase())
149+
|| key.toLowerCase().contains("allowLocalInfile".toLowerCase())
150+
|| key.toLowerCase().contains("allowUrlInLocalInfile".toLowerCase())
151+
|| key.toLowerCase().contains("#".toLowerCase());
152+
}
153+
93154
public static String getJdbcUsername(Map<String, String> properties)
94155
throws JDBCParamsIllegalException {
95156
String username =

linkis-engineconn-plugins/jdbc/src/test/java/org/apache/linkis/manager/engineplugin/jdbc/utils/JdbcParamUtilsTest.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,21 @@ public class JdbcParamUtilsTest {
3131
@Test
3232
@DisplayName("testFilterJdbcUrl")
3333
public void testFilterJdbcUrl() {
34-
String url = "jdbc:mysql://localhost:3306/db_name";
34+
String url =
35+
"jdbc:mysql://localhost:3306/db_name?allowLoadLocalInfile=true&autoDeserialize=true&#sk=13&p1=v1";
36+
url = JdbcParamUtils.filterJdbcUrl(url);
37+
Assertions.assertEquals(
38+
"jdbc:mysql://localhost:3306/db_name?p1=v1&allowLoadLocalInfile=false&autoDeserialize=false&allowLocalInfile=false&allowUrlInLocalInfile=false",
39+
url);
40+
41+
url =
42+
"jdbc:mysql://localhost:3306/db_name?\\#allowLoadLocalInfile=true&autoDeserialize=true&#sk=13&p1=v1";
43+
url = JdbcParamUtils.filterJdbcUrl(url);
44+
Assertions.assertEquals(
45+
"jdbc:mysql://localhost:3306/db_name?p1=v1&allowLoadLocalInfile=false&autoDeserialize=false&allowLocalInfile=false&allowUrlInLocalInfile=false",
46+
url);
47+
48+
url = "jdbc:mysql://localhost:3306/db_name";
3549
url = JdbcParamUtils.filterJdbcUrl(url);
3650
Assertions.assertEquals(
3751
"jdbc:mysql://localhost:3306/db_name?allowLoadLocalInfile=false&autoDeserialize=false&allowLocalInfile=false&allowUrlInLocalInfile=false",

linkis-public-enhancements/linkis-datasource/linkis-metadata-query/service/jdbc/src/main/java/org/apache/linkis/metadata/query/service/mysql/SqlConnection.java

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,16 @@
2020
import org.apache.linkis.common.conf.CommonVars;
2121
import org.apache.linkis.metadata.query.common.domain.MetaColumnInfo;
2222

23-
import org.springframework.util.CollectionUtils;
23+
import org.apache.commons.lang.StringUtils;
2424

2525
import java.io.Closeable;
2626
import java.io.IOException;
2727
import java.sql.*;
2828
import java.util.*;
2929
import java.util.stream.Collectors;
3030

31+
import scala.annotation.meta.param;
32+
3133
import org.slf4j.Logger;
3234
import org.slf4j.LoggerFactory;
3335

@@ -47,6 +49,9 @@ public class SqlConnection implements Closeable {
4749
private static final CommonVars<Integer> SQL_SOCKET_TIMEOUT =
4850
CommonVars.apply("wds.linkis.server.mdm.service.sql.socket.timeout", 6000);
4951

52+
private static final CommonVars<Boolean> MYSQL_STRONG_SECURITY_ENABLE =
53+
CommonVars.apply("linkis.mysql.strong.security.enable", false);
54+
5055
private Connection conn;
5156

5257
private ConnectMessage connectMessage;
@@ -74,21 +79,36 @@ public SqlConnection(
7479
* @param extraParams
7580
*/
7681
private void validateParams(Map<String, Object> extraParams) {
77-
if (CollectionUtils.isEmpty(extraParams)) {
82+
if (extraParams == null) {
7883
return;
7984
}
8085

86+
// enable strong security
87+
if (MYSQL_STRONG_SECURITY_ENABLE.getValue()) {
88+
LOG.info(
89+
"mysql metadata use strong security configuration. Remove all connection parameters.");
90+
extraParams.clear();
91+
}
92+
8193
// Delete suspected vulnerability parameters
8294
Iterator<Map.Entry<String, Object>> iterator = extraParams.entrySet().iterator();
8395
while (iterator.hasNext()) {
8496
Map.Entry<String, Object> entry = iterator.next();
8597
String key = entry.getKey();
86-
if ("allowLoadLocalInfile".equalsIgnoreCase(key)
87-
|| "autoDeserialize".equalsIgnoreCase(key)
88-
|| "allowLocalInfile".equalsIgnoreCase(key)
89-
|| "allowUrlInLocalInfile".equalsIgnoreCase(key)) {
90-
extraParams.remove(key);
98+
if (StringUtils.isBlank(key)
99+
|| entry.getValue() == null
100+
|| StringUtils.isBlank(entry.getValue().toString())) {
91101
iterator.remove();
102+
continue;
103+
}
104+
String value = entry.getValue().toString();
105+
if (keyAndValueIsNotSecurity(key, value, "allowLoadLocalInfile")
106+
|| keyAndValueIsNotSecurity(key, value, "autoDeserialize")
107+
|| keyAndValueIsNotSecurity(key, value, "allowLocalInfile")
108+
|| keyAndValueIsNotSecurity(key, value, "allowUrlInLocalInfile")
109+
|| keyAndValueIsNotSecurity(key, value, "#")) {
110+
iterator.remove();
111+
LOG.warn("mysql metadata sensitive param : key={} and value={}", key, value);
92112
}
93113
}
94114

@@ -97,6 +117,26 @@ private void validateParams(Map<String, Object> extraParams) {
97117
extraParams.put("autoDeserialize", "false");
98118
extraParams.put("allowLocalInfile", "false");
99119
extraParams.put("allowUrlInLocalInfile", "false");
120+
121+
// print extraParams
122+
StringBuilder sb = new StringBuilder("mysql metadata url extraParams: [ ");
123+
for (Map.Entry<String, Object> paramEntry : extraParams.entrySet()) {
124+
sb.append(paramEntry.getKey()).append("=").append(paramEntry.getValue()).append(" ,");
125+
}
126+
sb.deleteCharAt(sb.length() - 1);
127+
sb.append("]");
128+
LOG.info(sb.toString());
129+
}
130+
131+
private boolean keyAndValueIsNotSecurity(String key, String value, String param) {
132+
return !(isSecurity(key, param) && isSecurity(value, param));
133+
}
134+
135+
private boolean isSecurity(String noSecurityKey, String param) {
136+
if (StringUtils.isBlank(param) || StringUtils.isBlank(noSecurityKey)) {
137+
return true;
138+
}
139+
return !noSecurityKey.toLowerCase().contains(param.toLowerCase());
100140
}
101141

102142
public List<String> getAllDatabases() throws SQLException {

0 commit comments

Comments
 (0)