Druid未授权访问漏洞:原理、复现与Spring Boot安全配置实战
1. 项目概述从一次内部安全巡检说起前段时间公司内部做了一次常规的安全资产梳理和漏洞扫描。在检查一个内部使用的数据监控平台时扫描器突然弹出了一个中危告警“Druid监控接口存在未授权访问风险”。说实话看到这个告警我心里咯噔了一下。Druid作为阿里开源的高性能数据库连接池几乎是国内Java后端项目的标配尤其在Spring Boot生态里集成Druid并开启其内置的监控功能是再常见不过的操作。很多开发者包括早期的我都习惯性地在application.yml里加上druid.stat-view-servlet.enabled: true觉得有个可视化界面看SQL执行情况、连接池状态很方便却往往忽略了最关键的一步——配置访问权限。这个疏忽就为“未授权访问”漏洞敞开了大门。这个漏洞的原理其实不复杂但危害却不小。攻击者无需任何用户名密码直接访问/druid/index.html或/druid/sql.html等路径就能看到你应用所有执行的SQL语句、慢查询、Web URI访问统计甚至能直接执行SQL查询。这意味着什么意味着你的数据库结构、业务逻辑、敏感数据查询模式在攻击者面前近乎透明。如果这个监控界面部署在公网那简直就是给黑客送了一份“系统架构说明书”。今天我就以一个安全从业者和开发者的双重身份带大家完整地复现一次Druid未授权访问漏洞从环境搭建、漏洞验证、原理深入分析到最终的修复方案和深度防御思考。无论你是想学习Web渗透测试的初学者还是负责项目安全开发的工程师相信这篇详实的记录都能给你带来收获。2. 漏洞环境搭建与核心原理剖析2.1 为什么选择Spring Boot Druid作为靶场要复现一个漏洞首先得有一个存在漏洞的环境。我选择用Spring Boot来搭建因为它和Druid的集成度最高也最贴近真实的生产环境。很多快速开发的项目使用Spring Boot Initializr生成项目时往往会直接引入druid-spring-boot-starter依赖这本身没问题问题出在后续的配置上。我们先来看一个典型的、存在漏洞的配置。创建一个新的Spring Boot项目在pom.xml中引入关键依赖dependency groupIdcom.alibaba/groupId artifactIddruid-spring-boot-starter/artifactId version1.2.20/version !-- 此处使用一个历史版本许多旧项目仍在使用 -- /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdmysql/groupId artifactIdmysql-connector-java/artifactId scoperuntime/scope /dependency然后在application.yml中进行如下配置spring: datasource: type: com.alibaba.druid.pool.DruidDataSource url: jdbc:mysql://localhost:3306/test_db?useSSLfalseserverTimezoneUTC username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver # Druid 监控控制台配置存在漏洞的配置 druid: stat-view-servlet: enabled: true # 开启StatViewServlet这是监控页面的入口 login-username: admin # 设置了用户名 login-password: admin # 设置了密码 # 注意这里没有配置allow和deny也没有在WebSecurityConfig中做访问控制看到问题了吗我们虽然设置了login-username和login-password但Druid的访问控制有两道关卡第一道是Druid Servlet自身的allow/denyIP过滤和登录认证第二道是Web应用框架层面的安全控制如Spring Security。上面的配置只完成了Druid自身认证的用户名密码设置但没有通过allow参数指定允许访问的IP。在默认情况下如果不配置allow它的值是空但Druid的处理逻辑是如果allow为空则允许所有IP访问。这是一个非常危险的默认行为。实操心得很多开发者和文章会误导你认为只要配了login-username和login-password就安全了。大错特错在allow为空且没有外层安全框架保护的情况下Druid的监控页面根本不会弹出登录框直接就能访问。这是本漏洞最核心的误区所在。2.2 Druid监控体系架构与漏洞入口点要理解漏洞得先知道Druid监控是怎么工作的。Druid通过一组Servlet来提供监控功能StatViewServlet提供监控展示的HTML页面如/druid/index.html。ResourceServlet提供静态资源JS、CSS。WebStatFilter用于统计Web请求信息。当enabled: true时Druid的自动配置类会向Spring容器注册这些Servlet并将其映射到/druid/*路径下。这些Servlet在初始化时会读取我们配置的allow、deny、login-username等参数。关键逻辑在com.alibaba.druid.support.http.StatViewServlet的init方法和process方法中。其访问控制判断顺序大致如下检查请求IP是否在deny列表中如果是则拒绝。检查请求IP是否在allow列表中如果allow非空且IP不在其中则拒绝。如果allow为空则跳过IP检查。检查是否配置了login-username。如果配置了则进入HTTP Basic认证流程如果没配置则直接允许访问。漏洞就产生在第3步和第4步的结合处当allow为空或未配置但login-username已配置时由于跳过了IP检查请求会直接进入认证环节。然而攻击者可以通过直接访问特定URL如/druid/index.html来绕过认证提示吗在某些版本或特定条件下如果请求不是由浏览器发起的或者缺少某些头信息Servlet可能不会正确返回401状态码和要求认证的头部导致页面直接渲染。更常见的情况是开发者压根没配login-username那么根据第4步访问完全自由。3. 漏洞复现与利用过程全记录3.1 启动存在漏洞的Spring Boot应用将上述配置的应用启动起来假设运行在http://localhost:8080。为了模拟真实情况我写了一个简单的Controller和数据库查询操作让Druid有SQL记录可以监控。RestController public class TestController { Autowired private JdbcTemplate jdbcTemplate; GetMapping(/user/{id}) public String getUser(PathVariable Long id) { // 模拟一个数据库查询这条SQL会被Druid监控到 String sql SELECT username FROM sys_user WHERE id id; // 注意这里仅作演示实际请使用参数化查询避免SQL注入 try { return jdbcTemplate.queryForObject(sql, String.class); } catch (Exception e) { return User not found; } } }应用启动后我们先正常访问几次/user/1接口生成一些监控数据。3.2 未授权访问验证与信息泄露现在我们切换到攻击者视角。攻击者通过子域名爆破、网络空间搜索引擎如Fofa、Shodan或偶然发现得知目标站点使用了Druid。常见的探测路径有/druid/index.html/druid/login.html(有些版本)/api/druid/index.html(可能被映射到其他路径)/sql.html/weburi.html我们在浏览器中直接访问http://localhost:8080/druid/index.html。场景一未配置任何安全参数最严重如果application.yml中关于druid.stat-view-servlet的配置只有enabled: true甚至像下面这样druid: stat-view-servlet: enabled: true那么浏览器将直接显示Druid监控台首页无需任何认证。左侧菜单栏清晰可见“数据源”、“SQL监控”、“SQL防火墙”、“Web应用”、“URI监控”、“Session监控”等。点击“SQL监控”你之前执行的所有SELECT username FROM sys_user WHERE id ...语句及其执行时间、读取行数等信息一览无余。这相当于把数据库的慢查询日志和业务调用链直接暴露了。场景二配置了密码但未配置allow常见错误配置即我们前面给出的那个配置。这时访问http://localhost:8080/druid/index.html预期是弹出HTTP Basic认证对话框要求输入用户名(admin)和密码(admin)。但是在一些旧版本如1.1.10之前或特定环境下认证可能失效。更关键的是攻击者可以尝试直接访问JSON数据接口这些接口同样由StatViewServlet处理但可能因为编码问题或Servlet路径匹配规则导致认证被绕过。例如尝试访问http://localhost:8080/druid/weburi.json来获取URI监控数据。注意事项测试时不要只用浏览器。浏览器的行为可能具有欺骗性例如缓存了认证信息。一定要使用curl、Postman或Burp Suite这类工具进行验证。使用curl命令可以清晰地看到服务器返回的HTTP状态码和头部信息curl -v http://localhost:8080/druid/index.html如果返回200 OK且内容是HTML说明未授权访问存在。如果返回401 Unauthorized并带有WWW-Authenticate: Basic realmDruid Console头部则说明认证生效但密码可能弱口令需要进一步爆破。3.3 利用泄露信息进行深度渗透假设我们已成功未授权访问到监控页面能获得哪些有价值的信息呢数据源信息在“数据源”页面可以看到当前所有Druid数据源的连接池活跃数、等待数、执行查询总数等。虽然不直接暴露JDBC URL和密码但能判断数据库的繁忙程度和健康状态。完整SQL语句“SQL监控”页是重灾区。这里记录了所有执行过的SQL及其参数。攻击者可以分析业务逻辑通过观察SQL表名和字段名推断出用户表、订单表、商品表等核心业务数据结构。发现未参数化查询如果看到SQL语句中直接拼接了变量如WHERE id 1那么这里就可能存在SQL注入漏洞。攻击者可以结合监控到的SQL结构快速构造注入Payload。获取敏感数据模式通过观察SELECT的字段可能推断出哪些字段是用户名、手机号、邮箱等敏感信息。Web应用访问详情“URI监控”页面统计了所有HTTP请求的路径、执行时间、请求次数、并发数等。这可以帮助攻击者绘制API地图了解应用有哪些接口API端点。发现高性能瓶颈接口频繁访问或耗时长的接口可能是重点攻击目标或业务核心。识别未授权API结合返回的JdbcResultSet行数等信息辅助判断接口功能。Session监控如果开启了Session监控可能会泄露部分Session信息虽然不直接是Session ID但对于理解应用状态管理有帮助。这些信息单独看可能价值有限但组合起来就是一份珍贵的“攻击者指南”。它极大地降低了后续进行SQL注入、越权访问、API滥用等攻击的成本和难度。4. 漏洞修复方案与安全配置实战发现了漏洞接下来就是修复。修复Druid未授权访问必须从“默认拒绝”的安全原则出发多层面加固。4.1 方案一正确配置Druid StatViewServlet基础修复这是最直接的修复方式必须同时配置allow和登录认证。druid: stat-view-servlet: enabled: true login-username: your_strong_username # 改为强用户名 login-password: Your$tr0ngPssw0rd! # 改为强密码 allow: 127.0.0.1,192.168.1.0/24 # 明确指定允许访问的IP或网段 deny: # 明确拒绝的IP reset-enable: false # 强烈建议关闭“重置所有”功能防止误操作或恶意清空统计 # 可选更改监控页面路径增加一点隐蔽性但非安全措施 # url-pattern: /my-monitor/*关键解释allow: 这里设置为127.0.0.1本机和192.168.1.0/24内网一个网段。生产环境务必将其设置为运维管理后台的IP地址或VPN入口IP段切勿设置为空或0.0.0.0。deny: 如果需要屏蔽某些IP可以在这里配置。reset-enable: false: 这个非常重要。如果为true监控页面上会有一个“重置所有”按钮点击后所有监控统计清零。这不仅是功能问题攻击者可能通过频繁重置来干扰你的监控和问题排查。实操心得allow支持IP和域名但建议只用IP。网段支持CIDR格式如192.168.1.0/24。多个值用英文逗号分隔不要有空格。我曾遇到过因为allow: 127.0.0.1, 192.168.1.1IP后面多了一个空格导致配置解析失败allow失效的坑。4.2 方案二通过Spring Security进行访问控制推荐仅靠Druid自身的IP限制和Basic认证还不够强。Basic认证密码是明文配置在文件中的且传输是Base64编码近乎明文。更安全的方式是将其纳入到应用统一的安全管理体系例如使用Spring Security。首先添加Spring Security依赖dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-security/artifactId /dependency然后编写一个安全配置类Configuration EnableWebSecurity public class SecurityConfig extends WebSecurityConfigurerAdapter { Override protected void configure(HttpSecurity http) throws Exception { http .authorizeRequests() .antMatchers(/druid/**).hasRole(ADMIN) // 限制/druid/*路径只能ADMIN角色访问 .anyRequest().permitAll() // 其他请求放开按实际需求调整 .and() .formLogin() // 使用表单登录比Basic Auth更友好安全 .loginPage(/login) // 自定义登录页 .permitAll() .and() .logout() .permitAll() .and() .csrf().disable() // 根据情况决定是否禁用CSRF对于监控接口有时需要禁用 .headers().frameOptions().sameOrigin(); // 允许同源iframe嵌入如果Druid页面需要被iframe // 特别注意如果不需要Druid的Basic Auth弹窗需要在Druid配置中注释掉login-username/password // 否则会弹出两层认证 } Bean Override public UserDetailsService userDetailsService() { // 这里使用内存用户演示生产环境应从数据库或LDAP读取 UserDetails admin User.withDefaultPasswordEncoder() // 注意仅用于演示生产环境必须用PasswordEncoder加密 .username(admin) .password(secureAdminPass123) .roles(ADMIN) .build(); return new InMemoryUserDetailsManager(admin); } }同时需要修改Druid配置移除或注释掉login-username和login-password让访问控制完全交给Spring Securitydruid: stat-view-servlet: enabled: true allow: # 可以留空因为访问控制已由Spring Security接管 # login-username: admin # 注释掉 # login-password: admin # 注释掉这种方式的优势统一的认证授权管理员使用一套账号密码管理整个后台包括Druid监控。更安全的认证方式可以使用表单登录、记住我、多因子认证等比HTTP Basic Auth安全得多。灵活的访问策略可以轻松实现基于角色、IP、时间的复杂访问规则。易于审计所有登录和访问行为都可以通过Spring Security的审计功能记录下来。4.3 方案三通过网络层隔离最彻底对于安全性要求极高的生产环境最彻底的办法是根本不让监控界面被公网访问。部署层面将包含Druid监控的应用程序部署在内网通过VPN或堡垒机进行访问。公网只暴露必要的业务API网关或前端。网络策略在防火墙或云安全组上设置规则只允许特定的管理IP段访问应用服务器的8080端口或你的服务端口。反向代理配置如果使用Nginx/Apache作为反向代理可以在代理层对/druid/*路径的访问进行IP白名单限制或二次认证。location /druid/ { allow 10.0.0.0/8; # 内网网段 allow 172.16.0.0/12; deny all; proxy_pass http://localhost:8080; # 还可以在此处添加HTTP Basic认证使用htpasswd # auth_basic Druid Console; # auth_basic_user_file /etc/nginx/.htpasswd; }4.4 方案四彻底关闭监控非必要不开启如果项目确实不需要Druid的监控功能或者有更完善的应用性能监控APM系统如SkyWalking、Pinpoint那么最安全的方式就是直接关闭它。druid: stat-view-servlet: enabled: false # 关闭StatViewServlet web-stat-filter: enabled: false # 也建议关闭WebStatFilter减少不必要的性能开销和潜在信息泄露在关闭前请评估是否会影响运维排查问题的能力。通常在测试和开发环境开启在生产环境严格管控并限制访问是一个平衡的选择。5. 漏洞扫描、排查与深度防御建议5.1 如何主动发现与扫描此类漏洞作为防御方我们不能等到被攻击了才发现问题。需要主动进行扫描和排查。内部资产自查配置检查检查所有Spring Boot项目的application.yml或application.properties文件搜索druid.stat-view-servlet.enabled、allow、login-username等关键词。确保生产环境的配置是安全的。代码审查在CI/CD流水线中加入安全代码扫描SAST使用工具如SonarQube、Checkmarx并配置规则来识别不安全的Druid配置模式。自动化漏洞扫描使用专业扫描器如AWVS、Nessus、Nexpose等它们通常有检测Druid未授权访问的插件。使用开源工具如nmap的http-vuln-*脚本或专门针对Web框架的扫描工具。也可以编写简单的Python脚本进行批量检测import requests targets [http://app1.com, http://app2.com] paths [/druid/index.html, /druid/sql.html] for target in targets: for path in paths: url target path try: resp requests.get(url, timeout5) if resp.status_code 200 and Druid Console in resp.text: print(f[VULN] {url} - Druid console exposed!) except: pass网络空间测绘定期使用Fofa、Shodan、ZoomEye等平台以titleDruid Console或bodyDruid Stat Index等特征语法搜索公司资产是否被意外暴露在公网。5.2 针对已上线系统的紧急排查清单如果怀疑线上系统存在此漏洞请立即按以下步骤操作立即验证尝试从非授权网络如手机4G网络访问https://your-domain.com/druid/index.html。如果能够直接打开或仅弹出弱口令认证框则存在风险。检查配置登录服务器查看应用配置文件。重点检查allow字段是否为空或包含过于宽泛的IP段如0.0.0.0/0。检查依赖版本使用mvn dependency:tree或检查pom.xml确认Druid版本。一些非常旧的版本可能存在其他安全漏洞建议升级到最新稳定版如1.2.x系列的最新版本。审查访问日志检查Web服务器Nginx/Apache和应用日志搜索是否有大量访问/druid/*路径的异常请求特别是来自陌生IP的请求。临时缓解如果无法立即重启应用修改配置可以在防火墙层面紧急添加规则阻断外部IP对/druid/*路径的访问。5.3 深度防御与安全开发规范修复一个具体漏洞是治标建立规范才能治本。安全配置基线在项目脚手架或内部中间件中预置安全的Druid配置模板。将allow设置为127.0.0.1并强制要求开发者在部署到不同环境时显式修改。环境差异化配置利用Spring Boot的Profile特性严格区分配置。application-dev.yml:enabled: true,allow: 127.0.0.1方便开发调试。application-prod.yml:enabled: true,allow: 10.10.1.0/24(运维网段)并配置强密码或者直接enabled: false。定期安全培训向开发团队普及“默认安全”原则。任何管理界面、监控端点、调试接口在默认情况下都必须是关闭或受严格保护的。开启任何功能前必须先考虑其安全影响。纳入SDL流程在软件开发生命周期SDL的需求和设计阶段就要考虑组件的安全配置。在测试阶段安全测试用例必须包含对管理接口未授权访问的检测。监控与告警对管理接口的访问建立监控告警。任何对/druid/*、/actuator/*、/swagger-ui.html等路径的访问如果来源IP不在白名单内应立即触发告警通知安全团队。Druid未授权访问漏洞是一个典型的“错误配置”漏洞。它提醒我们在追求开发效率和功能强大的同时绝不能忽视组件默认配置带来的安全隐患。作为开发者要有“安全左移”的意识在编写第一行配置代码时就思考它运行在生产环境的样子作为运维和安全人员则需要通过工具和流程将这种安全配置的要求固化下来持续监控。安全是一个整体每一个细微的疏忽都可能成为整个防线被突破的起点。