如果说Mycat提供的SQL拦截器是为了事后审计SQL语句的话,那么,Mycat提供的SQL防火墙就是为了事先的预防。

为什么需要使用SQL防火墙?

对应熟悉MySQL的童鞋来说,我们可以指定用户只能通过某些IP来访问数据库。但是,一旦我们使用了Mycat,所有访问数据库的认证就交由Mycat来进行。之前我们在Mycat的server.xml文件中所配置的那些用户,在所有能够访问到Mycat的主机上都可以登录数据库,这是很不安全的。Mycat的SQL防火墙功能,就给我们提供了统一控制哪些用户可以通过哪些主机访问后端数据库的功能。

另外,Mycat提供的SQL防火墙实现了SQL屏蔽功能,它能够让我们对一些SQL语句进行屏蔽,比如,没有添加where条件的delete语句或者drop语句等。以增强数据的安全性。

如何启用Mycat的SQL防火墙?

要想启用Mycat的SQL防火墙,则需要在server.xml文件中新增一个firewall标签,如下所示。

<firewall>
<whitehost>
<host user="root" host="127.0.0.1"></host>
</whitehost>
<blacklist check="true">
<property name="noneBaseStatementAllow">true</property>
<property name="deleteWhereNoneCheck">true</property>
</blacklist>
</firewall>

其中,whitehost标签中配置了白名单中的主机和用户,一旦启用了Mycat的SQL防火墙,则只有在白名单中配置的主机和用户才能访问Mycat。例如,上述配置中,只有在Mycat所在的节点主机上使用root用户才能访问Mycat,使用其他用户和其他主机都不能访问Mycat。

blacklist标签下配置了一个黑名单的列表,在这个列表中,配置了Mycat需要对哪些操作进行限制。例如上述配置中,我们配置了是否允许执行DDL操作(true为不允许),是否检查没有where条件的delete语句(true为检查,不允许执行)。

实际配置

接下来,我们在server.xml文件中,新增上述配置。最终,server.xml文件的内容如下所示。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mycat:server SYSTEM "server.dtd">
<mycat:server xmlns:mycat="http://io.mycat/">
<system>
<property name="useHandshakeV10">1</property>
<property name="defaultSqlParser">druidparser</property>
<property name="serverPort">3307</property>
<property name="managerPort">3308</property>
<property name="nonePasswordLogin">0</property>
<property name="bindIp">0.0.0.0</property>
<property name="charset">utf8mb4</property>
<property name="frontWriteQueueSize">2048</property>
<property name="txIsolation">2</property>
<property name="processors">2</property>
<property name="idleTimeout">1800000</property>
<property name="sqlExecuteTimeout">300</property>
<property name="useSqlStat">0</property>
<property name="useGlobleTableCheck">0</property>
<property name="sequenceHandlerType">1</property>
<property name="defaultMaxLimit">1000</property>
<property name="maxPacketSize">104857600</property>

<property name="sqlInterceptor">
io.mycat.server.interceptor.impl.StatisticsSqlInterceptor
</property>
<property name="sqlInterceptorType">
UPDATE,DELETE,INSERT
</property>
<property name="sqlInterceptorFile">/tmp/sql.txt</property>
</system>

<firewall>
<whitehost>
<host user="mycat" host="192.168.175.150"></host>
</whitehost>
<blacklist check="true">
<property name="noneBaseStatementAllow">true</property>
<property name="deleteWhereNoneCheck">true</property>
</blacklist>
</firewall>

<user name="mycat" defaultAccount="true">
<property name="usingDecrypt">1</property>
<property name="password">cTwf23RrpBCEmalp/nx0BAKenNhvNs2NSr9nYiMzHADeEDEfwVWlI6hBDccJjNBJqJxnunHFp5ae63PPnMfGYA==</property>
<property name="schemas">shop</property>
</user>

</mycat:server>

上述配置中,只允许mycat用户在192.168.175.150主机上访问Mycat,不允许执行DDL操作,不允许执行没有where条件的delete语句。

附录:黑名单拦截明细配置

配置项

缺省值

描述

selelctAllow

true

是否允许执行 SELECT 语句

selectAllColumnAllow

true

是否允许执行 SELECT * FROM T 这样的语句。如果设置为 false,不允许执行 select * from t,但可以select * from (select id, name from t) a。这个选项是防御程序通过调用 select * 获得数据表的结构信息。

selectIntoAllow

true

SELECT 查询中是否允许 INTO 字句

deleteAllow

true

是否允许执行 DELETE 语句

updateAllow

true

是否允许执行 UPDATE 语句

insertAllow

true

是否允许执行 INSERT 语句

replaceAllow

true

是否允许执行 REPLACE 语句

mergeAllow

true

是否允许执行 MERGE 语句,这个只在 Oracle 中有用

callAllow

true

是否允许通过 jdbc 的 call 语法调用存储过程

setAllow

true

是否允许使用 SET 语法

truncateAllow

true

truncate 语句是危险,缺省打开,若需要自行关闭

createTableAllow

true

是否允许创建表

alterTableAllow

true

是否允许执行 Alter Table 语句

dropTableAllow

true

是否允许修改表

commentAllow

false

是否允许语句中存在注释,Oracle 的用户不用担心,Wall 能够识别 hints和注释的区别

noneBaseStatementAllow

false

是否允许非以上基本语句的其他语句,缺省关闭,通过这个选项就能够屏蔽 DDL。

multiStatementAllow

false

是否允许一次执行多条语句,缺省关闭

useAllow

true

是否允许执行 mysql 的 use 语句,缺省打开

describeAllow

true

是否允许执行 mysql 的 describe 语句,缺省打开

showAllow

true

是否允许执行 mysql 的 show 语句,缺省打开

commitAllow

true

是否允许执行 commit 操作

rollbackAllow

true

是否允许执行 roll back 操作

##如果把 selectIntoAllow、deleteAllow、updateAllow、insertAllow、mergeAllow 都设置为 false,这就是一个只读数据源了。##

拦截配置-永真条件

selectWhereAlwayTrueCheck

true

检查 SELECT 语句的 WHERE 子句是否是一个永真条件

selectHavingAlwayTrueCheck

true

检查 SELECT 语句的 HAVING 子句是否是一个永真条件

deleteWhereAlwayTrueCheck

true

检查 DELETE 语句的 WHERE 子句是否是一个永真条件

deleteWhereNoneCheck

false

检查 DELETE 语句是否无 where 条件,这是有风险的,但不是 SQL 注入类型的风险

updateWhereAlayTrueCheck

true

检查 UPDATE 语句的 WHERE 子句是否是一个永真条件

updateWhereNoneCheck

false

检查 UPDATE 语句是否无 where 条件,这是有风险的,但不是SQL 注入类型的风险

conditionAndAlwayTrueAllow

false

检查查询条件(WHERE/HAVING 子句)中是否包含 AND 永真条件

conditionAndAlwayFalseAllow

false

检查查询条件(WHERE/HAVING 子句)中是否包含 AND 永假条件

conditionLikeTrueAllow

true

检查查询条件(WHERE/HAVING 子句)中是否包含 LIKE 永真条件

其他拦截配置

selectIntoOutfileAllow

false

SELECT … INTO OUTFILE 是否允许,这个是 mysql 注入攻击的常见手段,缺省是禁止的

selectUnionCheck

true

检测 SELECT UNION

selectMinusCheck

true

检测 SELECT MINUS

selectExceptCheck

true

检测 SELECT EXCEPT

selectIntersectCheck

true

检测 SELECT INTERSECT

mustParameterized

false

是否必须参数化,如果为 True,则不允许类似 WHERE ID = 1 这种不参数化的 SQL

strictSyntaxCheck

true

是否进行严格的语法检测,Druid SQL Parser 在某些场景不能覆盖所有的SQL 语法,出现解析 SQL 出错,可以临时把这个选项设置为 false,同时把 SQL 反馈给 Druid 的开发者。

conditionOpXorAllow

false

查询条件中是否允许有 XOR 条件。XOR 不常用,很难判断永真或者永假,缺省不允许。

conditionOpBitwseAllow

true

查询条件中是否允许有"&"、"~"、"|"、"^"运算符。

conditionDoubleConstAllow

false

查询条件中是否允许连续两个常量运算表达式

minusAllow

true

是否允许 SELECT * FROM A MINUS SELECT * FROM B 这样的语句

intersectAllow

true

是否允许 SELECT * FROM A INTERSECT SELECT * FROM B 这样的语句

constArithmeticAllow

true

拦截常量运算的条件,比如说 WHERE FID = 3 - 1,其中"3 - 1"是常量运算表达式。

limitZeroAllow

false

是否允许 limit 0 这样的语句

禁用对象检测配置

tableCheck

true

检测是否使用了禁用的表

schemaCheck

true

检测是否使用了禁用的 Schema

functionCheck

true

检测是否使用了禁用的函数

objectCheck

true

检测是否使用了“禁用对对象”

variantCheck

true

检测是否使用了“禁用的变量”

readOnlyTables

指定的表只读,不能够在 SELECT INTO、DELETE、UPDATE、INSERT、MERGE 中作为"被修改表"出现