MySQL性能调优与架构设计——第4章 MySQL安全管理
第4章 MySQL安全管理
前言
?? 對于任何一個企業來說,其數據庫系統中所保存數據的安全性無疑是非常重要的,尤其是公司的有些商業數據,可能數據就是公司的根本,失去了數據的安全性,可能就是失去了公司的一切。本章將針對 MySQL 的安全相關內容進行較為詳細的介紹。
4.1 數據庫系統安全相關因素
一、外圍網絡:
?? MySQL的大部分應用場景都是基于網絡環境的,而網絡本身是一個充滿各種入侵危險的環境,所以要保護他的安全,在條件允許的情況下,就應該從最外圍的網絡環境開始“布防”,因為這一層防線可以從最大范圍內阻止可能存在的威脅。
?? 在網絡環境中,任意兩點之間都可能存在無窮無盡的“道路”可以抵達,是一個真正“條條道路通羅馬”的環境。在那許許多多的道路中,只要有一條道路不夠安全,就可能被入侵者利用。當然,由于所處的環境不同,潛在威脅的來源也會不一樣。有些MySQL所處環境是暴露在整個廣域網中,可以說是完全“裸露”在任何可以接入網絡環境的潛在威脅者面前。而有些MySQL是在一個環境相對小一些的局域網之內,相對來說,潛在威脅者也會少很多。處在局域網之內的MySQL,由于有局域網出入口的網絡設備的基本保護,相對于暴露在廣域網中要安全不少,主要威脅對象基本上控制在了可以接入局域網的內部潛在威脅者,和極少數能夠突破最外圍防線(局域網出入口的安全設備)的入侵者。所以,盡可能的讓我們的MySQL處在一個有保護的局域網之中,是非常必要的。
二、主機:
?? 有了網絡設備的保護,我們的MySQL就足夠安全了么?我想大家都會給出否定的回答。因為即使我們局域網出入口的安全設備足夠的強大,可以攔截住外圍試圖入侵的所有威脅者,但如果威脅來自局域網內部呢?比如局域網中可能存在被控制的設備,某些被控制的有權限接入局域網的設備,以及內部入侵者等都仍然是威脅者。所以說,吉使在第一層防線之內,我們仍然存在安全風險,局域網內部仍然會有不少的潛在威脅存在。
?? 這個時候就需要我們部署第二道防線“主機層防線”了。“主機層防線”主要攔截網絡(包括局域網內)或者直連的未授權用戶試圖入侵主機的行為。因為一個惡意入侵者在登錄到主機之后,可能通過某些軟件程序竊取到那些自身安全設置不夠健壯的數據庫系統的登入口令,從而達到竊取或者破壞數據的目的。如一個主機用戶可以通過一個未刪除且未設置密碼的無用戶名本地帳戶輕易登入數據庫,也可以通過MySQL初始安裝好之后就存在的無密碼的“root@localhost”用戶登錄數據庫并獲得數據庫最高控制權限。
?
? 非法用戶除了通過登入數據庫獲取(或者破壞)數據之外,還可能通過主機上面相關權限設置的漏洞,跳過數據庫而直接獲取MySQL數據(或者日志)文件達到竊取數據的目的,或者直接刪除數據(或者日志)文件達到破壞數據的目的。
三、數據庫:
? 通過第二道防線“主機層防線”的把守,我們又可以擋住很大一部分安全威脅者。但仍然可能有極少數突破防線的入侵者。而且即使沒有任何“漏網之魚”,那些有主機登入權限的使用者呢?是否真的就是完全可信任對象?No,我們不能輕易冒這個潛在風險。對于一個有足夠安全意識的管理員來說,是不會輕易放任任何一個潛在風險存在的。
? 這個時候,我們的第三道防線,“數據庫防線”就需要發揮他的作用了。“數據庫防線”也就是MySQL數據庫系統自身的訪問控制授權管理相關模塊。這道防線基本上可以說是MySQL的最后一道防線了,也是最核心最重要的防線。他首先需要能夠抵擋住在之前的兩層防線都沒有能夠阻攔住的所有入侵威脅,同時還要能夠限制住擁有之前二層防線自由出入但不具備數據庫訪問權限的潛在威脅者,以確保數據庫自身的安全以及所保存數據的安全。
? 之前的二層防線對于所有數據庫系統來說基本上區別不大,都存在著基本相同的各種威脅,不論是Oracle還是MySQL,以及任何其他的數據庫管理系統,都需要基本一致的“布防”策略。但是這第三層防線,也就是各自自身的“數據庫防線”對于每個數據庫系統來說都存在較大的差異,因為每種數據庫都有各自不太一樣的專門負責訪問授權相關功能的模塊。不論是權限劃分還是實現方式都可能不太一樣。
? 對于MySQL來說,其訪問授權相關模塊主要是由兩部分組成。一個是基本的用戶管理模塊,另一個是訪問授權控制模塊。用戶管理模塊的功能相對簡單一些,主要是負責用戶登錄連接相關的基本權限控制,但其在安全控制方面的作用卻不比任何環節小。他就像 MySQL的一個“大門門衛”一樣,通過校驗每一位敲門者所給的進門“暗號”(登入口令),決定是否給敲門者開門。而訪問授權控制模塊則是隨時隨地檢查已經進門的訪問者,校驗他們是否有訪問所發出請求需要訪問的數據的權限。通過校驗者可順利拿到數據,而未通過校驗的訪問者,只能收到“訪問越權了”的相關反饋。
? 上面的三道防線組成了如圖4-1所示的三道堅固的安全保護壁壘,就像三道堅固的城墻一樣保護這 MySQL 數據庫中的數據。只要保障足夠,基本很難有人能夠攻破這三道防線。
四、代碼:
?? 1、SQL語句相關安全因素:
? “SQL注入攻擊”這個術語我想大部分讀者朋友都聽說過了?指的就是攻擊者根據數據庫的SQL語句解析器的原理,利用程序中對客戶端所提交數據的校驗漏洞,從而通過程序動態提交數據接口提交非法數據,達到攻擊者的入侵目的。
? “SQL注入攻擊”的破壞性非常的大,輕者造成數據被竊取,重者數據遭到破壞,甚至可能丟失全部的數據。如果讀者朋友還不是太清楚何為“SQL 注入攻擊”,建議通過互聯網搜索一下,可以得到非常多非常詳細的介紹及案例分析,這里有不做詳細介紹了。
?? 2、程序代碼相關安全因素:
?? 程序代碼如果權限校驗不夠仔細而存在安全漏洞,則同樣可能會被入侵者利用,達到竊取數據等目的。比如,一個存在安全漏洞的信息管理系統,很容易就可能竊取到其他一些系統的登入口令。之后,就能堂而皇之的輕松登錄相關系統達到竊取相關數據的目的。甚至還可能通過應用系統中保存不善的數據庫系統連接登錄口令,從而帶來更大的損失。
4.2 MySQL 權限系統介紹
4.2.1 權限系統簡介
?? 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來實現用戶和權限變更的操作。
4.2.2? 權限授予與去除
?? 要為某個用戶授權,可以使用GRANT命令,要去除某個用戶已有的權限則使用REVOKE命令。當然,出了這兩者之外還有一種比較暴力的辦法,那就是直接更新grant tables系統表。當給某個用戶授權的時候,不僅需要指定用戶名,同時還要指定來訪主機。如果在授權的時候僅指定用戶名,則MySQL會自動認為是對'username'@'%'授權。要去除某個用戶的的權限同樣也需要指定來訪主機。
?? 可能有些時候我們還會需要查看某個用戶目前擁有的權限,這可以通過兩個方式實現,首先是通過執行“SHOW GRANTS FOR 'username'@'hostname'” 命令來獲取之前該用戶身上的所有授權。另一種方法是查詢grant tables里面的權限信息。
4.2.3 權限級別
MySQL中的權限分為五個級別,分別如下:
1、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):
??? 要授予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)
2、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的權限,則可以有兩種實現方式:
??? 1、在執行GRANT命令的時候,通過“database.*”來限定權限作用域為database整個數據庫,如下:
??? 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'??????????????????? |
??? +------------------------------------------------------------------+
??? 2、先通過USE命令選定需要授權的數據庫,然后通過“*”來限定作用域,這樣授權的作用域實際上就是當前選定的整個數據庫。
??? 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)
3、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這八種權限。
4、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上面所做的擴展。
5、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的權限授予,可以在當前不存在該數據庫的時候就完成授權。
4.2.4 MySQL訪問控制實現原理
??? MySQL訪問控制實際上由兩個功能模塊共同組成,從第一篇的第二章架構組成中可以看到,一個是負責“看守MySQL大門”的用戶管理模塊,另一個就是負責監控來訪者每一個動作的訪問控制模塊。用戶管理模塊決定造訪客人能否進門,而訪問控制模塊則決定每個客人進門能拿什么不能拿什么。下面是一張MySQL中實現訪問控制的簡單流程圖(見圖4-2):
1、用戶管理
??? 我們先看看用戶管理模塊是如何工作的。在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;
??? 一個用戶要想訪問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;
??? 但是這里有一個比較特殊的訪問限制,如果要通過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”命令。
2、訪問控制
?? 當客戶端連接通過用戶管理模塊的驗證,可連接上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”。
4.3 MySQL 訪問授權策略
?? 在我們了解了影響數據庫系統安全的相關因素以及MySQL權限系統的工作原理之后,就需要為我們的系統設計一個安全合理的授權策略。我想,每個人心里都清楚,要想授權最簡單最簡單方便,維護工作量最少,那自然是將所有權限都授予所有的用戶來的最簡單方便了。但是,我們大家肯定也都知道,一個用戶所用有的權限越大,那么他給我們的系統所帶來的潛在威脅也就越大。所以,從安全方面來考慮的話,權限自然是授予的越小越好。一個有足夠安全意識的管理員在授權的時候,都會只授予必要的權限,而不會授予任何多余的權限。既然我們這一章是專門討論安全的,那么我們現在也就從安全的角度來考慮如何設計一個更為安全合理的授權策略。
?? 首先,需要了解來訪主機。
?? 由于MySQL數據庫登錄驗證用戶的時候是出了用戶名和密碼之外,還要驗證來源主機。所以我們還需要了解每個用戶可能從哪些主機發起連接。當然,我們也可以通過授權的時候直接通過“%”通配符來給所有主機都有訪問的權限,但是這樣作就違背了我們安全策略的原則,帶來了潛在風險,所以并不可取。尤其是在沒有局域網的防火墻保護的情況下,更是不能輕易允許可以從任何主機登錄的用戶存在。能通過具體主機名或者IP地址指定的盡量通過使用具體的主機名和IP地址來限定來訪主機,不能用具體的主機名或者IP地址限定的也需要用盡可能小的通配范圍來限定。
?? 其次,了解用戶需求。
?? 既然是要做到僅授予必要的權限,那么我們必須了解每個用戶所擔當的角色,也就是說,我們需要充分了解每個用戶需要連接到數據庫上完成什么工作。了解該用戶是一個只讀應用的用戶,還是一個讀寫都有的帳戶;是一個備份作業的用戶還是一個日常管理的帳戶;是只需要訪問特定的某個(或者某幾個)數據庫(Schema),還是需要訪問所有的數據庫。只有了解了需要做什么,才能準確的了解需要授予什么樣的權限。因為如果權限過低,會造成工作無法正常完成,而權限過高,則存在潛在的安全風險。
?? 再次,要為工作分類。
?? 為了做到各司其職,我們需要將需要做的工作分門別類,不同類別的工作使用不同的用戶,做好用戶分離。雖然這樣可能會帶來管理成本方面的部分工作量增加,但是基于安全方面的考慮,這部分管理工作量的增加是非常值得的。而且我們所需要做的用戶分離也只是一個適度的分離。比如將執行備份工作、復制工作、常規應用訪問、只讀應用訪問和日常管理工作分別分理出單獨的特定帳戶來授予各自所需權限。這樣,既可以讓安全風險盡量降低,也可以讓同類同級別的相似權限合并在一起,不互相交織在一起。對于PROCESS,FILE和SUPER這樣的特殊權限,僅僅只有管理類帳號才需要,不應該授予其他非管理帳號。
?? 最后,確保只有絕對必要者擁有GRANT OPTION 權限。
?? 之前在權限系統介紹的時候我們已經了解到GRANT OPTION權限的特殊性,和擁有該權限之后的潛在風險,所以在這里也就不再累述了。總之,為了安全考慮,擁有GRANT OPTION權限的用戶越少越好,盡可能只讓擁有超級權限的用戶才擁有GRANT OPTION權限。
4.4 安全設置注意事項
?? 在前面我們了解了影響數據庫系統安全的幾個因素,也了解了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數據庫的應用程序足夠安全,以防止入侵者通過應用程序中的漏洞而入侵到應用服務器,最終通過應用程序中的數據庫相關關配置而獲取數據庫的登錄口令。
4.5 小結
? 安全無小事,一旦安全出了問題一切都完了。數據的安全是一個企業安全方面最核心最重要的內容,只有保障的數據的安全,企業才有可能真正“安全”。希望這一章 MySQL 安全方面的內容能夠對各位讀者在構筑安全的企業級 MySQL 數據庫系統中帶來一點幫助。
摘自:《MySQL性能調優與架構設計》簡朝陽
轉載請注明出處:
作者:JesseLZJ
出處:http://jesselzj.cnblogs.com
轉載于:https://www.cnblogs.com/jesselzj/p/4713400.html
總結
以上是生活随笔為你收集整理的MySQL性能调优与架构设计——第4章 MySQL安全管理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 国产与第三方库FFmpeg SDK
- 下一篇: (一个)AngularJS获取贴纸Hel