关于druid
Druid是阿里巴巴开源平台上的一个数据库连接池实现,结合了C3P0、DBCP、PROXOOL等数据库连接池的优点,同时加入了日志监控,可以很好的监控DB池连接以及SQL的执行情况。Druid Monitor就是其附带的监控工具,除了监控数据库以及SQL执行以外,还可以监控Web应用、URI监控、Session监控、Spring监控等。
具体实现
Druid 0.1.18 之后版本都发布到maven中央仓库中,所以你只需要在项目的pom.xml中加上dependency就可以了。
xml
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid-version}</version>
</dependency>
Druid内置提供了一个StatViewServlet用于展示Druid的统计信息。在相关配置文件中配置druid的参数后,然后注册对应的servlet和filter即可启用DruidMonitor了。主要是以下两种方式:
* 通过web.xml
以Spring整合为例,将spring配置文件中数据源配置替换成监控需要的配置:
```xml
<!-- 配置初始化大小、最小、最大 -->
<property name="initialSize" value="1" />
<property name="minIdle" value="1" />
<property name="maxActive" value="20" />
<!-- 配置获取连接等待超时的时间 -->
<property name="maxWait" value="60000" />
<!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->
<property name="timeBetweenEvictionRunsMillis" value="60000" />
<!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->
<property name="minEvictableIdleTimeMillis" value="300000" />
<property name="validationQuery" value="SELECT 'x'" />
<property name="testWhileIdle" value="true" />
<property name="testOnBorrow" value="false" />
<property name="testOnReturn" value="false" />
<!-- 打开PSCache,并且指定每个连接上PSCache的大小 -->
<property name="poolPreparedStatements" value="true" />
<property name="maxPoolPreparedStatementPerConnectionSize" value="20" />
<!-- 配置监控统计拦截的filters -->
<property name="filters" value="stat" />
      配置完连接池的属性后,就是注册对应的servlet和filter启用DruidMonitor了:
xml
DruidStatView
com.alibaba.druid.support.http.StatViewServlet
DruidStatView
/druid/
      filter配置,例如设置哪些请求进行过滤排除掉,从而不进行统计等:
xml
druidWebStatFilter
com.alibaba.druid.support.http.WebStatFilter
exclusions
/public/,.js,.css,/druid,.jsp,*.swf
principalSessionName
sessionInfo
profileEnable
true
druidWebStatFilter
/*
```
完成上述配置后,访问对应项目的/druid/index.html即可访问DruidMonitor了。
* Springboot整合
Springboot支持Servlet3.0,通过注解的方式代替传统的xml进行相关的配置,其有两种配置文件,一种是application.properties,另一种是application.yml,两种都可以配置Springboot项目中的一些变量的定义,参数的设置等。
首先还是在对应的配置文件中添加druid的配置,以yml为例:
spring:
datasource:
# 配置多数据源时使用
main:
name: databaseA
driver-class-name: com.mysql.jdbc.Driver
# 对应自己的数据库连接
url: jdbc:mysql://
username: xxx
password: xxx
type: com.alibaba.druid.pool.DruidDataSource
#初始化连接大小
initialSize: 5
#最小连接池数量
minIdle: 5
#最大连接池数量
maxActive: 20
maxIdle: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
# 测试连接
validationQuery: SELECT 1 FROM DUAL
# 申请连接的时候检测,建议配置为true,不影响性能,并且保证安全性
testWhileIdle: true
# 获取连接时执行检测,建议关闭,影响性能
testOnBorrow: false
# 归还连接时执行检测,建议关闭,影响性能
testOnReturn: false
# 是否开启PSCache,PSCache对支持游标的数据库性能提升巨大,oracle建议开启,mysql下建议关闭
poolPreparedStatements: false
# 开启poolPreparedStatements后生效
maxPoolPreparedStatementPerConnectionSize: 20
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall,log4j
logSlowSql: true
然后在代码中读取对应的配置并注册对应的servlet和filter启用DruidMonitor,例如如下的DruidSourceConfig.java,通过Spring Boot 提供的 ServletRegistrationBean 和FilterRegistrationBean接口进行注册:
```java
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
import java.sql.SQLException;
@Configuration
public class DruidSourceConfig {
// 有多数据源时可调为多数据源的配置,如@Value("${spring.datasource.main.name}")
@Value("${spring.datasource.name}")
private String mainName;
@Value("${spring.datasource.url}")
private String mainUrl;
@Value("${spring.datasource.username}")
private String mainUsername;
@Value("${spring.datasource.password}")
private String mainPassword;
@Value("${spring.datasource.driver-class-name}")
private String mainDriverClassName;
@Value("${spring.datasource.initialSize}")
private String mainInitialSize;
@Value("${spring.datasource.minIdle}")
private String mainMinIdle;
@Value("${spring.datasource.maxActive}")
private String mainMaxActive;
@Value("${spring.datasource.maxWait}")
private String mainMaxWait;
@Value("${spring.datasource.timeBetweenEvictionRunsMillis}")
private String mainTimeBetweenEvictionRunsMillis;
@Value("${spring.datasource.minEvictableIdleTimeMillis}")
private String mainMinEvictableIdleTimeMillis;
@Value("${spring.datasource.validationQuery}")
private String mainValidationQuery;
@Value("${spring.datasource.filters}")
private String mainFilters;
@Value("{spring.datasource.logSlowSql}")
private String mainLogSlowSql;
@Value("${spring.datasource.type}")
private String mainType;
@Value("{spring.datasource.maxIdle}")
private String mainMaxIdle;
@Bean
@Primary
public DataSource mainDataSource() {
DruidDataSource datasource = new DruidDataSource();
datasource.setUrl(mainUrl);
datasource.setUsername(mainUsername);
datasource.setPassword(mainPassword);
datasource.setDriverClassName(mainDriverClassName);
//configuration
if (StringUtils.isNotBlank(mainInitialSize)) {
datasource.setInitialSize(Integer.parseInt(mainInitialSize));
}
if (StringUtils.isNotBlank(mainMinIdle)) {
datasource.setMinIdle(Integer.parseInt(mainMinIdle));
}
if (StringUtils.isNotBlank(mainMaxActive)) {
datasource.setMaxActive(Integer.parseInt(mainMaxActive));
}
if (StringUtils.isNotBlank(mainMaxWait)) {
datasource.setMaxWait(Integer.parseInt(mainMaxWait));
}
if (StringUtils.isNotBlank(mainTimeBetweenEvictionRunsMillis)) {
datasource.setTimeBetweenEvictionRunsMillis(Integer.parseInt(mainTimeBetweenEvictionRunsMillis));
}
if (StringUtils.isNotBlank(mainMinEvictableIdleTimeMillis)) {
datasource.setMinEvictableIdleTimeMillis(Integer.parseInt(mainMinEvictableIdleTimeMillis));
}
datasource.setValidationQuery(mainValidationQuery);
datasource.setTestWhileIdle(true);
datasource.setTestOnBorrow(false);
datasource.setTestOnReturn(false);
try {
datasource.setFilters(mainFilters);
} catch (SQLException e) {
e.printStackTrace();
}
return datasource;
}
@Bean
public ServletRegistrationBean statViewServlet(){
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(),"/druid/*");
//是否可以重置数据
servletRegistrationBean.addInitParameter("resetEnable","false");
return servletRegistrationBean;
}
@Bean
public FilterRegistrationBean statFilter(){
//创建过滤器
FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(new WebStatFilter());
//设置过滤器过滤路径
filterRegistrationBean.addUrlPatterns("/*");
//忽略过滤的形式
filterRegistrationBean.addInitParameter("exclusions","*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return filterRegistrationBean;
}
```
同样的完成上述配置后,访问对应项目的/druid/index.html即可访问DruidMonitor了。
以上的两种方式都存在一个问题就是这样配置的DruidMonitor是缺少权限控制的,只要访问对应的页面即可使用对应的业务,存在信息泄漏的风险。
利用思路
查找敏感信息
/druid/datasource.html下有相关数据库的配置
/druid/sql.html下有相关sql执行日志
/druid/spring.html spring监控页面
datasource配置:
sql执行日志:
Spring监控页面:
获取用户sessionId登陆
/druid/websession.html下可以看到相关用户的sessionId
用户sessionId监控页面:
获取web URI越权测试
/druid/weburi.html下可以看到应用相关的URI接口
利用实例
没有测试账号,登录处没有SQL注入,尝试暴力破解没有成果,没有发现相关的框架漏洞,无法进一步深入测试。 发现网站存在Druid Monitor,访问地址http://ip:port/项目名/druid/index.html
Druid Monitor可以监控当前应用的Session状态,URL如下:/druid/websession.html
这里比较幸运,能获取到应用的所有sessionId,那么这时候就可以利用session的特点(识别用户并保持用户信息),尝试登录系统了。在Monitor选择一个sessionid,在Burp尝试修改成自己的sessionid:
替换后我们发现此时系统跳转到业务页面了:
整个过程没有使用任何的账户密码进行登录,单纯是找到一个存活的session进行会话的获取。登录系统后最直接的就是创建相关的账号,然后使用新账号以正常的形式进行登录然后深入了。
这里有个比较麻烦的地方,就是每个业务请求都需要替换我们的sessionid,可以使用Burp的功能模块来解决:
修复建议
为Druid监控配置访问权限,以web.xml配置为例,相关参数如下:
xml
<servlet>
<servlet-name>DruidStatView</servlet-name>
<servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
<!--
deny优先于allow,如果在deny列表中,就算在allow列表中,也会被拒绝。
如果allow没有配置或者为空,则允许所有访问
-->
<init-param>
<param-name>allow</param-name>
<param-value>x.x.x.x</param-value>
</init-param>
<init-param>
<param-name>deny</param-name>
<param-value>x.x.x.x</param-value>
</init-param>
<!-- 用户名和密码 -->
<init-param>
<param-name>loginUsername</param-name>
<param-value>username</param-value>
</init-param>
<init-param>
<param-name>loginPassword</param-name>
<param-value>password</param-value>
</init-param>
</servlet>
同理,SpringBoot整合的情况下也可以通过设置对应的参数进行访问控制:
java
@Bean
public ServletRegistrationBean statViewServlet(){
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(),"/druid/*");
//设置ip白名单
servletRegistrationBean.addInitParameter("allow","");
//设置ip黑名单,优先级高于白名单
servletRegistrationBean.addInitParameter("deny","");
//设置控制台管理用户
servletRegistrationBean.addInitParameter("loginUsername","root");
servletRegistrationBean.addInitParameter("loginPassword","root");
//是否可以重置数据
servletRegistrationBean.addInitParameter("resetEnable","false");
return servletRegistrationBean;
}
参考资料
https://github.com/alibaba/druid/wiki/%E5%B8%B8%E8%A7%81%E9%97%AE%E9%A2%98
https://github.com/alibaba/druid/tree/master/druid-spring-boot-starter
相关推荐: JavaWeb中常见的信息泄漏——druid监控台
关于druid Druid是阿里巴巴开源平台上的一个数据库连接池实现,结合了C3P0、DBCP、PROXOOL等数据库连接池的优点,同时加入了日志监控,可以很好的监控DB池连接以及SQL的…
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论