前言 mysql
对于任何一个企业来讲,其数据库系统中所保存数据的安全性无疑是很是重要的,尤为是公司的有些商业数据,可能数据就是公司的根本,失去了数据的安全性,可能就是失去了公司的一切。本章将针对 MySQL 的安全相关内容进行较为详细的介绍。 sql
MySQL 的大部分应用场景都是基于网络环境的,而网络自己是一个充满各类入侵危险的环境,因此要保护他的安全,在条件容许的状况下,就应该从最外围的网络环境开始"布防",由于这一层防线能够从最大范围内阻止可能存在的威胁。 数据库
在网络环境中,任意两点之间均可能存在无穷无尽的"道路"能够抵达,是一个真正"条条道路通罗马"的环境。在那许许多多的道路中,只要有一条道路不够安全,就可能被入侵者利用。固然,因为所处的环境不一样,潜在威胁的来源也会不同。有些 MySQL 所处环境是暴露在整个广域网中,能够说是彻底"裸露"在任何能够接入网络环境的潜在威胁者面前。而有些 MySQL 是在一个环境相对小一些的局域网以内,相对来讲,潜在威胁者也会少不少。处在局域网以内的 MySQL,因为有局域网出入口的网络设备的基本保护,相对于暴露在广域网中要安全很多,主要威胁对象基本上控制在了能够接入局域网的内部潜在威胁者,和极少数可以突破最外围防线(局域网出入口的安全设备)的入侵者。因此,尽量的让咱们的 安全
MySQL 处在一个有保护的局域网之中,是很是必要的。 服务器
有了网络设备的保护,咱们的 MySQL 就足够安全了么?我想你们都会给出否认的回答。由于即便咱们局域网出入口的安全设备足够的强大,能够拦截住外围试图入侵的全部威胁者,但若是威胁来自局域网内部呢?好比局域网中可能存在被控制的设备,某些被控制的有权限接入局域网的设备,以及内部入侵者等都仍然是威胁者。因此说,吉使在第一层防线以内,咱们仍然存在安全风险,局域网内部仍然会有很多的潜在威胁存在。 网络
这个时候就须要咱们部署第二道防线"主机层防线"了 。"主机层防线"主要拦截网络(包括局域网内)或者直连的未受权用户试图入侵主机的行为。由于一个恶意入侵者在登陆到主机以后,可能经过某些软件程序窃取到那些自身安全设置不够健壮的数据库系统的登入口令,从而达到窃取或者破坏数据的目的。如一个主机用户能够经过一个未删除且未设置密码的无用户名本地账户轻易登入数据库,也能够经过 MySQL 初始安装好以后就存在的无密码的"root@localhost"用户登陆数据库并得到数据库最高控制权限。非法用户除了经过登入数据库获取(或者破坏)数据以外,还可能经过主机上面相关权限设置的漏洞,跳过数据库而直接获取 MySQL 数据(或者日志)文件达到窃取数据的目的,或者直接删除数据(或者日志)文件达到破坏数据的目的。 session
经过第二道防线"主机层防线"的把守,咱们又能够挡住很大一部分安全威胁者。但仍然可能有极少数突破防线的入侵者。并且即便没有任何"漏网之鱼",那些有主机登入权限的使用者呢?是否真的就是彻底可信任对象?No,咱们不能轻易冒这个潜在风险。对于一个有足够安全意识的管理员来讲,是不会轻易听任任何一个潜在风险存在的。 架构
这个时候,咱们的第三道防线,"数据库防线"就须要发挥他的做用了。"数据库防线" 也就是 MySQL 数据库系统自身的访问控制受权管理相关模块。这道防线基本上能够说是 MySQL 的最后一道防线了,也是最核心最重要的防线。他首先须要可以抵挡住在以前的两层防线都没有可以阻拦住的全部入侵威胁,同时还要可以限制住拥有以前二层防线自由出入但不具有数据库访问权限的潜在威胁者,以确保数据库自身的安全以及所保存数据的安全。 dom
以前的二层防线对于全部数据库系统来讲基本上区别不大,都存在着基本相同的各类威胁,不管是 Oracle 仍是 MySQL,以及任何其余的数据库管理系统,都须要基本一致的"布防"策略。可是这第三层防线,也就是各自自身的"数据库防线"对于每一个数据库系统来讲都存在较大的差别,由于每种数据库都有各自不太同样的专门负责访问受权相关功能的模块。不管是权限划分仍是实现方式均可能不太同样。 socket
对于 MySQL 来讲,其访问受权相关模块主要是由两部分组成。一个是基本的用户管理模块,另外一个是访问受权控制模块。用户管理模块的功能相对简单一些,主要是负责用户登陆链接相关的基本权限控制,但其在安全控制方面的做用却不比任何环节小。他就像 MySQL 的一个"大门门卫"同样,经过校验每一位敲门者所给的进门"暗号"(登入口令),决定是否给敲门者开门。而访问受权控制模块则是随时随地检查已经进门的访问者,校验他们是否有访问所发出请求须要访问的数据的权限。经过校验者可顺利拿到数据,而未经过校验的访问者,只能收到"访问越权了"的相关反馈。
上面的三道防线组成了如图 4-1 所示的三道坚固的安全保护壁垒,就像三道坚固的城墙同样保护这 MySQL 数据库中的数据。只要保障足够,基本很难有人可以攻破这三道防线。
图 4-1
4、代码:
"SQL 注入攻击"这个术语我想大部分读者朋友都据说过了?指的就是攻击者根据数据库的 SQL 语句解析器的原理,利用程序中对客户端所提交数据的校验漏洞,从而经过程序动态提交数据接口提交非法数据,达到攻击者的入侵目的。
"SQL 注入攻击"的破坏性很是的大,轻者形成数据被窃取,重者数据遭到破坏,甚至可能丢失所有的数据。若是读者朋友还不是太清楚何为"SQL 注入攻击",建议经过互联网搜索一下,能够获得很是多很是详细的介绍及案例分析,这里有不作详细介绍了。
程序代码若是权限校验不够仔细而存在安全漏洞,则一样可能会被入侵者利用,达到窃取数据等目的。好比,一个存在安全漏洞的信息管理系统,很容易就可能窃取到其余一些系统的登入口令。以后,就能冠冕堂皇的轻松登陆相关系统达到窃取相关数据的目的。甚至还可能经过应用系统中保存不善的数据库系统链接登陆口令,从而带来更大的损失。
MySQL 的权限系统在实现上比较简单,相关权限信息主要存储在几个被称为 grant tables 的系统表中,即: mysql.User,mysql.db,mysql.Host,mysql.table_priv 和 mysql.column_priv。因为权限信息数据量比较小,并且访问又很是频繁,因此 Mysql 在启动的时候,就会将全部的权限信息都 Load 到内存中保存在几个特定的结构中。因此才有咱们每次手工修改了权限相关的表以后,都须要执行"FLUSH PRIVILEGES"命令从新加载 MySQL 的权限信息。固然,若是咱们经过 GRANT,REVOKE 或者 DROP USER 命令来修改相关权限,则不须要手工执行 FLUSH PRIVILEGES 命令,由于经过 GRANT,REVOKE 或者 DROP USER 命令所作的权限修改在修改系统表的同时也会更新内存结构中的权限信息。在 MySQL5.0.2 或更高版本的时候,MySQL 还增长了 CREATE USER 命令,以此建立无任何特别权限(仅拥有初始 USAGE 权限)的用户,经过 CREATE USER 命令建立新了新用户以后,新用户的信息也会自动更新到内存结构中。因此,建议读者通常状况下尽可能使用 GRANT,REVOKE,CREATE USER 以及 DROP USER 命令来进行用户和权限的变动操做,尽可能减小直接修改 grant tables 来实现用户和权限变动的操做。
要为某个用户受权,可使用 GRANT 命令,要去除某个用户已有的权限则使用 REVOKE 命令。固然,出了这二者以外还有一种比较暴力的办法,那就是直接更新 grant tables 系统表。当给某个用户受权的时候,不只须要指定用户名,同时还要指定来访主机。若是在受权的时候仅指定用户名,则 MySQL 会自动认为是对'username'@'%'受权。要去除某个用户的的权限一样也须要指定来访主机。
可能有些时候咱们还会须要查看某个用户目前拥有的权限,这能够经过两个方式实现,首先是经过执行"SHOW GRANTS FOR 'username'@'hostname'" 命令来获取以前该用户身上的全部受权。另外一种方法是查询 grant tables 里面的权限信息。
MySQL 中的权限分为五个级别,分别以下:
一、Global Level:
Global Level 的权限控制又称为全局权限控制,全部权限信息都保存在 mysql.user 表中。Global Level 的全部权限都是针对整个 mysqld 的,对全部的数据库下的全部表及全部字段都有效。若是一个权限是以 Global Level 来授予的,则会覆盖其余全部级别的相同权限设置。好比咱们首先给 abc 用户受权能够 UPDATE 指定数据库如 test 的 t 表,而后又在全局级别 REVOKE 掉了 abc 用户对全部数据库的全部表的 UPDATE 权限。则这时候的 abc 用户将再也不拥有用对 test.t 表的更新权限。Global Level 主要有以下这些权限(见表 4-1):
表 4-1
名称 |
版本支持 |
限制信息 |
||
ALTER |
ALL |
表结构更改权限 |
||
ALTER |
ROUTINE |
5.0.3+ |
procedure,function 和 trigger 等的 变动权限 |
|
CREATE |
ALL |
数据库,表和索引的建立权限 |
||
CREATE |
ROUTINE |
5.0.3+ |
procedure,function 和 trigger 等的 变动权限 |
|
CREATE TABLES |
TE |
MPORARY |
4.0.2+ |
临时表的建立权限 |
CREATE |
USER |
5.0.3+ |
建立用户的权限 |
|
CREATE |
VIEW |
5.0.1+ |
建立视图的权限 |
|
DELETE |
All |
删除表数据的权限 |
||
DROP |
All |
删除数据库对象的权限 |
||
EXECUTE |
5.0.3+ |
procedure,function 和 trigger 等的 执行权限 |
||
FILE |
All |
执行 LOAD DATA INFILE 和 SELECT ... INTO FILE 的权限 |
||
INDEX |
All |
在已有表上建立索引的权限 |
||
INSERT |
All |
数据插入权限 |
||
LOCK TABLES |
4.0.2+ |
执行 LOCK TABLES 命令显示给表加锁的权限 |
||
PROCESS |
All |
执行 SHOW PROCESSLIST 命令的权限 |
||
RELOAD |
All |
执行 FLUSH 等让数据库从新 Load 某些对象或者数据的命令的权限 |
||
REPLICATION |
CLIENT |
4.0.2+ |
执 行 SHOW MASTER STATUS 和 SHOW SLAVE STATUS 命令的权限 |
|
REPLICATION |
SLAVE |
4.0.2+ |
复制环境中 Slave 链接用户所须要的复制权限 |
|
SELECT |
All |
数据查询权限 |
||
SHOW DATABASES |
4.0.2+ |
执行 SHOW DATABASES 命令的权限 |
||
SHOW VIEW |
5.0.1+ |
执 行 SHOW CREATE VIEW 命 令 查 看 view 建立语句的权限 |
||
SHUTDOWN |
All |
MySQL Server 的 shut down 权 限( 如经过 mysqladmin 执行 shutdown 命令所使 用的链接用户) |
||
SUPER |
4.0.2+ |
执 行 kill 线 程 , CHANGE MASTER, PURGE MASTER LOGS, and SET GLOBAL 等命令的权限 |
||
UPDATE |
All |
更新数据的权限 |
||
USAGE |
All |
新建立用户后不授任何权限的时候所拥有的最小权限 |
要授予 Global Level 的权限,则只须要在执行 GRANT 命令的时候,用"*.*"来指定适
用范围是 Global 的便可,当有多个权限须要授予的时候,也并不须要屡次重复执行 GRANT 命令,只须要一次将全部须要的权限名称经过逗号(",")分隔开便可,以下:
root@localhost : mysql 05:14:35> GRANT SELECT,UPDATE,DELETE,INSERT ON *.*
TO 'def'@'localhost';
Query OK, 0 rows affected (0.00 sec)
二、Database Level
Database Level 是在 Global Level 之下,其余三个 Level 之上的权限级别,其做用域即为所指定整个数据库中的全部对象。与 Global Level 的权限相比,Database Level 主要少了如下几个权限:CREATE USER,FILE,PROCESS,RELOAD,REPLICATION CLIENT,REPLICATION SLAVE,SHOW DATABASES,SHUTDOWN,SUPER 和 USAGE 这几个权限,没有增长任何权限。以前咱们说过 Global Level 的权限会覆盖底下其余四层的相同权限,Database Level 也同样 ,虽然他本身可能会被 Global Level 的权限设置所覆盖,但同时他也能覆盖比他更下层的
Table,Column 和 Routine 这三层的权限。
若是要授予 Database Level 的权限,则能够有两种实现方式:
root@localhost : mysql 06:06:26> GRANT ALTER ON test.* TO 'def'@'localhost'; Query OK, 0 rows affected (0.00 sec)
root@localhost : test 06:12:45> SHOW GRANTS FOR def@localhost;
+------------------------------------------------------------------+
| Grants for def@localhost |
+------------------------------------------------------------------+
| GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO 'def'@'localhost' |
| GRANT ALTER ON `test`.* TO 'def'@'localhost' |
+------------------------------------------------------------------+
root@localhost : mysql 06:14:05> USE test; Database changed
root@localhost : test 06:13:10> GRANT DROP ON * TO 'def'@'localhost'; Query OK, 0 rows affected (0.00 sec)
root@localhost : test 06:15:26> SHOW GRANTS FOR def@localhost;
+------------------------------------------------------------------+
| Grants for def@localhost |
+------------------------------------------------------------------+
| GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO 'def'@'localhost' |
| GRANT DROP, ALTER ON `test`.* TO 'def'@'localhost' | +------------------------------------------------------------------+
在授予权限的时候,若是有相同的权限须要授予多个用户,咱们也能够在受权语句中一次写上多个用户信息,经过逗号(,)分隔开就能够了,以下:
root@localhost : mysql 05:22:32> grant create on perf.* to
'abc'@'localhost','def'@'localhost'; Query OK, 0 rows affected (0.00 sec)
root@localhost : mysql 05:22:46> SHOW GRANTS FOR def@localhost;
+------------------------------------------------------------------+
| Grants for def@localhost |
+------------------------------------------------------------------+
| GRANT SELECT, INSERT, UPDATE, DELETE ON *.* TO 'def'@'localhost' |
| GRANT DROP, ALTER ON `test`.* TO 'def'@'localhost' |
| GRANT CREATE ON `perf`.* TO 'def'@'localhost' |
+------------------------------------------------------------------
+
3 rows in set (0.00 sec)
root@localhost : mysql 05:23:13> SHOW GRANTS FOR abc@localhost;
+------------------------------------------------------------------+
| Grants for abc@localhost |
+------------------------------------------------------------------+
| GRANT CREATE ON `perf`.* TO 'abc'@'localhost' |
| GRANT SELECT ON `test`.* TO 'abc'@'localhost' |
+------------------------------------------------------------------+
3 rows in set (0.00 sec)
三、Table Level
Database Level 之下就是 Table Level 的权限了,Table Level 的权限能够被 Global Level 和 Database Level 的权限所覆盖,同时也能覆盖 Column Level 和 Routine Level 的权限。
Table Level 的权限做用范围是受权语句中所指定数据库的指定表。如能够经过以下语句给 test 数据库的 t1 表受权: root@localhost : test 12:02:15> GRANT INDEX ON test.t1 TO
'abc'@'%.jianzhaoyang.com';
Query OK, 0 rows affected, 1 warning (0.00 sec)
root@localhost : test 12:02:53> SHOW GRANTS FOR 'abc'@'%.jianzhaoyang.com';
+----------------------------------------------------------+
| Grants for abc@*.jianzhaoyang.com |
+----------------------------------------------------------+
| GRANT USAGE ON *.* TO 'abc'@'%.jianzhaoyang.com' |
| GRANT INDEX ON `test`.`t1` TO 'abc'@'%.jianzhaoyang.com' | +----------------------------------------------------------+
上面的受权语句在测试给 test 数据库的 t1 表授予 Table Level 的权限的同时,还测试了将权限授予含有通配符"%"的全部".jianzhaoyang.com"主机。其中的 USAGE 权限是每一个用户都有的最基本权限。
Table Level 的权限因为其做用域仅限于某个特定的表,因此权限种类也比较少,仅有
ALTER,CREATE,DELETE,DROP,INDEX,INSERT,SELECT UPDATE 这八种权限。
四、Column Level
Column Level 的权限做用范围就更小了,仅仅是某个表的指定的某个(活某些)列。因为权限的覆盖原则,Column Level 的权限一样能够被 Global,Database,Table 这三个级别的权限中的相同级别所覆盖,并且因为 Column Level 所针对的权限和 Routine Level 的权限做用域没有重合部分,因此不会有覆盖与被覆盖的关系。针对 Column Level 级别的权限仅有 INSERT,SELECT 和 UPDATE 这 三 种 。Column Level 的权限受权语句语法基本和 Table
Level 差很少,只是须要在权限名称后面将须要受权的列名列表经过括号括起来,以下:
root@localhost : test 12:14:46> GRANT SELECT(id,value) ON test.t2 TO
'abc'@'%.jianzhaoyang.com'; Query OK, 0 rows affected(0.00 sec)
root@localhost : test 12:16:49> SHOW GRANTS FOR 'abc'@'%.jianzhaoyang.com'; +-----------------------------------------------------------------------+
| Grants for abc@*.jianzhaoyang.com |
+-----------------------------------------------------------------------+
| GRANT USAGE ON *.* TO 'abc'@'%.jianzhaoyang.com' |
| GRANT SELECT (value, id) ON `test`.`t2` TO 'abc'@'%.jianzhaoyang.com' |
| GRANT INDEX ON `test`.`t1` TO 'abc'@'%.jianzhaoyang.com' |
+-----------------------------------------------------------------------+
注意:当某个用户在向某个表插入(INSERT)数据的时候,若是该用户在该表中某列上面没有 INSERT 权限,则该列的数据将以默认值填充。这一点和不少其余的数据库都有一些区别,是 MySQL 本身在 SQL 上面所作的扩展。
五、Routine Level
Routine Level 的权限主要只有 EXECUTE 和 ALTER ROUTINE 两种,主要针对的对象是 procedure 和 function 这两种对象,在授予 Routine Level 权限的时候,须要指定数据库和相关对象,如:
root@localhost : test 04:03:26> GRANT EXECUTE ON test.p1 to
'abc'@'localhost';
Query OK, 0 rows affected (0.00 sec)
除了上面几类权限以外,还有一个很是特殊的权限 GRANT,拥有 GRANT 权限的用户能够将自身所拥有的任何权限所有授予其余任何用户,因此 GRANT 权限是一个很是特殊也很是重要的权限。GRANT 权限的授予方式也和其余任何权限都不太同样,一般都是经过在执行 GRANT 受权语句的时候在最后添加 WITH GRANT OPTION 子句达到授予 GRANT 权限的目的。
此外,咱们还能够经过 GRANT ALL 语句授予某个 Level 的全部可用权限给某个用户,
如:
root@localhost : test 04:15:48> grant all on test.t5 to 'abc'; Query OK, 0 rows affected (0.00 sec)
root@localhost : test 04:27:39> grant all on perf.* to 'abc'; Query OK, 0 rows affected (0.00 sec)
root@localhost : test 04:27:52> show grants for 'abc'; +--------------------------------------------------+
| Grants for abc@% |
+--------------------------------------------------+
| GRANT USAGE ON *.* TO 'abc'@'%' |
| GRANT ALL PRIVILEGES ON `perf`.* TO 'abc'@'%' |
| GRANT ALL PRIVILEGES ON `test`.`t5` TO 'abc'@'%' | +--------------------------------------------------+
在以上五个 Level 的权限中,Table、Column 和 Routine 三者在受权中所依赖(或者引用)的对象必须是已经存在的,而不像 Database Level 的权限授予,能够在当前不存在该数据库的时候就完成受权。
MySQL 访问控制实际上由两个功能模块共同组成,从第一篇的第二章架构组成中能够看到,一个是负责"看守 MySQL 大门"的用户管理模块,另外一个就是负责监控来访者每个动做的访问控制模块。用户管理模块决定造访客人可否进门,而访问控制模块则决定每一个客人进门能拿什么不能拿什么。下面是一张 MySQL 中实现访问控制的简单流程图(见图 4-2):
图 4-2 一、 用户管理咱们先看看用户管理模块是如何工做的。在 MySQL 中,用户访问控制部分的实现比较简单,全部受权用户都存放在一个系统表中:mysql.user,固然这个表不只仅存放了受权用户的基本信息,还存放有部分细化的权限信息。用户管理模块须要使用的信息不多,主要就是
Host,User,Password 这三项,都在 mysql.user 表中,以下: sky@localhost : (none) 12:35:04> USE mysql; Database changed sky@localhost : mysql 12:35:08> DESC user;
+---------------+--------------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+---------------+--------------------+------+-----+---------+-------+
| Host |
| char(60) |
| NO |
| PRI | |
| |
| |
| User |
| char(16) |
| NO |
| PRI | |
| |
| |
| Password |
| char(41) |
| NO |
| | |
| |
| |
... ...
+---------------+--------------------+------+-----+---------+-------+
一个用户要想访问 MySQL,至少须要提供上面列出的这三项数据,MySQL 才能判断是否
该让他"进门"。这三项实际上由量部分组成:访问者来源的主机名(或者主机 IP 地址信息 )和访问者的来访"暗号"(登陆用户名和登陆密码),这两部分中的任何一个没有可以匹配上都没法让看守大门的用户管理模块乖乖开门。其中 Host 信息存放的是 MySQL 容许所对应的 User 的信任主机,能够是某个具体的主机名(如:mytest)或域名(如:www.domain.com),也能够是以"%"来充当通配符的某个域名集合(如:%.domain.com);也能够是一个具体的 IP 地址(如:1.2.3.4),一样也能够是存在通配符的域名集合(如:1.2.3.%);还能够用"%" 来表明任何主机,就是不对访问者的主机作任何限制。如如下设置:
root@localhost : mysql 01:18:12> SELECT host,user,password FROM user ORDER BY user; +--------------------+------+-------------------------------------------+
| host | user | password |
+--------------------+------+-------------------------------------------+
| % | abc |
| | |
| *.jianzhaoyang.com | abc |
| | |
| localhost | abc |
| *2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19 | |
| 1.2.3.4 | abc |
| *2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19 | |
| 1.2.3.* | def |
| *2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19 | |
| % | def |
| *2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19 | |
| localhost | def |
| *2470C0C06DEE42FD1618BB99005ADCA2EC9D1E19 | |
... ...
+--------------------+------+-------------------------------------------+
可是这里有一个比较特殊的访问限制,若是要经过 localhost 访问的话,必需要有一条专门针对 localhost 的受权信息,即便不对任何主机作限制也不行。以下例所示,存在 def@% 的用户设置,可是若是不使用-h 参数来访问,则登陆会被拒绝,由于 mysql 在默认状况下会链接 localhost:
sky@sky:~$ mysql -u def -p Enter password:
ERROR 1045 (28000): Access denied for user 'def'@'localhost' (using password: YES)
可是当经过-h 参数,明确指定了访问的主机地址以后就没问题了,以下:
sky@sky:~$ mysql -u def -p -h 127.0.0.1 Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 17
Server version: 5.0.51a-log Source distribution
Type 'help;' or '\h' for help. Type '\c' to clear the buffer. def@127.0.0.1 : (none) 01:26:04>
若是咱们有一条 localhost 的访问受权则能够不使用-h 参数来指定登陆 host 而链接默认的 localhost: sky@sky:~$ mysql -u abc -p Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 18
Server version: 5.0.51a-log Source distribution
Type 'help;' or '\h' for help. Type '\c' to clear the buffer.
abc@localhost : (none) 01:27:19> exit Bye
若是 MySQL 正在运行之中的时候,咱们对系统作了权限调整,那调整以后的权限什么时
候会生效呢?
咱们先了解什么时候 MySQL 存放于内存结构中的权限信息被更新:FLUSH PRIVILEGES 会强行让 MySQL 更新 Load 到内存中的权限信息;GRANT、REVOKE 或者 CREATE USER 和 DROP USER 操做会直接更新内存中俄权限信息;重启 MySQL 会让 MySQL 彻底从 grant tables 中读取权限信息。
那内存结构中的权限信息更新以后对已经链接上的用户什么时候生效呢?对于 Global Level 的权限信息的修改,仅仅只有更改以后新建链接才会用到,对于已经链接上的 session 并不会受到影响。而对于 Database Level 的权限信息的修改,只有当客户端请求执行了"USE database_name"命令以后,才会在从新校验中使用到新的权限信息。因此有些时候若是在作了比较紧急的 Global 和 Database 这两个 Level 的权限变动以后 ,可能须要经过"KILL"命令将已经链接在 MySQL 中的 session 杀掉强迫他们从新链接以使用更新后的权限。对于 Table Level 和 Column Level 的权限,则会在下一次须要使用到该权限的 Query 被请求的时候生效,也就是说,对于应用来说,这两个 Level 的权限,更新以后马上就生效了,而不会须要执行"KILL"命令。
二、 访问控制
当客户端链接经过用户管理模块的验证,可链接上 MySQL Server 以后,就会发送各类 Query 和 Command 给 MySQL Server,以实现客户端应用的各类功能。当 MySQL 接收到客户端的请求以后,访问控制模块是须要校验该用户是否知足提交的请求所须要的权限。权限校验过程是从最大范围的权限往最小范围的权限开始依次校验所涉及到的每一个对象的每一个权限。
在验证全部所需权限的时候,MySQL 首先会查找存储在内存结构中的权限数据,首先查找 Global Level 权限,若是所需权限在 Global Level 都有定义(GRANT 或者 REVOKE),则完成权限校验(经过或者拒绝),若是没有找到全部权限的定义,则会继续日后查找 Database Level 权限,进行 Global Level 未定义的所需权限的校验,若是仍然没有可以找到全部所需权限的定义,MySQL 会继续往更小范围的权限定义域查找,也就是 Table
Level,最后则是 Column Level 或者 Routine Level。
下面咱们就以客户端经过 abc@localhost 链接后请求以下 Query 我为例:
SELECT id,name FROM test.t4 where status = 'deleted';
图 4-3
在前面咱们了解到 MySQL 的 grant tables 有 mysql.user,mysql.db,mysql.host, mysql.table_priv 和 mysql.column_priv 这五个,我想出了 mysql.host 以外的四个都是很是容易理解的,每个表针对 MySQL 中的一种逻辑对象,存放某一特定 Level 的权限,惟独 mysql.host 稍有区别。咱们如今就来看看 mysql.host 权限表到底在 MySQL 的访问控制中充当了一个什么样的角色呢?
mysql.host 在 MySQL 访问控制模块中所实现的功能比较特殊,和其余几个 grant tables 不太同样。首先是 mysql.host 中的权限数据不是(也不能)经过 GRANT 或者 REVOKE 来授予或者去除,必须经过手工经过 INSERT、UPDATE 和 DELETE 命令来修改其中的数据。其次是其中的权限数据没法单独生效,必须经过和 mysql.db 权限表的数据一块儿才能生效。并且仅当 mysql.db 中存在不完整(某些场景下的特殊设置)的时候,才会促使访问控制模块再结合 mysql.host 中查找是否有相应的补充权限数据实现以达到权限校验的目的,就好比上图中所示。在 mysql.db 中没法找到知足权限校验的全部条件的数据(db.User = 'abc' AND db.host = 'localhost' AND db.Database_name = 'test'),则说明在 mysql.db 中没法完成权限校验,因此也不会直接就校验 db.Select_priv 的值是否为'Y'。可是 mysql.db 中有 db.User = 'abc' AND db.Database_name = 'test' AND db.host = '' 这样一条权限信息存在,你们可能注意到了这条权限信息中的 db.host 中是空值,注意是空值而不是'%'这个通配符哦。当 MySQL 注意到有这样一条权限信息存在的时候,就该是 mysql.host 中所存放的权限信息出场的时候了。这时候,MySQL 会检测 mysql.host 中是否存在知足以下条件的权限信息:host.Host = 'localhost' AND host.Db = 'test'。若是存在,则开始进行 Select_priv 权限的校验。因为权限信息存在于 mysql.db 和 mysql.host 二者之中,并且是二者信息合并才能知足要求,因此 Select_priv 的校验也须要两表都为'Y'才能知足要求,经过校验。
咱们已经清楚,MySQL 的权限是授予"username@hostname"的,也就是说,至少须要用户名和主机名两者才能肯定一个访问者的权限。又因为 hostname 能够是一个含有通配符的域名,也能够是一个含有通配符的 IP 地址段。那么若是同一个用户有两条权限信息,一条是针对特定域名的,另一个是含有通配符的域名,并且前者属于后者包含。这时候 MySQL 如何来肯定权限信息呢?实际上 MySQL 永远优先考虑更精确范围的权限。在 MySQL 内部会按照 username 和 hostname 做一个排序,对于相同 username 的权限,其 host 信息越接近访问者的来源 host,则排序位置越靠前,则越早被校验使用到。并且,MySQL 在权限校验过程当中 ,只要找到匹配的权限以后,就不会再继续日后查找是否还有匹配的权限信息,而直接完成校验过程。
你们应该也看到了在 mysql.user 这个权限表中有 max_questions,max_updates, max_connections,max_user_connections 这四列,前面三列是从 MySQL4.0.2 版本才开始有的,其功能是对访问用户进行每小时所使用资源的限制,而最后的 max_user_connections 则是从 MySQL5.0.3 版本才开始有的,他和 max_connections 的区别是限制耽搁用户的链接总次数,而不是每小时的链接次数。而要使这四项限制生效,须要在建立用户或者给用户受权的时候加上如下四种子句:
max_questions : |
WITH MAX_QUERIES_PER_HOUR n; |
max_updates : |
WITH MAX_UPDATES_PER_HOUR n; |
max_connections : |
WITH MAX_CONNECTIONS_PER_HOUR n; |
max_user_connections: |
MAX_USER_CONNECTIONS。 |
四个子句能够同时使用,如:
" WITH MAX_QUERIES_PER_HOUR 5000 MAX_CONNECTIONS_PER_HOUR 10
MAX_USER_CONNECTIONS 10000"。
在咱们了解了影响数据库系统安全的相关因素以及 MySQL 权限系统的工做原理以后,就须要为咱们的系统设计一个安全合理的受权策略。我想,每一个人内心都清楚,要想受权最简单最简单方便,维护工做量最少,那天然是将全部权限都授予全部的用户来的最简单方便了 。可是,咱们你们确定也都知道,一个用户所用有的权限越大,那么他给咱们的系统所带来的潜在威胁也就越大。因此,从安全方面来考虑的话,权限天然是授予的越小越好。一个有足够安全意识的管理员在受权的时候,都会只授予必要的权限,而不会授予任何多余的权限。既然咱们这一章是专门讨论安全的,那么咱们如今也就从安全的角度来考虑如何设计一个更为安全合理的受权策略。
首先,须要了解来访主机。
因为 MySQL 数据库登陆验证用户的时候是出了用户名和密码以外,还要验证来源主机。因此咱们还须要了解每一个用户可能从哪些主机发起链接。固然,咱们也能够经过受权的时候直接经过"%"通配符来给全部主机都有访问的权限,可是这样做就违背了咱们安全策略的原则,带来了潜在风险,因此并不可取。尤为是在没有局域网的防火墙保护的状况下,更是不能轻易容许能够从任何主机登陆的用户存在。能经过具体主机名或者 IP 地址指定的尽可能经过使用具体的主机名和 IP 地址来限定来访主机,不能用具体的主机名或者 IP 地址限定的也须要用尽量小的通配范围来限定。
其次,了解用户需求。
既然是要作到仅授予必要的权限,那么咱们必须了解每一个用户所担当的角色,也就是说 ,咱们须要充分了解每一个用户须要链接到数据库上完成什么工做。了解该用户是一个只读应用的用户,仍是一个读写都有的账户;是一个备份做业的用户仍是一个平常管理的账户;是只须要访问特定的某个(或者某几个)数据库(Schema),仍是须要访问全部的数据库。只有了解了须要作什么,才能准确的了解须要授予什么样的权限。由于若是权限太低,会形成工做没法正常完成,而权限太高,则存在潜在的安全风险。
再次,要为工做分类。
为了作到各司其职,咱们须要将须要作的工做分门别类,不一样类别的工做使用不一样的用户,作好用户分离。虽然这样可能会带来管理成本方面的部分工做量增长,可是基于安全方面的考虑,这部分管理工做量的增长是很是值得的。并且咱们所须要作的用户分离也只是一个适度的分离。好比将执行备份工做、复制工做、常规应用访问、只读应用访问和平常管理工做分别分理出单独的特定账户来授予各自所需权限。这样,既可让安全风险尽可能下降,也可让同类同级别的类似权限合并在一块儿,不互相交织在一块儿。对于 PROCESS,FILE 和
SUPER 这样的特殊权限,仅仅只有管理类账号才须要,不该该授予其余非管理账号。
最后,确保只有绝对必要者拥有 GRANT OPTION 权限。
以前在权限系统介绍的时候咱们已经了解到 GRANT OPTION 权限的特殊性,和拥有该权限以后的潜在风险,因此在这里也就再也不累述了。总之,为了安全考虑,拥有 GRANT OPTION 权限的用户越少越好,尽量只让拥有超级权限的用户才拥有 GRANT OPTION 权限。
在前面咱们了解了影响数据库系统安全的几个因素,也了解了 MySQL 权限系统的相关原理和实现,这一节咱们将针对这些因素进行一些基本的安全设置讨论,了解一些必要的注意事项。
首先,天然是最外围第一层防线的网络方面的安全。
咱们首先要肯定咱们所维护的 MySQL 环境是否真的须要提供网络服务?是否可使咱们的 MySQL 仅仅提供本地访问,而禁止网络服务?若是能够,那么咱们能够在启动 MySQL 的时候经过使用"--skip-networking"参数选项,让 MySQL 不经过 TCP/IP 监听网络请求,而仅仅经过命名管道或共享内存(在 Windows 中)或 Unix 套接字文件(在 Unix 中)来和客户端链接交互。
固然,在本章最开始的时候,咱们就已经讨论过,因为 MySQL 数据库在大部分应用场景中都是在网络环境下,经过网络链接提供服务。因此咱们只有少部分应用能经过禁用网络监听来断绝网络访问以保持安全,剩下的大部分仍是须要经过其余方案来解决网络方面存在的潜在安全威胁。
使用私有局域网络。咱们能够经过使用私有局域网络,经过网络设备,统一私有局域网的出口,并经过网络防火墙设备控制出口的安全。
使用 SSL 加密通道。若是咱们的数据对保密要求很是严格,能够启用 MySQL 提供的 SSL 访问接口,将传输数据进行加密。使网络传输的数据即便被截获,也没法轻易使用。
访问受权限定来访主机信息。在以前的权限系统介绍中咱们已经了解到 MySQL 的权限信息是针对用户和来访主机两者结合定位的。因此咱们能够在受权的时候,经过指定主机的主机名、域名或者 IP 地址信息来限定来访主机的范围。
其次,在第二层防线主机上面也有如下一些须要注意的地方。
OS 安全方面。关闭 MySQL Server 主机上面任何不须要的服务,这不只能从安全方面减小潜在隐患,还能减轻主机的部分负担,尽量提升性能。使用网络扫描工具(如 nmap 等)扫描主机端口,检查除了 MySQL 须要监听的端口 3306(或者自定义更改后的某个端口)以外,还有哪些端口是打开正在监听的,并去掉没必要要的端口。严格控制 OS 账号的管理,以防止账号信息外泄,尤为是 root 和 mysql 账号。对 root 和 mysql 等对 mysql 的相关文件有特殊操做权限的 OS 账号登陆后作出比较显眼的提示,并在 Terminal 的提示信息中输出当前用户信息,以防止操做的时候通过屡次用户切换后出现人为误操做。
用非 root 用户运行 MySQL。这在 MySQL 官方文档中也有很是明显的提示,提醒用户不要使用 root 用户来运行 MySQL。由于若是使用 root 用户运行 MySQL,那么 mysqld 的进程就会拥有 root 用户所拥有的权限,任何具备 FILE 权限的 MySQL 用户就能够在 MySQL 中向系统中的任何位置写入文件。固然,因为 MySQL 不接受操做系统层面的认证,因此任何操做系统层级的账号都不能直接登陆 MySQL,这一点和 Oracle 的权限认证有些区别,因此在这一方面咱们能够减小一些安全方面的顾虑。
文件和进程安全。合理设置文件的权限属性,MySQL 相关的数据和日志文件和所在的文件夹属主和所属组都设置为 mysql,且禁用其余全部用户(除了拥有超级权限的用户,如 root)的读写权限。以防止数据或者日志文件被窃取或破坏。由于若是一个用户对 MySQL 的数据文件有读取权限的话,能够很容易将数据复制。binlog 文件也很容易还原整个数据库。而若是有写权限的话就更糟了,由于有了写权限,数据或者日志文件就有被破坏或者删除的风险存在。保护好 socket 文件的安全,尽可能不要使用默认的位置(如/tmp/mysql.sock),以防止被有意或无心的删除。
确保 MySQL Server 所在的主机上所必要运行的其余应用或者服务足够安全,避免由于其余应用或者服务存在安全漏洞而被入侵者攻破防线。
在 OS 层面还有不少关于安全方面的其余设置和须要注意的地方,但考虑到篇幅问题,这里就不作进一步分析了,有兴趣的读者能够参考各类不一样 OS 在安全方面的专业书籍。
再次,就是最后第三道防线 MySQL 自身方面的安全设置注意事项。
到了最后这道防线上,咱们有更多须要注意的地方。
用户设置。咱们必须确保任何能够访问数据库的用户都有一个比较复杂的内容做为密码,而不是很是简单或者比较有规律的字符,以防止被使用字典破解程序攻破。在 MySQL 初始安装完成以后,系统中可能存在一个不须要任何密码的 root 用户,有些版本安装完成以后还会存在一个能够经过 localhost 登陆的没有用户名和密码的账号。这些账号会给系统带来极大的安全隐患,因此咱们必须在正式启用以前尽早删除,或者设置一个比较安全的密码。对于密码数据的存放,也不要存放在简单的文本文件之中,而应该使用专业密码管理软件来管理(如 KeePass)。同时,就像以前在网络安全注意事项部分讲到的那样,尽量为每个账户限定必定范围的可访问主机。尤为是拥有超级权限的 MySQL root 账号,尽可能确保只能经过 localhost 访问。
安全参数。在 MySQL 官方参考手册中也有说明,不管是从安全方面考虑仍是从性能以及功能稳定性方面考虑,不须要使用的功能模块尽可能都不要启用。例如,若是不须要使用用户自定义函数,就不要在启动的时候使用"--allow-suspicious-udfs"参数选项,以防止被别有居心的潜在威胁者利用此功能而对 MySQL 的安全形成威胁;不须要从本地文件中 Load 数据到数据库中,就使用"--local-infile=0"禁用掉能够从客户端机器上 Load 文件到数据库中;使用新的密码规则和校验规则(不要使用"--old-passwords"启动数据库),这项功能是为了兼容旧版本的密码校验方式的,如无额数必要,不要使用该功能,旧版本的密码加密方式要比新的方式在安全方面弱不少。
除了以上这三道防线,咱们还应该让链接 MySQL 数据库的应用程序足够安全,以防止入侵者经过应用程序中的漏洞而入侵到应用服务器,最终经过应用程序中的数据库相关关配置而获取数据库的登陆口令。
安全无小事,一旦安全出了问题一切都完了。数据的安全是一个企业安全方面最核心最重要的内容,只有保障的数据的安全,企业才有可能真正"安全"。但愿这一章 MySQL 安全方面的内容可以对各位读者在构筑安全的企业级 MySQL 数据库系统中带来一点帮助。