diff --git a/src/main/java/com/vince/xq/common/utils/security/JdbcSecurityUtil.java b/src/main/java/com/vince/xq/common/utils/security/JdbcSecurityUtil.java new file mode 100644 index 0000000..ecc38df --- /dev/null +++ b/src/main/java/com/vince/xq/common/utils/security/JdbcSecurityUtil.java @@ -0,0 +1,62 @@ +package com.vince.xq.common.utils.security; + +/** + * JDBC 连接安全校验工具类 + * + * 防止通过恶意 JDBC URL 参数触发: + * - MySQL: allowLoadLocalInfile 文件读取, autoDeserialize 反序列化 RCE + * - PostgreSQL: socketFactory 任意类加载 RCE + * - H2: INIT 参数代码执行 + * + * 校验策略: 对 URL 全文 toLowerCase() + contains() 匹配, + * 覆盖所有参数分隔符格式 (?, ;, (), address=) + */ +public class JdbcSecurityUtil { + + /** + * 全驱动危险参数黑名单 (全小写) + */ + private static final String[] UNSAFE_PARAMS = { + // MySQL / MariaDB + "allowloadlocalinfile", + "allowurlinlocalinfile", + "allowloadlocalinfileinpath", + "autodeserialize", + "queryinterceptors", + "statementinterceptors", + "detectcustomcollations", + "maxallowedpacket", + // PostgreSQL + "socketfactory", + "socketfactoryarg", + "sslfactory", + "sslhostnameverifier", + "sslpasswordcallback", + "authenticationpluginclassname", + "jaasapplicationname", + // H2 + "init=", + "runscript", + "trace_level_system_out", + }; + + /** + * 校验 JDBC URL 是否包含危险参数 + * + * @param jdbcUrl JDBC 连接地址 + * @throws IllegalArgumentException 包含危险参数时抛出 + */ + public static void validate(String jdbcUrl) { + if (jdbcUrl == null || jdbcUrl.trim().isEmpty()) { + return; + } + + String lowerUrl = jdbcUrl.toLowerCase(); + + for (String unsafeParam : UNSAFE_PARAMS) { + if (lowerUrl.contains(unsafeParam)) { + throw new IllegalArgumentException("JDBC URL 包含不安全参数: " + unsafeParam); + } + } + } +} diff --git a/src/main/java/com/vince/xq/project/common/RunUtil.java b/src/main/java/com/vince/xq/project/common/RunUtil.java index ae8df2d..7a7bc0f 100644 --- a/src/main/java/com/vince/xq/project/common/RunUtil.java +++ b/src/main/java/com/vince/xq/project/common/RunUtil.java @@ -4,6 +4,7 @@ import com.vince.xq.project.system.ProbeJobInstance.domain.ProbeJobInstance; import com.vince.xq.project.system.instance.controller.InstanceController; import com.vince.xq.project.system.jobconfig.domain.Jobconfig; +import com.vince.xq.common.utils.security.JdbcSecurityUtil; import com.vince.xq.project.system.dbconfig.domain.Dbconfig; import com.vince.xq.project.system.instance.domain.Instance; import com.vince.xq.project.system.probeJobConfig.domain.Probejobconfig; @@ -214,6 +215,7 @@ public static List> runDiffDetail(Dbconfig dbconfi throw new Exception("注册驱动失败"); } Connection conn = null; + JdbcSecurityUtil.validate(dbconfig.getUrl()); try { conn = DriverManager.getConnection(dbconfig.getUrl(), dbconfig.getUserName(), dbconfig.getPwd()); Statement stat = conn.createStatement(); @@ -250,6 +252,7 @@ public static Instance runNum(Dbconfig dbconfig, String connectDriver, String co throw new Exception("注册驱动失败"); } Connection conn = null; + JdbcSecurityUtil.validate(dbconfig.getUrl()); try { conn = DriverManager.getConnection(dbconfig.getUrl(), dbconfig.getUserName(), dbconfig.getPwd()); Statement stat = conn.createStatement(); @@ -358,6 +361,7 @@ private static Map runProbJobPrimaryKey(Dbconfig dbconfig, Strin throw new Exception("注册驱动失败"); } Connection conn = null; + JdbcSecurityUtil.validate(dbconfig.getUrl()); try { conn = DriverManager.getConnection(dbconfig.getUrl(), dbconfig.getUserName(), dbconfig.getPwd()); Statement stat = conn.createStatement(); @@ -388,6 +392,7 @@ private static List> runProbJobField(Dbconfig dbco throw new Exception("注册驱动失败"); } Connection conn = null; + JdbcSecurityUtil.validate(dbconfig.getUrl()); try { conn = DriverManager.getConnection(dbconfig.getUrl(), dbconfig.getUserName(), dbconfig.getPwd()); Statement stat = conn.createStatement(); diff --git a/src/main/java/com/vince/xq/project/system/dbconfig/controller/DbConfigController.java b/src/main/java/com/vince/xq/project/system/dbconfig/controller/DbConfigController.java index 8fe8eb6..40d3591 100644 --- a/src/main/java/com/vince/xq/project/system/dbconfig/controller/DbConfigController.java +++ b/src/main/java/com/vince/xq/project/system/dbconfig/controller/DbConfigController.java @@ -9,6 +9,7 @@ import com.vince.xq.framework.web.page.TableDataInfo; import com.vince.xq.project.system.dbconfig.domain.Dbconfig; import com.vince.xq.project.system.dbconfig.service.IDbconfigService; +import com.vince.xq.common.utils.security.JdbcSecurityUtil; import org.apache.shiro.authz.annotation.RequiresPermissions; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -89,6 +90,11 @@ public String add(ModelMap mmap) { @PostMapping("/add") @ResponseBody public AjaxResult addSave(@Validated Dbconfig dbconfig) { + try { + JdbcSecurityUtil.validate(dbconfig.getUrl()); + } catch (IllegalArgumentException e) { + return error("连接地址安全校验失败:" + e.getMessage()); + } if (UserConstants.DBCONFIG_NAME_NOT_UNIQUE.equals(dbconfigService.checkConnectNameUnique(dbconfig))) { return error("新增数据库连接名称'" + dbconfig.getConnectName() + "'失败,数据库连接名称已存在"); } @@ -115,6 +121,11 @@ public String edit(@PathVariable("id") Long id, ModelMap mmap) { @PostMapping("/edit") @ResponseBody public AjaxResult editSave(@Validated Dbconfig dbconfig) { + try { + JdbcSecurityUtil.validate(dbconfig.getUrl()); + } catch (IllegalArgumentException e) { + return error("连接地址安全校验失败:" + e.getMessage()); + } if (UserConstants.POST_NAME_NOT_UNIQUE.equals(dbconfigService.checkConnectNameUnique(dbconfig))) { return error("修改数据库连接名称'" + dbconfig.getConnectName() + "'失败,数据库连接名称已存在"); } @@ -132,7 +143,10 @@ public String checkConnectNameUnique(Dbconfig dbconfig) { @ResponseBody public AjaxResult testConnection(Dbconfig dbconfig) { try { + JdbcSecurityUtil.validate(dbconfig.getUrl()); dbconfigService.testConnection(dbconfig); + } catch (IllegalArgumentException e) { + return error("连接地址安全校验失败:" + e.getMessage()); } catch (Exception e) { e.printStackTrace(); return error("错误信息:" + e.getMessage()); diff --git a/src/main/java/com/vince/xq/project/system/dbconfig/service/DbconfigServiceImpl.java b/src/main/java/com/vince/xq/project/system/dbconfig/service/DbconfigServiceImpl.java index a2a7350..9696f2e 100644 --- a/src/main/java/com/vince/xq/project/system/dbconfig/service/DbconfigServiceImpl.java +++ b/src/main/java/com/vince/xq/project/system/dbconfig/service/DbconfigServiceImpl.java @@ -8,6 +8,7 @@ import com.vince.xq.project.common.DbTypeEnum; import com.vince.xq.project.system.dbconfig.domain.Dbconfig; import com.vince.xq.project.system.dbconfig.mapper.DbconfigMapper; +import com.vince.xq.common.utils.security.JdbcSecurityUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -137,6 +138,7 @@ private void testConnectionDriver(Dbconfig dbconfig, String connectDriver) throw } catch (ClassNotFoundException e) { throw new Exception("注册驱动失败"); } + JdbcSecurityUtil.validate(dbconfig.getUrl()); Connection conn = null; try { conn = DriverManager.getConnection(dbconfig.getUrl(), dbconfig.getUserName(), dbconfig.getPwd());