Druid连接池SQL防火墙规则配置:自定义黑白名单
1. 为什么需要SQL防火墙?
你是否遇到过生产环境中突然出现大量慢查询?或者担心开发人员误写的DELETE语句导致全表数据被清空?Druid连接池内置的SQL防火墙(WallFilter)可以帮你解决这些问题。通过灵活的黑白名单规则,你可以精确控制哪些SQL操作被允许,哪些被拒绝,从而有效防止SQL注入攻击和误操作。
读完本文后,你将能够:
- 理解Druid SQL防火墙的核心原理
- 配置表级和函数级的黑白名单规则
- 实现动态更新防火墙规则
- 结合实际场景设计安全策略
2. SQL防火墙工作原理
Druid的SQL防火墙通过WallFilter实现,它是Druid的一个过滤器,位于JDBC调用链中,对所有SQL语句进行拦截和检查。其核心处理流程如下:
关键实现类位于core/src/main/java/com/alibaba/druid/wall/WallFilter.java,它通过调用WallProvider的check方法对SQL进行验证。WallProvider会根据不同数据库类型(如MySQL、Oracle)提供特定的解析和验证逻辑。
3. 基础配置:开启SQL防火墙
要启用Druid的SQL防火墙,需要在数据源配置中添加WallFilter。以下是Spring环境中的配置示例:
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
<!-- 其他基础配置 -->
<property name="url" value="jdbc:mysql://localhost:3306/test" />
<property name="username" value="root" />
<property name="password" value="password" />
<!-- 配置过滤器 -->
<property name="filters" value="wall,stat" />
<!-- WallFilter配置 -->
<property name="connectionProperties" value="druid.wall.logViolation=true;druid.wall.throwException=true" />
</bean>
上述配置中,druid.wall.logViolation=true表示记录违规SQL,druid.wall.throwException=true表示对违规SQL抛出异常。这些参数定义在core/src/main/java/com/alibaba/druid/wall/WallFilter.java的configFromProperties方法中。
4. 黑白名单规则配置详解
4.1 配置方式概述
Druid提供了两种配置黑白名单的方式:
- 通过API动态添加规则
- 通过配置文件加载规则
其中,配置文件方式更适合生产环境,支持动态更新。规则文件通常放置在classpath下的特定目录中,通过WallConfig的loadConfig方法加载,如core/src/main/java/com/alibaba/druid/wall/WallConfig.java的301-311行所示:
loadResource(this.denyVariants, dir + "/deny-variant.txt");
loadResource(this.denySchemas, dir + "/deny-schema.txt");
loadResource(this.denyFunctions, dir + "/deny-function.txt");
loadResource(this.denyTables, dir + "/deny-table.txt");
loadResource(this.denyObjects, dir + "/deny-object.txt");
loadResource(this.readOnlyTables, dir + "/readonly-table.txt");
loadResource(this.permitFunctions, dir + "/permit-function.txt");
loadResource(this.permitTables, dir + "/permit-table.txt");
loadResource(this.permitSchemas, dir + "/permit-schema.txt");
loadResource(this.permitVariants, dir + "/permit-variant.txt");
4.2 表级黑白名单
4.2.1 禁止操作敏感表
创建deny-table.txt文件,添加禁止操作的表名:
user_info
order_.*
上述配置将禁止对user_info表和所有以order_开头的表执行任何操作。规则支持正则表达式,由core/src/main/java/com/alibaba/druid/util/PatternMatcher.java提供匹配能力。
4.2.2 只读表设置
创建readonly-table.txt文件,添加只读表名:
product_info
system_config
配置后,对这些表的UPDATE、DELETE、INSERT操作将被拒绝。实现逻辑见core/src/main/java/com/alibaba/druid/wall/WallConfig.java的603-605行:
public boolean isReadOnly(String tableName) {
return this.readOnlyTables.contains(tableName);
}
4.3 函数级黑白名单
4.3.1 禁用危险函数
创建deny-function.txt文件,添加禁止使用的函数:
exec
system
xp_cmdshell
这些函数通常被用于SQL注入攻击,禁止后可以有效提高安全性。函数名匹配逻辑见core/src/main/java/com/alibaba/druid/wall/WallConfig.java的649-656行:
public boolean isDenyFunction(String name) {
if (!functionCheck) {
return false;
}
name = WallVisitorUtils.form(name);
return this.denyFunctions.contains(name);
}
4.3.2 允许特定函数
创建permit-function.txt文件,添加允许使用的函数:
concat
substring
date_format
当配置了permit-function.txt时,只有列出的函数才被允许使用,其他函数将被拒绝。
4.4 SQL操作类型控制
除了表和函数级控制,Druid还支持对SQL操作类型进行细粒度控制。这些配置通过WallConfig的属性实现,定义在core/src/main/java/com/alibaba/druid/wall/WallConfig.java中:
private boolean selectAllow = true;
private boolean deleteAllow = true;
private boolean updateAllow = true;
private boolean insertAllow = true;
private boolean dropTableAllow = true;
private boolean alterTableAllow = true;
可以通过Spring配置文件进行修改:
<bean id="wallConfig" class="com.alibaba.druid.wall.WallConfig">
<property name="deleteAllow" value="false" />
<property name="dropTableAllow" value="false" />
<property name="alterTableAllow" value="false" />
</bean>
<bean id="wallFilter" class="com.alibaba.druid.wall.WallFilter">
<property name="config" ref="wallConfig" />
</bean>
上述配置禁止了DELETE、DROP TABLE和ALTER TABLE操作,适合只读服务场景。
5. 高级应用:动态更新规则
在生产环境中,我们通常需要在不重启应用的情况下更新规则。Druid提供了两种动态更新方式:
5.1 通过JMX动态更新
Druid支持JMX管理,可以通过JConsole或其他JMX客户端修改WallConfig的属性。相关MBean定义在core/src/main/java/com/alibaba/druid/wall/WallConfigMBean.java中。
5.2 基于配置中心的动态更新
结合配置中心(如Nacos、Apollo),可以实现规则的动态推送。以下是一个简单的实现示例:
public class DynamicWallConfigUpdater {
private WallConfig wallConfig;
private ConfigService configService; // 配置中心客户端
public void start() {
configService.addListener("druid.wall.deny.tables", new Listener() {
@Override
public void receiveConfigInfo(String configInfo) {
wallConfig.getDenyTables().clear();
for (String table : configInfo.split(",")) {
wallConfig.getDenyTables().add(table.trim());
}
}
});
}
}
6. 典型场景配置示例
6.1 电商订单系统
对于电商订单系统,通常需要:
- 禁止删除订单数据
- 限制对用户表的更新
- 禁止重命名或删除表
相关配置如下:
deny-table.txt
order_info
user_info
readonly-table.txt
order_info
product_info
WallConfig配置
<property name="deleteAllow" value="false" />
<property name="dropTableAllow" value="false" />
<property name="renameTableAllow" value="false" />
6.2 报表分析系统
对于报表分析系统,通常是只读场景:
WallConfig配置
<property name="selectAllow" value="true" />
<property name="deleteAllow" value="false" />
<property name="updateAllow" value="false" />
<property name="insertAllow" value="false" />
<property name="dropTableAllow" value="false" />
同时,可以限制查询返回的记录数,防止大查询拖慢系统:
<property name="selectLimit" value="1000" />
该配置定义在core/src/main/java/com/alibaba/druid/wall/WallConfig.java的129行:private int selectLimit = -1;,当值大于0时生效。
7. 监控与审计
启用SQL防火墙后,建议同时启用Druid的监控功能,以便查看SQL执行情况和防火墙拦截记录。监控配置如下:
<servlet>
<servlet-name>DruidStatView</servlet-name>
<servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DruidStatView</servlet-name>
<url-pattern>/druid/*</url-pattern>
</servlet-mapping>
访问http://localhost:8080/druid/sql.html可以查看SQL执行统计,包括被WallFilter拦截的SQL记录。
8. 总结与最佳实践
Druid的SQL防火墙提供了强大的安全防护能力,但配置不当也可能影响业务正常运行。以下是一些最佳实践:
- 循序渐进:先启用日志模式(
druid.wall.throwException=false)观察一段时间,再开启拦截模式 - 最小权限原则:只允许必要的表、函数和操作类型
- 定期审计:结合Druid监控定期审计SQL执行情况,优化规则配置
- 动态更新:生产环境使用动态更新机制,避免重启应用
- 结合其他安全措施:SQL防火墙只是安全防护的一环,还需结合网络隔离、权限控制等措施
通过合理配置Druid的SQL防火墙,你可以有效防范SQL注入攻击和误操作,提高系统的安全性和稳定性。完整的配置示例和更多高级功能,请参考官方文档doc/ha-datasource.md。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



