360网站管家 发表于 2015-10-9 11:38:58

MySQL安全配置详解

目录

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,以此类推。逐级下降

用流程图表示如下:
http://upload.server110.com/image/20141031/00554Q157-0.gif
了解了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

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
页: [1]
查看完整版本: MySQL安全配置详解