|
目录
1. 前言
2. Mysql账户权限安全
3. Mysql数据的网络安全配置
4. 密码策略安全
5. Mysql日志
6. Mysql数据库服务所在主机安全配置
7. 部署SQL注入检测、防御模块
8. mysqld安全相关启动选项
9. mysql备份策略
1. 前言
Mysql数据库安全配置、或者叫加固属于风险模型中的一环,它需要安全人员在理论和实践的学习中不断发现新的问题,并针对这些问题对数据的各个方面的配置进行强化。本文试图围绕着数据库风险识别、数据库安全加固这个问题,探讨可以采取的措施来最大程度的保证我们的数据库的安全控制处在一个较好的水平。
2. Mysql账户权限安全
mysql中存在4个控制权限的表,分别为
1. mysql.USER表
2. mysql.DB表
3. mysql.TABLES_PRIV表
4. mysql.COLUMNS_PRIV表
要注意的是,Mysql中有一个数据库"information_schema",似乎里面保存的也是一些权限信息,但是要明白的是,这个数据库"information_schema"是为系统管理员提供元数据的一个简便方式,它实际上是一个视图,可以理解为对Mysql中的一个信息的封装,对于Mysql主程序来说,身份认证和授权的信息的来源只有一个,就是"mysql"。
http://www.cnblogs.com/hzhida/archive/2012/08/08/2628826.html
0×1. mysql.USER表
select * from USER;
desc USER;
mysql> desc USER;+------------------------+-----------------------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+------------------------+-----------------------------------+------+-----+---------+-------+
| Host | char(60) | NO | PRI | | |
| User | char(16) | NO | PRI | | |
| Password | char(41) | NO | | | |
| Select_priv | enum('N','Y') | NO | | N | |
| Insert_priv | enum('N','Y') | NO | | N | |
| Update_priv | enum('N','Y') | NO | | N | |
| Delete_priv | enum('N','Y') | NO | | N | |
| Create_priv | enum('N','Y') | NO | | N | |
| Drop_priv | enum('N','Y') | NO | | N | |
| Reload_priv | enum('N','Y') | NO | | N | |
| Shutdown_priv | enum('N','Y') | NO | | N | |
| Process_priv | enum('N','Y') | NO | | N | |
| File_priv | enum('N','Y') | NO | | N | |
| Grant_priv | enum('N','Y') | NO | | N | |
| References_priv | enum('N','Y') | NO | | N | |
| Index_priv | enum('N','Y') | NO | | N | |
| Alter_priv | enum('N','Y') | NO | | N | |
| Show_db_priv | enum('N','Y') | NO | | N | |
| Super_priv | enum('N','Y') | NO | | N | |
| Create_tmp_table_priv | enum('N','Y') | NO | | N | |
| Lock_tables_priv | enum('N','Y') | NO | | N | |
| Execute_priv | enum('N','Y') | NO | | N | |
| Repl_slave_priv | enum('N','Y') | NO | | N | |
| Repl_client_priv | enum('N','Y') | NO | | N | |
| Create_view_priv | enum('N','Y') | NO | | N | |
| Show_view_priv | enum('N','Y') | NO | | N | |
| Create_routine_priv | enum('N','Y') | NO | | N | |
| Alter_routine_priv | enum('N','Y') | NO | | N | |
| Create_user_priv | enum('N','Y') | NO | | N | |
| Event_priv | enum('N','Y') | NO | | N | |
| Trigger_priv | enum('N','Y') | NO | | N | |
| Create_tablespace_priv | enum('N','Y') | NO | | N | |
| ssl_type | enum('','ANY','X509','SPECIFIED') | NO | | | |
| ssl_cipher | blob | NO | | NULL | |
| x509_issuer | blob | NO | | NULL | |
| x509_subject | blob | NO | | NULL | |
| max_questions | int(11) unsigned | NO | | 0 | |
| max_updates | int(11) unsigned | NO | | 0 | |
| max_connections | int(11) unsigned | NO | | 0 | |
| max_user_connections | int(11) unsigned | NO | | 0 | |
| plugin | char(64) | YES | | | |
| authentication_string | text | YES | | NULL | |
| password_expired | enum('N','Y') | NO | | N | |
+------------------------+-----------------------------------+------+-----+---------+-------+
0×2. mysql.DB表
select * from DB;
desc DB;
mysql> desc DB; +-----------------------+---------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-----------------------+---------------+------+-----+---------+-------+
| Host | char(60) | NO | PRI | | |
| Db | char(64) | NO | PRI | | |
| User | char(16) | NO | PRI | | |
| Select_priv | enum('N','Y') | NO | | N | |
| Insert_priv | enum('N','Y') | NO | | N | |
| Update_priv | enum('N','Y') | NO | | N | |
| Delete_priv | enum('N','Y') | NO | | N | |
| Create_priv | enum('N','Y') | NO | | N | |
| Drop_priv | enum('N','Y') | NO | | N | |
| Grant_priv | enum('N','Y') | NO | | N | |
| References_priv | enum('N','Y') | NO | | N | |
| Index_priv | enum('N','Y') | NO | | N | |
| Alter_priv | enum('N','Y') | NO | | N | |
| Create_tmp_table_priv | enum('N','Y') | NO | | N | |
| Lock_tables_priv | enum('N','Y') | NO | | N | |
| Create_view_priv | enum('N','Y') | NO | | N | |
| Show_view_priv | enum('N','Y') | NO | | N | |
| Create_routine_priv | enum('N','Y') | NO | | N | |
| Alter_routine_priv | enum('N','Y') | NO | | N | |
| Execute_priv | enum('N','Y') | NO | | N | |
| Event_priv | enum('N','Y') | NO | | N | |
| Trigger_priv | enum('N','Y') | NO | | N | |
+-----------------------+---------------+------+-----+---------+-------+
0×3. mysql.TABLES_PRIV表
select * from TABLES_PRIV;
desc TABLES_PRIV;
mysql> desc TABLES_PRIV;
+-------------+-----------------------------------------------------------------------------------------------------------------------------------+------+-----+-------------------+-----------------------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+-----------------------------------------------------------------------------------------------------------------------------------+------+-----+-------------------+-----------------------------+
| Host | char(60) | NO | PRI | | |
| Db | char(64) | NO | PRI | | |
| User | char(16) | NO | PRI | | |
| Table_name | char(64) | NO | PRI | | |
| Grantor | char(77) | NO | MUL | | |
| Timestamp | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
| Table_priv | set('Select','Insert','Update','Delete','Create','Drop','Grant','References','Index','Alter','Create View','Show view','Trigger') | NO | | | |
| Column_priv | set('Select','Insert','Update','References') | NO | | | |
+-------------+-----------------------------------------------------------------------------------------------------------------------------------+------+-----+-------------------+-----------------------------+
0×4. mysql.COLUMNS_PRIV表
select * from COLUMNS_PRIV;
desc COLUMNS_PRIV;
mysql> desc COLUMNS_PRIV; +-------------+----------------------------------------------+------+-----+-------------------+-----------------------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+----------------------------------------------+------+-----+-------------------+-----------------------------+
| Host | char(60) | NO | PRI | | |
| Db | char(64) | NO | PRI | | |
| User | char(16) | NO | PRI | | |
| Table_name | char(64) | NO | PRI | | |
| Column_name | char(64) | NO | PRI | | |
| Timestamp | timestamp | NO | | CURRENT_TIMESTAMP | on update CURRENT_TIMESTAMP |
| Column_priv | set('Select','Insert','Update','References') | NO | | | |
+-------------+----------------------------------------------+------+-----+-------------------+-----------------------------+
以上是这4个表的基本结构,在我们进行数据库连接、登录的时候,mysql权限表的验证过程为:
1. 先从user表中的:
1) Host
2) User
3) Password
这3个字段中判断连接的ip、用户名、密码是否存在,存在则通过验证。
2. 通过身份认证后,进行权限分配,按照:
1) user
2) db
3) tables_priv
4) columns_priv
的顺序进行验证。
即先检查全局权限表user,如果user中对应的权限为Y,则此用户对所有数据库的权限都为Y,将不再检查db,tables_priv,columns_priv
如果全局权限表user对应的权限为N,则到db表中检查此用户对应的具体数据库,并得到db中为Y的权限
如果db中为N,则检查tables_priv中此数据库对应的具体表,取得表中的权限Y,以此类推。逐级下降
用流程图表示如下:
了解了Mysql的账户权限原理和判断流程,我们接下来需要了解就是应该以怎样的方式去进行权限配置,才能达到所谓的"最小权限原则"呢?Mysql的账户权限优先级顺序是:
user->db->tables_priv->columns_pri
稍作思考,我们可以发现,这些表的作用本质上是一样的,区别就在于它们的作用域范围不同,从user到columns_pri逐级作用域范围降低,因此控制粒度也增大,它们的配置遵循"就近原则",即以优先级最低的那个为准,所以,我们在进行Mysql的账户权限安全配置的时候会发现"我们似乎在做很多重复性的工作"。但我们要明白的,Mysql的这种逐层次的权限配置体系为我们提供了一个细粒度的控制方法。所以我们的权限配置也应该按照这个顺序来有规划地进行。
0×1. USER表
权限
| 权限级别
| 权限说明
| 最佳安全实践: 网站使用账户是否给予
| CREATE
| 数据库、表或索引
| 创建数据库、表或索引权限
| 建议给与,安装WEB系统时需要创建表
| DROP
| 数据库或表
| 删除数据库或表权限
| 建议给与
| GRANT OPTION
| 数据库、表或保存的程序
| 赋予权限选项
| 不建议给与
| REFERENCES
| 数据库或表
| 无
| 不建议给与
| ALTER
| 表
| 更改表,比如添加字段、索引等
| 建议给与
| DELETE
| 表
| 删除数据权限
| 建议给与
| INDEX
| 表
| 索引权限
| 建议给与
| INSERT
| 表
| 插入权限
| 建议给与
| SELECT
| 表
| 查询权限
| 建议给与
| UPDATE
| 表
| 更新权限
| 建议给与
| CREATE VIEW
| 视图
| 创建视图权限
| 建议给与
| SHOW VIEW
| 视图
| 查看视图权限
| 建议给与
| ALTER ROUTINE
| 存储过程
| 更改存储过程权限
| 不建议给与
| CREATE ROUTINE
| 存储过程
| 创建存储过程权限
| 不建议给与
| EXECUTE
| 存储过程
| 执行存储过程权限
| 不建议给与
| FILE
| 服务器主机上的文件访问
| 文件访问权限
| 不建议给与,防止因为注入导致的隐私文件泄漏
| CREATE TEMPORARY TABLES
| 服务器管理
| 创建临时表权限
| 不建议给与,防止借助临时表发动的二次注入
| LOCK TABLES
| 服务器管理
| 锁表权限
| 不建议给与
| CREATE USER
| 服务器管理
| 创建用户权限
| 不建议给与
| PROCESS
| 服务器管理
| 查看进程权限
| 不建议给与
| RELOAD
| 服务器管理
| 执行flush-hosts, flush-logs, flush-privileges, flush-status,flush-tables, flush-threads, refresh, reload等命令的权限
| 不建议给与
| REPLICATION CLIENT
| 服务器管理
| 复制权限
| 不建议给与
| REPLICATION SLAVE
| 服务器管理
| 复制权限
| 不建议给与
| SHOW DATABASES
| 服务器管理
| 查看数据库列表权限
| 不建议给与
| SHUTDOWN
| 服务器管理
| 关闭数据库权限
| 不建议给与
| SUPER
| 服务器管理
| 执行kill线程权限
| 不建议给与
| 从表格中可以看到,USER表主要针对数据库的账户进行粗粒度的权限控制,定义了"某人允许做什么事"。
0×2. DB表
权限
| 说明
| 网站使用账户是否给予
| Select
| 可对其下所有表进行查询
| 建议给予
| Insert
| 可对其下所有表进行插入
| 建议给予
| Update
| 可对其下所有表进行更新
| 建议给予
| Delete
| 可对其下所有表进行删除
| 建议给予
| Create
| 可在此数据库下创建表或者索引
| 建议给予
| Drop
| 可删除此数据库,及此数据库下的表
| 不建议给予
| Grant
| 赋予权限选项
| 不建议给予
| References
| 未来MySQL特性的占位符
| 不建议给予
| Index
| 可对其下的所有表进行索引
| 建议给予
| Alter
| 可对其下的所有表进行更改
| 建议给予
| Create_tmp_table
| 创建临时表
| 不建议给予
| Lock_tables
| 可对其下所有表进行锁定
| 不建议给予
| Create_view
| 可在此数据下创建视图
| 建议给予
| Show_view
| 可在此数据下查看视图
| 建议给予
| Create_routine
| 可在此数据下创建存储过程
| 不建议给予
| Alter_routine
| 可在此数据下更改存储过程
| 不建议给予
| Execute
| 可在此数据下执行存储过程
| 不建议给予
| Event
| 可在此数据下创建事件调度器
| 不建议给予
| Trigger
| 可在此数据下创建触发器
| 不建议给予
| DB表可以看成是USER表对权限控制的一个补充,一个更细粒度地、针对数据库库级别的权限控制。
同时,DB表也隐式包含了将账户限定在某个数据库的范围内这个配置,即限制某个用户只能用对它自己的数据库的控制权,对不属于它的数据库禁止操作,这能有效防止横向越权的发生。
mysql> select host,db,user from db;
+------+---------+------+
| host | db | user |
+------+---------+------+
| % | test | |
| % | test\_% | |
+------+---------+------+
可以看到,user字段为空,表示当前不对用户做任何数据库属权限制,在网站的部署过程中,应该单独针对网站建立一个账户一个独立的数据库,并为这个账户分配唯一的专属数据库,防止网络被入侵后的横向、纵向提权。
对于Mysql中的账户权限相关的安全配置,总结如下:
1. 针对每个网站建立一个单独的账户
2. 为每个网站单独建立一个专属数据库(虽然DEDE、DZ普通采用表前缀的方法来实现"一库多站",但好的做法还是"一库一站")
3. 按照user->db->tables_priv->columns_pri的顺序进行细粒度的权限控制
4. 为每个用户单独配置一个专属数据库,保证当前用户的所有操作只能发生在它自己的数据库中,防止SQL注入发生后,黑客通过注入点访问到系统表
账户权限安全配置需要的常用命令
1. 新建一个用户并给予相应数据库的权限
grant select,insert,update,delete,create,drop privileges on database.* to user@localhost identified by 'passwd';
grant all privileges on database.* to user@localhost identified by 'passwd';
2. 刷新权限
flush privileges;
3. 显示授权
show grants;
4. 移除授权
revoke delete on *.* from 'user'@'localhost';
5. 删除用户
drop user 'user'@'localhost';
6. 给用户改名
rename user 'jack'@'%' to 'jim'@'%';
7. 给用户改密码
SET PASSWORD FOR 'root'@'localhost' = PASSWORD('123456');
3. Mysql数据的网络安全配置
对数据库所在的DMZ的网络拓朴的安全配置也是我们在进行安全评估的时候需要考虑的一个方面,这对防御内网扫描、网络攻击有一定帮助。
0×1: 限制访问Mysql端口的IP
对Mysql的访问IP的限制,可以从应用层和主机层来分别达到目的
1. 主机层:
1) windows可以通过windows防火墙
2) Linux下可以通过iptables
来限制允许访问mysql端口的IP地址
//只允许指定的IP进行访问
iptables -A INPUT -p tcp -s xxxx.xxxx.xxxx.xxxx/24 --dport 3306 -j ACCEPT
iptables -A INPUT -p tcp -s xxxx.xxxx.xxxx.xxxx/24 --dport 3306 -j ACCEPT
..
iptables -P INPUT DROP
可以看到,这种方法的缺点是硬编码导致灵活性低,如果mysql的默认端口3306被修改了(例如8890),则这条iptables规则也需要相应的修改
mysql> select host,user,password from user;
+-----------+------+-------------------------------------------+
| host | user | password |
+-----------+------+-------------------------------------------+
| localhost | root | *832EB84CB764129D05D498ED9CA7E5CE9B8F83EB |
| . | root | *832EB84CB764129D05D498ED9CA7E5CE9B8F83EB |
| :: | root | *832EB84CB764129D05D498ED9CA7E5CE9B8F83EB |
| localhost | | |
+-----------+------+-------------------------------------------+
这几行记录表明了root这个账户只能是本机登录,在部署的过程中,我们可以为指定账户添加某个安全的跳板机,并保证这个跳板机IP是不变的。
0×2: 修改mysql的端口
windows下可以修改配置文件my.ini来实现,linux可以修改配置文件my.cnf来实现。
port = 3306
对mysql端口的修改可以从一定程度上防止端口扫描工具的扫描。
0×3: 限制连接用户的数量
数据库的某用户多次远程连接,会导致性能的下降和影响其他用户的操作,有必要对其进行限制。可以通过限制单个账户允许的连接数量来实现,设置my.cnf文件的mysqld中的max_user_connections变量来完成。GRANT语句也可以支持资源控制选项来限制服务器对一个账户允许的使用范围。
#vi /etc/my.cnf
[mysqld]
max_user_connections 2
4. 密码策略安全
这里所谓的密文策略安全包括Mysql自身的账户密码的安全、也包括网站用户的密码安全。
0×1: mysql账户密码
因为mysql本身没有抗穷举的帐号锁定机制,所以对于mysql自身的登录帐号,尤其是root帐号,需要遵循"密码强度策略"设置高强度的密码,保证攻击者从穷举帐号攻击这条路无法获得合适的投资收益比。
0×2: 网站用户的密码
数据库管理员无法规定用户的网站使用什么样的密码策略,这完全取决于用户自己的编码习惯、或者说是站长所使用的CMS的编码习惯。这方面,mysql无法做的更多,但也许可以做的是建立一个针对网站密码穷举、脱库攻击的日志追溯系统,当发生脱库攻击时能第一时间给用户提供"事发现场"的更多信息。或者数据库管理员主动进行可控的撞库测试,提前帮助用户发生潜在的撞库、脱库风险。
5. Mysql日志
启动Mysql的日志不仅可以为我们提供性能热点的分析,还可以帮助我们加固Mysql数据库的安全,例如:
1. 从日志中获得典型SQL注入语句
2. 利用正则模型从日志中捕获注入攻击的发生
3. 在脱库、数据泄漏之后获得关于受攻击数据库的情况、泄漏范围等数据
mysql有以下几种日志,它们都在my.ini中进行配置:
1. 错误日志:-log-err
log-error=E:/wamp/logs/mysql_error.log
2. 查询日志(记录所有SQL语句):-log
log=E:/wamp/logs/mysql.log
3. 慢查询日志:-log-slow-queries
1) 查看慢查询时间
show variables like "long_query_time";默认10s
2) 查看慢查询配置情况
show status like "%slow_queries%";
3) 查看慢查询日志路径
show variables like "%slow%";
//存储位置、长SQL的阈值
E:\wamp\bin\mysql\mysql5.6.12\data\LittleHann-PC-slow.log
long_query_time=5
4. 更新日志:-log-update
5. 二进制日志:-log-bin//记录除select语句之外的所有sql语句到日志中,可以用来恢复数据文件log-bin=E:/wamp/logs/bin
查看日志开启情况:
show variables like 'log_%';
+----------------------------------------+----------------------------------------------------+
| Variable_name | Value &n |
|