前言 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 數據庫系統中帶來一點幫助。