MySQL安全性指南
作為一個MySQL的系統管理員,你有責任維護你的MySQL資料庫系統的資料安全性和完整性。本文主要主要介紹如何建立一個安全的MySQL系統,從系統內部和外部網路兩個角度,為你提供一個指南。
本文主要考慮下列安全性有關的問題:
為什麼安全性很重要,你應該防範那些攻擊?
伺服器面臨的風險(內部安全性),如何處理?
連線伺服器的客戶端風險(外部安全性),如何處理?
MySQL管理員有責任保證資料庫內容的安全性,使得這些資料記錄只能被那些正確授權的使用者訪問,這涉及到資料庫系統的內部安全性和外部安全性。
內部安全性關心的是檔案系統級的問題,即,防止MySQL資料目錄(DATADIR)被在伺服器主機有賬號的人(合法或竊取的)進行攻擊。如果資料目錄內容的許可權過分授予,使得每個人均能簡單地替代對應於那些資料庫表的檔案,那麼確保控制客戶透過網路訪問的授權表設定正確,對此毫無意義。
外部安全性關心的是從外部透過網路連線伺服器的客戶的問題,即,保護MySQL伺服器免受來自透過網路對伺服器的連線的攻擊。你必須設定MySQL授權表(grant table),使得他們不允許訪問伺服器管理的資料庫內容,除非提供有效的使用者名稱和口令。
下面就詳細介紹如何設定檔案系統和授權表mysql,實現MySQL的兩級安全性。
一、內部安全性-保證資料目錄訪問的安全
MySQL伺服器透過在MySQL資料庫中的授權表提供了一個靈活的許可權系統。你可以設定這些表的內容,允許或拒絕客戶對資料庫的訪問,這提供了你防止未授權的網路訪問對你資料庫攻擊的安全手段,然而如果主機上其他使用者能直接訪問資料目錄內容,建立對透過網路訪問資料庫的良好安全性對你毫無幫助,除非你知道你是登入MySQL伺服器執行主機的唯一使用者,否則你需要關心在這臺機器上的其他使用者獲得對資料目錄的訪問的可能性。
以下是你應該保護的內容:
資料庫檔案。很明顯,你要維護伺服器管理的資料庫的私用性。資料庫擁有者通常並且應該考慮資料庫內容的安全性,即使他們不想,也應該考慮時資料庫內容公開化,而不是透過糟糕的資料目錄的安全性來暴露這些內容。
日誌檔案。一般和更新日誌必須保證安全,因為他們包含查詢文字。對日誌檔案有訪問許可權的任何人可以監視資料庫進行過的操作。
更要重點考慮的日誌檔案安全性是諸如GRANT和SET PASSWORD等的查詢也被記載了,一般和更新日誌包含有敏感查詢的文字,包括口令(MySQL使用口令加密,但它在已經完成設定後才運用於以後的連線建立。設定一個口令的過程設計象GRANT或SET PASSWORD等查詢,並且這些查詢以普通文字形式記載在日誌檔案中)。如果一個攻擊者猶如日檔案的讀許可權,只需在日誌檔案上執行grep尋找諸如GRANT和PASSWORD等詞來發現敏感資訊。
顯然,你不想讓伺服器主機上的其他使用者有資料庫目錄檔案的寫許可權,因為他們可以重寫你的狀態檔案或資料庫表檔案,但是讀許可權也很危險。如果一個資料庫表檔案能被讀取,偷取檔案並得到MySQL本身,以普通文字顯示錶的內容也很麻煩,為什麼?因為你要做下列事情:
在伺服器主機上安裝你自己“特製”的MySQL伺服器,但是有一個不同於官方伺服器版本的埠、套接字和資料目錄。
執行mysql_install_db初始化你的資料目錄,這賦予你作為MySQL root使用者訪問你的伺服器的許可權,所以你有對伺服器訪問機制的完全控制,它也建立一個test資料庫。
將對應於你想偷取得表檔案複製到你伺服器的資料庫目錄下的test目錄。
啟動你的伺服器。你可以隨意訪問資料庫表,SHOW TABLES FROM test顯示你有一個偷來的表的複製,SELECT *顯示它們任何一個的全部內容。
如果你確實很惡毒,將許可權公開給你伺服器的任何匿名使用者,這樣任何人能從任何地方連線伺服器訪問你的test資料庫。你現在將偷來的資料庫表公佈於眾了。
在考慮一下,從相反的角度,你想讓別人對你這樣嗎?當然不!你可以透過在資料庫錄下執行ls -l命令確定你的資料庫是否包含不安全的檔案和目錄。查詢有“組”和“其他使用者”許可權設定的檔案和目錄。下面是一個不安全資料目錄的一部分列出:
% ls -l
total 10148
drwxrwxr-x 11 mysqladm wheel 1024 May 8 12:20 .
drwxr-xr-x 22 root wheel 512 May 8 13:31 ..
drwx------ 2 mysqladm mysqlgrp 512 Apr 16 15:57 menagerie
drwxrwxr-x 2 mysqladm wheel 512 Jan 25 20:40 mysql
drwxrwxr-x 7 mysqladm wheel 512 Aug 31 1998 sql-bench
drwxrwxr-x 2 mysqladm wheel 1536 May 6 06:11 test
drwx------ 2 mysqladm mysqlgrp 1024 May 8 18:43 tmp
....
正如你看到的,有些資料庫有正確的許可權,而其他不是。本例的情形是經過一段時間後的結果。較少限制的許可權由在許可權設定方面比更新版本更不嚴格的較早版本伺服器設定的(注意更具限制的目錄menageria和tmp都有較近日期)。MySQL當前版本確保這些檔案只能由執行伺服器的使用者讀取。
讓我們來修正這些許可權,使得只用伺服器使用者可訪問它們。你的主要保護工具來自於由UNIX檔案系統本身提供的設定檔案和目錄屬主和模式的工具。下面是我們要做的:
進入該目錄
% cd DATADIR
設定所有在資料目錄下的檔案屬主為由用於執行伺服器的賬號擁有(你必須以root執行這步)。在本文使用mysqladm和mysqlgrp作為該賬號的使用者名稱和組名。你可以使用下列命令之一改變屬主:
# chown mysqladm.mysqlgrp .
# find . -follow -type d -print | xargs chown mysqladm.mysqlgrp
設定你的資料目錄和資料庫目錄的模式使得他們只能由mysqladm讀取,這阻止其他使用者訪問你資料庫目錄的內容。你可以用下列命令之一以root或mysqladm身份執行。
% chmod -R go-rwx .
% find . -follow -type d -print | xargs chmod go-rwx
資料目錄內容的屬主和模式為mysqladm設定。現在你應該保證你總是以mysqladm使用者執行伺服器,因為現在這是唯一由訪問資料庫目錄許可權的使用者(除root)。
在完成這些設定後,你最終應該得到下面的資料目錄許可權:
% ls -l
total 10148
drwxrwx--- 11 mysqladm mysqlgrp 1024 May 8 12:20 .
drwxr-xr-x 22 root wheel 512 May 8 13:31 ..
drwx------ 2 mysqladm mysqlgrp 512 Apr 16 15:57 menagerie
drwx------ 2 mysqladm mysqlgrp 512 Jan 25 20:40 mysql
drwx------ 7 mysqladm mysqlgrp 512 Aug 31 1998 sql-bench
drwx------ 2 mysqladm mysqlgrp 1536 May 6 06:11 test
drwx------ 2 mysqladm mysqlgrp 1024 May 8 18:43 tmp
....
二、外部安全性-保證網路訪問的安全
MySQL的安全系統是很靈活的,它允許你以多種不垃圾廣告式設定使用者許可權。一般地,你可使用標準的SQL語句GRANT和REVOKE語句做,他們為你修改控制客戶訪問的授權表,然而,你可能由一個不支援這些語句的老版本的MySQL(在3.22.11之前這些語句不起作用),或者你發覺使用者許可權看起來不是以你想要的方式工作。對於這種情況,瞭解MySQL授權表的結構和伺服器如何利用它們決定訪問許可權是有幫助的,這樣的瞭解允許你透過直接修改授權表增加、刪除或修改使用者許可權,它也允許你在檢查這些表時診斷許可權問題。
關於如何管理使用者賬號,見《MySQL的使用者管理》。而對GRANT和REVOKE語句詳細描述,見《MySQL參考手冊》。
2.1 MySQL授權表的結構和內容
透過網路連線伺服器的客戶對MySQL資料庫的訪問由授權表內容來控制。這些表位於mysql資料庫中,並在第一次安裝MySQL的過程中初始化(執行mysql_install_db指令碼)。授權表共有5個表:user、db、host、tables_priv和columns_priv。
表1 user、db和host授權表結構
訪問範圍列
user db host
Host Host Host
User Db Db
Password User
資料庫/表許可權列
Alter_priv Alter_priv Alter_priv
Create_priv Create_priv Create_priv
Delete_priv Delete_priv Delete_priv
Drop_priv Drop_priv Drop_priv
Index_priv Index_priv Index_priv
Insert_priv Insert_priv Insert_priv
References_priv References_priv References_priv
Select_priv Select_priv Select_priv
Update_priv Update_priv Update_priv
File_priv Grant_priv Grant_priv
Grant_priv
Process_priv
Reload_priv
Shutdown_priv
表2 tables_priv和columns_priv屬權表結構
訪問範圍列
tables_priv columns_priv
Host Host
Db Db
User User
Table_name Table_name
Column_name
許可權列
Table_priv Column_priv
授權表的內容有如下用途:
user表
user表列出可以連線伺服器的使用者及其口令,並且它指定他們有哪種全域性(超級使用者)許可權。在user表啟用的任何許可權均是全域性許可權,並適用於所有資料庫。例如,如果你啟用了DELETE許可權,在這裡列出的使用者可以從任何表中刪除記錄,所以在你這樣做之前要認真考慮。
db表
db表列出資料庫,而使用者有許可權訪問它們。在這裡指定的許可權適用於一個資料庫中的所有表。
host表
host表與db表結合使用在一個較好層次上控制特定主機對資料庫的訪問許可權,這可能比單獨使用db好些。這個表不受GRANT和REVOKE語句的影響,所以,你可能發覺你根本不是用它。
tables_priv表
tables_priv表指定表級許可權,在這裡指定的一個許可權適用於一個表的所有列。
columns_priv表
columns_priv表指定列級許可權。這裡指定的許可權適用於一個表的特定列。
在“不用GRANT設定使用者”一節裡,我們再討論GRANT語句如何對修改這些表起作用,和你怎樣能透過直接修改授權表達到同樣的效果。
tables_priv和columns_priv表在MySQL 3.22.11版引進(與GRANT語句同時)。如果你有較早版本的MySQL,你的mysql資料庫將只有user、db和host表。如果你從老版本升級到3.22.11或更新,而沒有tables_priv和columns_priv表,執行mysql_fix_privileges_tables指令碼建立它們。
MySQL沒有rows_priv表,因為它不提供記錄級許可權,例如,你不能限制使用者於表中包含特定列值的行。如果你確實需要這種能力,你必須用應用程式設計來提供。如果你想執行建議的記錄級鎖定,你可用GET_LOCK()函式做到。
授權表包含兩種列:決定一個許可權何時運用的範圍列和決定授予哪種許可權的許可權列。
2.1.1 授權表範圍列
授權表範圍列指定表中的許可權何時運用。每個授權表條目包含User和Host列來指定許可權何時運用於一個給定使用者從給定主機的連線。其他表包含附加的範圍列,如db表包含一個Db列指出許可權運用於哪個資料庫。類似地,tables_priv和columns_priv表包含範圍欄位,縮小範圍到一個資料庫中的特定表或一個表的特定列。
2.1.2 授權表許可權列
授權表還包含許可權列,他們指出在範圍列中指定的使用者擁有何種許可權。由MySQL支援的許可權如下表所示。該表使用GRANT語句的許可權名稱。對於絕大多數在user、db和host表中的許可權列的名稱與GRANT語句中有明顯的聯絡。如Select_priv對應於SELECT許可權。
|-page-|
2.1.3 資料庫和表許可權
下列許可權運用於資料庫和表上的操作。
ALTER
允許你使用ALTER TABLE語句,這其實是一個簡單的第一級許可權,你必須由其他許可權,這看你想對資料庫實施什麼操作。
CREATE
允許你建立資料庫和表,但不允許建立索引。
DELETE
允許你從表中刪除現有記錄。
DROP
允許你刪除(拋棄)資料庫和表,但不允許刪除索引。
INDEX
允許你建立並刪除索引。
REFERENCES
目前不用。
SELECT
允許你使用SELECT語句從表中檢索資料。對不涉及表的SELECT語句就不必要,如SELECT NOW()或SELECT 4/2。
UPDATE
允許你修改表中的已有的記錄。
2.1.4 管理許可權
下列許可權運用於控制伺服器或使用者授權能力的操作的管理性操作。
FILE
允許你告訴伺服器讀或寫伺服器主機上的檔案。該許可權不應該隨便授予,它很危險,見“迴避授權表風險”。伺服器確實較謹慎地保持在一定範圍內使用該許可權。你只能讀任何人都能讀的檔案。你正在寫的檔案必須不是現存的檔案,這防止你迫使伺服器重寫重要檔案,如/etc/passwd或屬於別人的資料庫的資料目錄。
如果你授權FILE許可權,確保你不以UNIX的root使用者執行伺服器,因為root可在檔案系統的任何地方建立新檔案。如果你以一個非特權使用者執行伺服器,伺服器只能在給使用者能訪問的目錄中建立檔案。
GRANT
允許你將你自己的許可權授予別人,包括GRANT。
PROCESS
允許你透過使用SHOW PROCESS語句或mysqladmin process命令檢視伺服器內正在執行的執行緒(程式)的資訊。這個許可權也允許你用KILL語句或mysqladmin kill命令殺死執行緒。
你總是能看到或殺死你自己的執行緒。PROCESS許可權賦予你對任何執行緒做這些事情的能力。
RELOAD
允許你執行大量的伺服器管理操作。你可以發出FLUSH語句,你也能指性mysqladmin的reload、refresh、flush-hosts、flush-logs、flush-privileges和flush-tables等命令。
SHUTDOWN
允許你用mysqladmin shutdown關閉伺服器。
在user、db和host表中,每一個許可權以一個單獨的列指定。這些列全部宣告為一個ENUM("N","Y")型別,所以每個權的預設值是“N”。在tables_priv和columns_priv中的許可權以一個SET表示,它允許許可權用一個單個列以任何組合指定。這兩個表比其他三個表更新,這就是為什麼它們使用更有效的表示方式的原因。(有可能在未來,user、db和host表也用一個SET型別表示。)
在tables_priv表中的Table_priv列被定義成:
SET(´Select´,´Insert´,´Update´,´Delete´,´Create´,´Drop´,´Grant´,´References´,´Index´,´Alter´)
在coloums_priv表中的Column_priv列被定義成:
SET(´Select´,´Insert´,´Update´,´References´)
列許可權比表許可權少,因為列級較少的許可權有意義。例如你能建立一個表,但你不能建立一個孤立的列。
user表包含某些在其他授權表不存在的許可權的列:File_priv、Process_priv、Reload_priv和Shutdown_priv。這些許可權運用於你讓伺服器執行的與任何特定資料庫或表不相關的操作。如允許一個使用者根據當前資料庫是什麼來關閉資料庫是毫無意義的。
2.2 伺服器如何控制客戶訪問
在你使用MySQL時,客戶訪問控制有兩個階段。第一階段發生在你試圖連線伺服器時。伺服器查詢user表看它是否能找到一個條目匹配你的名字、你正在從那兒連線的主機和你提供的口令。如果沒有匹配,你就不能連線。如果有一個匹配,建立連線並繼續第二階段。在這個階段,對於每一個你發出的查詢,伺服器檢查授權表看你是否有足夠的許可權執行查詢,第二階段持續到你與伺服器對話的結束。
本小節詳細介紹MySQL伺服器用於將授權表條目匹配到來的連線請求或查詢的原則,這包括在授權表範圍列中合法的值的型別、結合授權表中的許可權資訊的方式和表中條目被檢查的次序。
2.2.1 範圍列內容
一些範圍列要求文字值,但它們大多數允許萬用字元或其他特殊值。
Host
一個Host列值可以是一個主機名或一個IP地址。值localhost意味著本地主機,但它只在你用一個localhost主機名時才匹配,而不是你在使用主機名時。假如你的本地主機名是pit.snake.net並且在user表中有對你的兩條記錄,一個有一個Host值或localhost,而另一個有pit.snake.net,有localhost的記錄將只當你連線localhost時匹配,其他在只在連線pit.snake.net時才匹配。如果你想讓客戶能以兩種方式連線,你需要在user表中有兩條記錄。
你也可以用萬用字元指定Host值。可以使用SQL的模式字元“%”和“_”並具有當你在一個查詢中使用LIKE算符同樣的含義(不允許regex算符)。 SQL模式字元都能用於主機名和IP地址。如%wisc.edu匹配任何wisc.edu域內的主機,而%.edu匹配任何教育學院的主機。類似地,192.168.%匹配任何在192.168 B類子網的主機,而192.168.3.%匹配任何在192.168.3 C類子網的主機。
%值匹配所有主機,並可用於允許一個使用者從任何地方連線。一個空白的Host值等同於%。(例外:在db表中,一個空白Host值含義是“進一步檢查host表”,該過程在“查詢訪問驗證”中介紹。)
從MySQL 3.23起,你也可以指定帶一個表明那些為用於網路地址的網路掩碼的IP地址,如192.168.128.0/17指定一個17位網路地址並匹配其IP地址是192.168.128前17位的任何主機。
User
使用者名稱必須是文字的或空白。一個空白值匹配任何使用者。%作為一個User值不意味著空白,相反它匹配一個字面上的%名字,這可能不是你想要的。
當一個到來的連線透過user表被驗證而匹配的記錄包含一個空白的User值,客戶被認為是一個匿名使用者。
Password
口令值可以是空或非空,不允許用萬用字元。一個空口令不意味著匹配任何口令,它意味著使用者必須不指定口令。
口令以一個加密過的值儲存,不是一個字面上的文字。如果你在Password列中儲存一個照字面上的口令,使用者將不能連線!GRANT語句和mysqladmin password命令為你自動加密口令,但是如果你用諸如INSERT、REPLACE、UPDATE或SET PASSWORD等命令,一定要用PASSWORD("new_password")而不是簡單的"new_password"來指定口令。
Db
在columns_priv和tables_priv表中,Db值必須是真正的資料庫名(照字面上),不允許模式和空白。在db和host中,Db值可以以字面意義指定或使用SQL模式字元´%´或´_´指定一個萬用字元。一個´%´或空白匹配任何資料庫。
Table_name,Column_name
這些列中的值必須是照字面意思的表或列名,不允許模式和空白。
某些範圍列被伺服器視為大小寫敏感的,其餘不是。這些原則總結在下表中。特別注意Table_name值總是被看作大小寫敏感的,即使在查詢中的表名的大小寫敏感性對待視伺服器執行的主機的檔案系統而定(UNIX下是大小寫敏感,而Windows不是)。
表3 授權表範圍列的大小寫敏感性
列
Host
User
Password
Db
Table_name
Column_name
大小寫敏感性
No
Yes
Yes
Yes
Yes
No
2.2.2 查詢訪問驗證
每次你發出一個查詢,伺服器檢查你是否有足夠的許可權執行它,它以user、db、tables_priv和columns_priv的順序檢查,知道它確定你有適當的訪問許可權或已搜尋所有表而一無所獲。更具體的說:
伺服器檢查user表匹配你開始連線的記錄以檢視你有什麼全域性許可權。如果你有並且它們對查詢足夠了,伺服器則執行它。
如果你的全域性許可權不夠,伺服器為你在db表中尋找並將該記錄中的許可權加到你的全域性許可權中。如果結果對查詢足夠,伺服器執行它。
如果你的全域性和資料庫級組合的許可權不夠,伺服器繼續查詢,首先在tables_priv表,然後columns_priv表。
如果你在檢查了所有表之後仍無許可權,伺服器拒絕你執行查詢的企圖。
用布林運算的術語,授權表中的許可權被伺服器這樣使用:
user OR tables_priv OR columns_priv
你可能疑惑為什麼前面的描述只引用4個授權表,而實際上有5個。實際上伺服器是這樣檢查訪問許可權:
user OR (db AND host) OR tables_priv OR columns_priv
第一個較簡單的表示式是因為host表不受GRANT和REVOKE語句影響。如果你總是用GRANT和REVOKE管理使用者許可權,你絕不需要考慮host表。但是其工作原理你用該知道:
當伺服器檢查資料庫級許可權時,它對於客戶查詢db表。如果Host列是空的,它意味著“檢查host表以找出哪一個主機能訪問資料庫”。
伺服器在host表中查詢有與來自db表的記錄相同的Db列值。如果沒有host記錄匹配客戶主機,則沒有授予資料庫級許可權。如果這些記錄的任何一個的確有一個匹配連線的客戶主機的Host列值,db表記錄和host表記錄結合產生客戶的資料庫級許可權。
然而,許可權用一個邏輯AND(與)結合起來,這意味著除非一個給定的許可權在兩個表中都有,否則客戶就不具備該許可權。以這種方式,你可以在db表中授予一個基本的許可權集,然後使用host表對特定的主機有選擇地禁用它們。如你可以允許從你的域中的所有主機訪問資料庫,但關閉了那些在較不安全區域的主機的資料庫許可權。
前面的描述毫無疑問使訪問檢查聽起來一個相當複雜的過程,特別是你以為伺服器對你發出的每個查詢進行許可權檢查,然而此過程是很快的,因為伺服器其實不從授權表對每個查詢查詢資訊,相反,它在啟動時將表的內容讀入記憶體,然後驗證查詢用的是記憶體中的副本。這大大提高了訪問檢查操作的效能。但有一個非常明顯的副作用。如果你直接修改授權表的內容,伺服器將不知道許可權的改變。
例如,如果你用一條INSERT語句向user表加入一個新記錄來增加一個新使用者,命名在記錄中的使用者將不能連線伺服器。這對管理員新手(有時對有經驗的老手)是很困惑的事情,當時解決方法很簡單:在你改變了它們之後告訴伺服器過載授權表內容,你可以發一條FLUSH PRIVILEGES或執行mysqladmin flush-privileges(或如果你有一個不支援flush-privileges的老版本,用mysqladmin reload。)。
2.2.3 範圍列匹配順序
MySQL伺服器按一種特定方式排序符授權表中的記錄,然後透過按序瀏覽記錄匹配到來的連線。找到的第一個匹配決定了被使用的記錄。理解MySQL使用的排序順序很重要,特別是對user表。
當伺服器讀取user表內容時,它根據在Host和User列中的值排序記錄,Host值起決定作用(相同的Host值排在一起,然後再根據User值排序)。然而,排序不是典序(按詞排序),它只是部分是。要牢記的是字面上的詞優先於模式。這意味著如果你正從client.your.net連線伺服器而Host有client.your.net和%.your.net兩個值,則第一個先選。類似地,%.your.net優先於%.net,然後是%。IP地址的匹配也是這樣的。
總之一句話,越具體越優先。可以參見本文附錄的例項。
2.3 避免授權表風險
本屆介紹一些在你授權時的一些預防措施,以及不明值的選擇帶來的風險。一般地,你要很“吝嗇”地授予超級使用者許可權,即不要啟用user表中條目中的許可權,而使用其它授權表,以將使用者許可權限制於資料庫、表、或列。在user表中的許可權允許於影響到你的伺服器操作或能訪問任何資料庫中的任何表。
不要授予對mysql資料庫的許可權。一個擁有包含授權表資料庫許可權的使用者可能會修改表以獲取對其他任何資料庫的許可權。授予允許一個使用者修改mysql資料庫表的許可權也實際上給了使用者以一個全域性GRANT許可權。如果使用者能直接修改表,這也等價於能夠發出任何你能想象的任何GRANT語句。
FILE許可權尤其危險,不要輕易授權它。以下是一個擁有FILE許可權的人能幹除的事情:
CREATE TABLE etc_passwd (pwd_entry TEXT);
LOAD DATA INFILE "/etc/passwd" into TABLE etc_passwd;
SELECT * FROM etc_passwd;
在發出這些語句後,使用者已經擁有了你的口令檔案的內容了。實際上,伺服器上任何公開可讀檔案的內容都可被擁有FILE許可權的使用者透過網路訪問。
FILE許可權也能被利用來危害沒有設定足夠許可權制的檔案許可權的系統上的資料庫。這就是你為什麼應該設定資料目錄只能由伺服器讀取的原因。如果對應於資料庫表的檔案可被任何人讀取,不只是使用者伺服器賬號的使用者可讀,任何有FILE許可權的使用者也可透過網路連線並讀取它們。下面演示這個過程:
建立一個有一個LONGBLOB列的表:
USER test;
CREATE TABLE tmp (b LONGBLOB);
使用該表讀取每個對應於你想偷取的資料庫表檔案的內容,然後將表內容寫入你自己資料庫的一個檔案中:
LOAD DATA INFILE "./other_db/x.frm" INTO TABLE tmp
FIELDS ESCAPED BY "" LINES TERMINATED BY "";
SELECT * FROM tmp INTO OUTFILE "y.frm"
FIELDS ESCAPED BY "" LINES TERMINATED BY "";
DELETE FROM tmp;
LOAD DATA INFILE "./other_db/x.ISD" INTO TABLE tmp
FIELDS ESCAPED BY "" LINES TERMINATED BY "";
SELECT * FROM tmp INTO OUTFILE "y.ISD"
FIELDS ESCAPED BY "" LINES TERMINATED BY "";
DELETE FROM tmp;
LOAD DATA INFILE "./other_db/x.ISM" INTO TABLE tmp
FIELDS ESCAPED BY "" LINES TERMINATED BY "";
SELECT * FROM tmp INTO OUTFILE "y.ISM"
現在你擁有了一個新表y,它包含other_db.x的內容並且你有全權訪問它。
為避免讓人以同樣的方式攻擊,根據“第一部分 內部安全性-保護你的資料目錄”中的指令設定你的資料目錄上的許可權。你也可以在你啟動伺服器時使用--skip-show-database選項限制使用者對於他們沒用訪問許可權的資料庫使用SHOW DATABASES和SHOW TABLES。這有助於防止使用者找到關於它們不能訪問的資料庫和表的資訊。
ALTER許可權能以不希望的方式使用。假定你想讓user1可以訪問table1但不能訪問tables2。一個擁有ALTER許可權的使用者可以透過使用ALTER TABLE將table2改名為table1來偷樑換柱。
當心GRANT許可權。兩個由不同許可權但都有GRANT許可權的使用者可以使彼此的權利更強大。
|-page-|
2.4 不用GRANT設定使用者
如果你有一個早於3.22.11的MySQL版本,你不能使用GRANT(或REVOKE)語句設定使用者及其訪問許可權,但你可以直接修改授權表的內容。如果你理解GRANT語句如何修改授權表,這很容易。那麼你透過手工發出INSERT語句就能自己做同樣的事情。
當你發出一條GRANT語句時,你指定一個使用者名稱和主機名,可能還有口令。對該使用者生成一個user表記錄,並且這些值記錄在User、Host和Password列中。如果你在GRANT語句中指定全域性許可權,這些許可權記錄在記錄的許可權列中。其中要留神的是GRANT語句為你加密口令,而INSERT不是,你需要在INSERT中使用PASSWORD()函式加密口令。
如果你指定資料庫級許可權,使用者名稱和主機名被記錄在db表的User和Host列。你為其授權的資料庫記錄在Db列中,你授予的許可權記錄在許可權列中。
對於表級和列級許可權,效果是類似的。在tables_priv和columns_priv表中建立記錄以記錄使用者名稱、主機名和資料庫,還有相關的表和列。授予的許可權記錄在許可權列中。
如果你還記得前面的介紹,你應該能即使不用GRANT語句也能做GRANT做的事情。記住在你直接修改授權表時,你將通知伺服器過載授權表,否則他不知道你的改變。你可以執行一個mysqladmin flush-privileges或mysqladmin reload命令強迫一個過載。如果你忘記做這個,你會疑惑為什麼伺服器不做你想做的事情。
下列GRANT語句建立一個擁有所有權的超級使用者。包括授權給別人的能力:
GRANT ALL ON *.* TO anyname@localhost IDENTIFIED BY "passwd"
WITH GRANT OPTION
該語句將在user表中為anyname@localhost建立一個記錄,開啟所有許可權,因為這裡是超級使用者(全域性)許可權儲存的地方,要用INSERT語句做同樣的事情,語句是:
INSERT INTO user VALUES("localhost","anyname",PASSWORD("passwd"),
"Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y")
你可能發現它不工作,這要看你的MySQL版本。授權表的結構已經改變而且你在你的user表可能沒有14個許可權列。用SHOW COLUMNS找出你的授權表包含的每個許可權列,相應地調整你的INSERT語句。 下列GRANT語句也建立一個擁有超級使用者身份的使用者,但是隻有一個單個的許可權:
GRANT RELOAD ON *.* TO flush@localhost IDENTIFIED BY "flushpass"
本例的INSERT語句比前一個簡單,它很容易列出列名並只指定一個許可權列。所有其它列將設定為預設的"N":
INSERT INTO user (Host,Password,Reload) VALUES("localhost","flush",PASSWORD("flushpass"),"Y")
資料庫級許可權用一個ON db_name.*子句而不是ON *.*進行授權:
GRANT ALL ON sample.* TO boris@localhost IDENTIFIED BY "ruby"
這些許可權不是全域性的,所以它們不儲存在user表中,我們仍然需要在user表中建立一條記錄(使得使用者能連線),但我們也需要建立一個db表記錄記錄資料庫集許可權:
INSERT INTO user (Host,User,Password) VALUES("localhost","boris",PASSWORD("ruby"))
INSERT INTO db VALUES("localhost","sample_db","boris","Y","Y","Y","Y","Y","Y","N","Y","Y","Y")
"N"列是為GRANT許可權;對末尾的一個資料庫級具有WITH GRANT OPTION的GRANT語句,你要設定該列為"Y"。
要設定表級或列級許可權,你對tables_priv或columns_priv使用INSERT語句。當然,如果你沒有GRANT語句,你將沒有這些表,因為它們在MySQL中同時出現。如果你確實有這些表並且為了某些原因想要手工操作它們,要知道你不能用單獨的列啟用許可權。
你設定tables_priv.Table_priv或columns_priv.Column_priv列來設定包含你想啟用的許可權值。例如,要對一個表啟用SELECT和INSERT許可權,你要在相關的tables_priv的記錄中設定Table_priv為"Select,Insert"。
如果你想對一個擁有MySQL賬號的使用者修改許可權,使用UPDATE而不是INSERT,不管你增加或撤銷許可權都是這樣。要完全刪除一個使用者,從使用者使用的每個表中刪除記錄。
如果你願意避免發一個查詢來直接修改全權表,你可以看一下MySQL自帶的mysqlaccess和mysql_setpermissions指令碼。
附錄1 小測驗
在你剛剛新安裝了一個MySQL伺服器,在你增加了一個允許連線MySQL的使用者,用下列語句:
GRANT ALL ON samp_db.* TO fred@*.snake.net IDENTIFIED "cocoa"
而fred碰巧在伺服器主機上有個賬號,所以他試圖連線伺服器:
%mysql -u fred -pcocoa samp_db
ERROR 1045: Access denied for user: ´fred@localhost´ (Using password: YES)
為什麼?
原因是:
先考慮一下mysql_install_db如何建立初始許可權表和伺服器如何使用user表記錄匹配客戶連線。在你用mysql_install_db初始化你的資料庫時,它建立類似這樣的user表:
Host User
localhost
pit.snake.net
localhost
pit.snake.net root
root
頭兩個記錄允許root指定localhost或主機名連線本地伺服器,後兩個允許匿名使用者從本地連線。當增加fred使用者後,
Host User
localhost
pit.snake.net
localhost
pit.snake.net
%.snake.net root
root
fred
在伺服器啟動時,它讀取記錄並排序它們(首先按主機,然後按主機上的使用者),越具體越排在前面:
Host User
localhost
localhost
pit.snake.net
pit.snake.net
%.snake.net root
root
fred
有localhost的兩個記錄排在一起,而對root的記錄排在第一,因為它比空值更具體。pit.snake.net的記錄也類似。所有這些均是沒有任何萬用字元的字面上的Host值,所以它們排在對fred記錄的前面,特別是匿名使用者排在fred之前。
結果是在fred試圖從localhost連線時,Host列中的一個空使用者名稱的記錄在包含%.snake.net的記錄前匹配。該記錄的口令是空的,因為預設的匿名使用者沒有口令。因為在fred連線時指定了一個口令,由一個錯配且連線失敗。
這裡要記住的是,雖然用萬用字元指定使用者可以從其連線的主機是很方便。但你從本地主機連線時會有問題,只要你在table表中保留匿名使用者記錄。
一般地,建議你刪除匿名使用者記錄:
mysql> DELETE FROM user WHERE User="";
更進一步,同時刪除其他授權表中的任何匿名使用者,有User列的表有db、tables_priv和columns_priv。
附錄2 使一個新的MySQL安裝更安全
在你自己安裝了一個新的MySQL伺服器後,你需要為MySQL的root使用者指定一個目錄(預設無口令),否則如果你忘記這點,你將你的MySQL處於極不安全的狀態(至少在一段時間內)。
在Unix(Linux)上,在按照手冊的指令安裝好MySQL後,你必須執行mysql_install_db指令碼建立包含授權表的mysql資料庫和初始許可權。在Windows上,執行分發中的Setup程式初始化資料目錄和mysql資料庫。假定伺服器也在執行。
當你第一次在機器上安裝MySQL時,mysql資料庫中的授權表是這樣初始化的:
你可以從本地主機(localhost)上以root連線而不指定口令。root使用者擁有所有許可權(包括管理許可權)並可做任何事情。(順便說明,MySQL超級使用者與Unix超級使用者有相同的名字,他們彼此毫無關係。)
匿名訪問被授予使用者可從本地連線名為test和任何名字以test_開始的資料庫。匿名使用者可對資料庫做任何事情,但無管理許可權。
從本地主機多伺服器的連線是允許的,不管連線的使用者使用一個localhost主機名或真實主機名。如:
% mysql -h localhost test
% mysql -h pit.snake.net test
你以root連線MySQL甚至不指定口令的事實只是意味著初始安裝不安全,所以作為管理員的你首先要做的應該是設定root口令,然後根據你設定口令使用的方法,你也可以告訴伺服器過載授權表是它知道這個改變。(在伺服器啟動時,它過載表到記憶體中而可能不知道你已經修改了它們。)
對MySQL 3.22和以上版本,你可以用mysqladmin設定口令:
% mysqladmin -u root password yourpassword
對於MySQL的任何版本,你可以用mysql程式並直接修改mysql資料庫中的user授權表:
% mysql -u root mysql
mysql>UPDATE user SET password=PASSWORD("yourpassword") WHERE User="root";
如果你有MySQL的老版本,使用mysql和UPDATE。
在你設定完口令後,透過執行下列命令檢查你是否需要告訴伺服器過載授權表:
% mysqladmin -u root status
如果伺服器仍然讓你以root而不指定口令而連線伺服器,過載授權表:
% mysqladmin -u root reload
在你設定了root的口令後(並且如果需要過載了授權表),你將需要在任何時候以root連線伺服器時指定口令。
本文主要考慮下列安全性有關的問題:
為什麼安全性很重要,你應該防範那些攻擊?
伺服器面臨的風險(內部安全性),如何處理?
連線伺服器的客戶端風險(外部安全性),如何處理?
MySQL管理員有責任保證資料庫內容的安全性,使得這些資料記錄只能被那些正確授權的使用者訪問,這涉及到資料庫系統的內部安全性和外部安全性。
內部安全性關心的是檔案系統級的問題,即,防止MySQL資料目錄(DATADIR)被在伺服器主機有賬號的人(合法或竊取的)進行攻擊。如果資料目錄內容的許可權過分授予,使得每個人均能簡單地替代對應於那些資料庫表的檔案,那麼確保控制客戶透過網路訪問的授權表設定正確,對此毫無意義。
外部安全性關心的是從外部透過網路連線伺服器的客戶的問題,即,保護MySQL伺服器免受來自透過網路對伺服器的連線的攻擊。你必須設定MySQL授權表(grant table),使得他們不允許訪問伺服器管理的資料庫內容,除非提供有效的使用者名稱和口令。
下面就詳細介紹如何設定檔案系統和授權表mysql,實現MySQL的兩級安全性。
一、內部安全性-保證資料目錄訪問的安全
MySQL伺服器透過在MySQL資料庫中的授權表提供了一個靈活的許可權系統。你可以設定這些表的內容,允許或拒絕客戶對資料庫的訪問,這提供了你防止未授權的網路訪問對你資料庫攻擊的安全手段,然而如果主機上其他使用者能直接訪問資料目錄內容,建立對透過網路訪問資料庫的良好安全性對你毫無幫助,除非你知道你是登入MySQL伺服器執行主機的唯一使用者,否則你需要關心在這臺機器上的其他使用者獲得對資料目錄的訪問的可能性。
以下是你應該保護的內容:
資料庫檔案。很明顯,你要維護伺服器管理的資料庫的私用性。資料庫擁有者通常並且應該考慮資料庫內容的安全性,即使他們不想,也應該考慮時資料庫內容公開化,而不是透過糟糕的資料目錄的安全性來暴露這些內容。
日誌檔案。一般和更新日誌必須保證安全,因為他們包含查詢文字。對日誌檔案有訪問許可權的任何人可以監視資料庫進行過的操作。
更要重點考慮的日誌檔案安全性是諸如GRANT和SET PASSWORD等的查詢也被記載了,一般和更新日誌包含有敏感查詢的文字,包括口令(MySQL使用口令加密,但它在已經完成設定後才運用於以後的連線建立。設定一個口令的過程設計象GRANT或SET PASSWORD等查詢,並且這些查詢以普通文字形式記載在日誌檔案中)。如果一個攻擊者猶如日檔案的讀許可權,只需在日誌檔案上執行grep尋找諸如GRANT和PASSWORD等詞來發現敏感資訊。
顯然,你不想讓伺服器主機上的其他使用者有資料庫目錄檔案的寫許可權,因為他們可以重寫你的狀態檔案或資料庫表檔案,但是讀許可權也很危險。如果一個資料庫表檔案能被讀取,偷取檔案並得到MySQL本身,以普通文字顯示錶的內容也很麻煩,為什麼?因為你要做下列事情:
在伺服器主機上安裝你自己“特製”的MySQL伺服器,但是有一個不同於官方伺服器版本的埠、套接字和資料目錄。
執行mysql_install_db初始化你的資料目錄,這賦予你作為MySQL root使用者訪問你的伺服器的許可權,所以你有對伺服器訪問機制的完全控制,它也建立一個test資料庫。
將對應於你想偷取得表檔案複製到你伺服器的資料庫目錄下的test目錄。
啟動你的伺服器。你可以隨意訪問資料庫表,SHOW TABLES FROM test顯示你有一個偷來的表的複製,SELECT *顯示它們任何一個的全部內容。
如果你確實很惡毒,將許可權公開給你伺服器的任何匿名使用者,這樣任何人能從任何地方連線伺服器訪問你的test資料庫。你現在將偷來的資料庫表公佈於眾了。
在考慮一下,從相反的角度,你想讓別人對你這樣嗎?當然不!你可以透過在資料庫錄下執行ls -l命令確定你的資料庫是否包含不安全的檔案和目錄。查詢有“組”和“其他使用者”許可權設定的檔案和目錄。下面是一個不安全資料目錄的一部分列出:
% ls -l
total 10148
drwxrwxr-x 11 mysqladm wheel 1024 May 8 12:20 .
drwxr-xr-x 22 root wheel 512 May 8 13:31 ..
drwx------ 2 mysqladm mysqlgrp 512 Apr 16 15:57 menagerie
drwxrwxr-x 2 mysqladm wheel 512 Jan 25 20:40 mysql
drwxrwxr-x 7 mysqladm wheel 512 Aug 31 1998 sql-bench
drwxrwxr-x 2 mysqladm wheel 1536 May 6 06:11 test
drwx------ 2 mysqladm mysqlgrp 1024 May 8 18:43 tmp
....
正如你看到的,有些資料庫有正確的許可權,而其他不是。本例的情形是經過一段時間後的結果。較少限制的許可權由在許可權設定方面比更新版本更不嚴格的較早版本伺服器設定的(注意更具限制的目錄menageria和tmp都有較近日期)。MySQL當前版本確保這些檔案只能由執行伺服器的使用者讀取。
讓我們來修正這些許可權,使得只用伺服器使用者可訪問它們。你的主要保護工具來自於由UNIX檔案系統本身提供的設定檔案和目錄屬主和模式的工具。下面是我們要做的:
進入該目錄
% cd DATADIR
設定所有在資料目錄下的檔案屬主為由用於執行伺服器的賬號擁有(你必須以root執行這步)。在本文使用mysqladm和mysqlgrp作為該賬號的使用者名稱和組名。你可以使用下列命令之一改變屬主:
# chown mysqladm.mysqlgrp .
# find . -follow -type d -print | xargs chown mysqladm.mysqlgrp
設定你的資料目錄和資料庫目錄的模式使得他們只能由mysqladm讀取,這阻止其他使用者訪問你資料庫目錄的內容。你可以用下列命令之一以root或mysqladm身份執行。
% chmod -R go-rwx .
% find . -follow -type d -print | xargs chmod go-rwx
資料目錄內容的屬主和模式為mysqladm設定。現在你應該保證你總是以mysqladm使用者執行伺服器,因為現在這是唯一由訪問資料庫目錄許可權的使用者(除root)。
在完成這些設定後,你最終應該得到下面的資料目錄許可權:
% ls -l
total 10148
drwxrwx--- 11 mysqladm mysqlgrp 1024 May 8 12:20 .
drwxr-xr-x 22 root wheel 512 May 8 13:31 ..
drwx------ 2 mysqladm mysqlgrp 512 Apr 16 15:57 menagerie
drwx------ 2 mysqladm mysqlgrp 512 Jan 25 20:40 mysql
drwx------ 7 mysqladm mysqlgrp 512 Aug 31 1998 sql-bench
drwx------ 2 mysqladm mysqlgrp 1536 May 6 06:11 test
drwx------ 2 mysqladm mysqlgrp 1024 May 8 18:43 tmp
....
二、外部安全性-保證網路訪問的安全
MySQL的安全系統是很靈活的,它允許你以多種不垃圾廣告式設定使用者許可權。一般地,你可使用標準的SQL語句GRANT和REVOKE語句做,他們為你修改控制客戶訪問的授權表,然而,你可能由一個不支援這些語句的老版本的MySQL(在3.22.11之前這些語句不起作用),或者你發覺使用者許可權看起來不是以你想要的方式工作。對於這種情況,瞭解MySQL授權表的結構和伺服器如何利用它們決定訪問許可權是有幫助的,這樣的瞭解允許你透過直接修改授權表增加、刪除或修改使用者許可權,它也允許你在檢查這些表時診斷許可權問題。
關於如何管理使用者賬號,見《MySQL的使用者管理》。而對GRANT和REVOKE語句詳細描述,見《MySQL參考手冊》。
2.1 MySQL授權表的結構和內容
透過網路連線伺服器的客戶對MySQL資料庫的訪問由授權表內容來控制。這些表位於mysql資料庫中,並在第一次安裝MySQL的過程中初始化(執行mysql_install_db指令碼)。授權表共有5個表:user、db、host、tables_priv和columns_priv。
表1 user、db和host授權表結構
訪問範圍列
user db host
Host Host Host
User Db Db
Password User
資料庫/表許可權列
Alter_priv Alter_priv Alter_priv
Create_priv Create_priv Create_priv
Delete_priv Delete_priv Delete_priv
Drop_priv Drop_priv Drop_priv
Index_priv Index_priv Index_priv
Insert_priv Insert_priv Insert_priv
References_priv References_priv References_priv
Select_priv Select_priv Select_priv
Update_priv Update_priv Update_priv
File_priv Grant_priv Grant_priv
Grant_priv
Process_priv
Reload_priv
Shutdown_priv
表2 tables_priv和columns_priv屬權表結構
訪問範圍列
tables_priv columns_priv
Host Host
Db Db
User User
Table_name Table_name
Column_name
許可權列
Table_priv Column_priv
授權表的內容有如下用途:
user表
user表列出可以連線伺服器的使用者及其口令,並且它指定他們有哪種全域性(超級使用者)許可權。在user表啟用的任何許可權均是全域性許可權,並適用於所有資料庫。例如,如果你啟用了DELETE許可權,在這裡列出的使用者可以從任何表中刪除記錄,所以在你這樣做之前要認真考慮。
db表
db表列出資料庫,而使用者有許可權訪問它們。在這裡指定的許可權適用於一個資料庫中的所有表。
host表
host表與db表結合使用在一個較好層次上控制特定主機對資料庫的訪問許可權,這可能比單獨使用db好些。這個表不受GRANT和REVOKE語句的影響,所以,你可能發覺你根本不是用它。
tables_priv表
tables_priv表指定表級許可權,在這裡指定的一個許可權適用於一個表的所有列。
columns_priv表
columns_priv表指定列級許可權。這裡指定的許可權適用於一個表的特定列。
在“不用GRANT設定使用者”一節裡,我們再討論GRANT語句如何對修改這些表起作用,和你怎樣能透過直接修改授權表達到同樣的效果。
tables_priv和columns_priv表在MySQL 3.22.11版引進(與GRANT語句同時)。如果你有較早版本的MySQL,你的mysql資料庫將只有user、db和host表。如果你從老版本升級到3.22.11或更新,而沒有tables_priv和columns_priv表,執行mysql_fix_privileges_tables指令碼建立它們。
MySQL沒有rows_priv表,因為它不提供記錄級許可權,例如,你不能限制使用者於表中包含特定列值的行。如果你確實需要這種能力,你必須用應用程式設計來提供。如果你想執行建議的記錄級鎖定,你可用GET_LOCK()函式做到。
授權表包含兩種列:決定一個許可權何時運用的範圍列和決定授予哪種許可權的許可權列。
2.1.1 授權表範圍列
授權表範圍列指定表中的許可權何時運用。每個授權表條目包含User和Host列來指定許可權何時運用於一個給定使用者從給定主機的連線。其他表包含附加的範圍列,如db表包含一個Db列指出許可權運用於哪個資料庫。類似地,tables_priv和columns_priv表包含範圍欄位,縮小範圍到一個資料庫中的特定表或一個表的特定列。
2.1.2 授權表許可權列
授權表還包含許可權列,他們指出在範圍列中指定的使用者擁有何種許可權。由MySQL支援的許可權如下表所示。該表使用GRANT語句的許可權名稱。對於絕大多數在user、db和host表中的許可權列的名稱與GRANT語句中有明顯的聯絡。如Select_priv對應於SELECT許可權。
|-page-|
2.1.3 資料庫和表許可權
下列許可權運用於資料庫和表上的操作。
ALTER
允許你使用ALTER TABLE語句,這其實是一個簡單的第一級許可權,你必須由其他許可權,這看你想對資料庫實施什麼操作。
CREATE
允許你建立資料庫和表,但不允許建立索引。
DELETE
允許你從表中刪除現有記錄。
DROP
允許你刪除(拋棄)資料庫和表,但不允許刪除索引。
INDEX
允許你建立並刪除索引。
REFERENCES
目前不用。
SELECT
允許你使用SELECT語句從表中檢索資料。對不涉及表的SELECT語句就不必要,如SELECT NOW()或SELECT 4/2。
UPDATE
允許你修改表中的已有的記錄。
2.1.4 管理許可權
下列許可權運用於控制伺服器或使用者授權能力的操作的管理性操作。
FILE
允許你告訴伺服器讀或寫伺服器主機上的檔案。該許可權不應該隨便授予,它很危險,見“迴避授權表風險”。伺服器確實較謹慎地保持在一定範圍內使用該許可權。你只能讀任何人都能讀的檔案。你正在寫的檔案必須不是現存的檔案,這防止你迫使伺服器重寫重要檔案,如/etc/passwd或屬於別人的資料庫的資料目錄。
如果你授權FILE許可權,確保你不以UNIX的root使用者執行伺服器,因為root可在檔案系統的任何地方建立新檔案。如果你以一個非特權使用者執行伺服器,伺服器只能在給使用者能訪問的目錄中建立檔案。
GRANT
允許你將你自己的許可權授予別人,包括GRANT。
PROCESS
允許你透過使用SHOW PROCESS語句或mysqladmin process命令檢視伺服器內正在執行的執行緒(程式)的資訊。這個許可權也允許你用KILL語句或mysqladmin kill命令殺死執行緒。
你總是能看到或殺死你自己的執行緒。PROCESS許可權賦予你對任何執行緒做這些事情的能力。
RELOAD
允許你執行大量的伺服器管理操作。你可以發出FLUSH語句,你也能指性mysqladmin的reload、refresh、flush-hosts、flush-logs、flush-privileges和flush-tables等命令。
SHUTDOWN
允許你用mysqladmin shutdown關閉伺服器。
在user、db和host表中,每一個許可權以一個單獨的列指定。這些列全部宣告為一個ENUM("N","Y")型別,所以每個權的預設值是“N”。在tables_priv和columns_priv中的許可權以一個SET表示,它允許許可權用一個單個列以任何組合指定。這兩個表比其他三個表更新,這就是為什麼它們使用更有效的表示方式的原因。(有可能在未來,user、db和host表也用一個SET型別表示。)
在tables_priv表中的Table_priv列被定義成:
SET(´Select´,´Insert´,´Update´,´Delete´,´Create´,´Drop´,´Grant´,´References´,´Index´,´Alter´)
在coloums_priv表中的Column_priv列被定義成:
SET(´Select´,´Insert´,´Update´,´References´)
列許可權比表許可權少,因為列級較少的許可權有意義。例如你能建立一個表,但你不能建立一個孤立的列。
user表包含某些在其他授權表不存在的許可權的列:File_priv、Process_priv、Reload_priv和Shutdown_priv。這些許可權運用於你讓伺服器執行的與任何特定資料庫或表不相關的操作。如允許一個使用者根據當前資料庫是什麼來關閉資料庫是毫無意義的。
2.2 伺服器如何控制客戶訪問
在你使用MySQL時,客戶訪問控制有兩個階段。第一階段發生在你試圖連線伺服器時。伺服器查詢user表看它是否能找到一個條目匹配你的名字、你正在從那兒連線的主機和你提供的口令。如果沒有匹配,你就不能連線。如果有一個匹配,建立連線並繼續第二階段。在這個階段,對於每一個你發出的查詢,伺服器檢查授權表看你是否有足夠的許可權執行查詢,第二階段持續到你與伺服器對話的結束。
本小節詳細介紹MySQL伺服器用於將授權表條目匹配到來的連線請求或查詢的原則,這包括在授權表範圍列中合法的值的型別、結合授權表中的許可權資訊的方式和表中條目被檢查的次序。
2.2.1 範圍列內容
一些範圍列要求文字值,但它們大多數允許萬用字元或其他特殊值。
Host
一個Host列值可以是一個主機名或一個IP地址。值localhost意味著本地主機,但它只在你用一個localhost主機名時才匹配,而不是你在使用主機名時。假如你的本地主機名是pit.snake.net並且在user表中有對你的兩條記錄,一個有一個Host值或localhost,而另一個有pit.snake.net,有localhost的記錄將只當你連線localhost時匹配,其他在只在連線pit.snake.net時才匹配。如果你想讓客戶能以兩種方式連線,你需要在user表中有兩條記錄。
你也可以用萬用字元指定Host值。可以使用SQL的模式字元“%”和“_”並具有當你在一個查詢中使用LIKE算符同樣的含義(不允許regex算符)。 SQL模式字元都能用於主機名和IP地址。如%wisc.edu匹配任何wisc.edu域內的主機,而%.edu匹配任何教育學院的主機。類似地,192.168.%匹配任何在192.168 B類子網的主機,而192.168.3.%匹配任何在192.168.3 C類子網的主機。
%值匹配所有主機,並可用於允許一個使用者從任何地方連線。一個空白的Host值等同於%。(例外:在db表中,一個空白Host值含義是“進一步檢查host表”,該過程在“查詢訪問驗證”中介紹。)
從MySQL 3.23起,你也可以指定帶一個表明那些為用於網路地址的網路掩碼的IP地址,如192.168.128.0/17指定一個17位網路地址並匹配其IP地址是192.168.128前17位的任何主機。
User
使用者名稱必須是文字的或空白。一個空白值匹配任何使用者。%作為一個User值不意味著空白,相反它匹配一個字面上的%名字,這可能不是你想要的。
當一個到來的連線透過user表被驗證而匹配的記錄包含一個空白的User值,客戶被認為是一個匿名使用者。
Password
口令值可以是空或非空,不允許用萬用字元。一個空口令不意味著匹配任何口令,它意味著使用者必須不指定口令。
口令以一個加密過的值儲存,不是一個字面上的文字。如果你在Password列中儲存一個照字面上的口令,使用者將不能連線!GRANT語句和mysqladmin password命令為你自動加密口令,但是如果你用諸如INSERT、REPLACE、UPDATE或SET PASSWORD等命令,一定要用PASSWORD("new_password")而不是簡單的"new_password"來指定口令。
Db
在columns_priv和tables_priv表中,Db值必須是真正的資料庫名(照字面上),不允許模式和空白。在db和host中,Db值可以以字面意義指定或使用SQL模式字元´%´或´_´指定一個萬用字元。一個´%´或空白匹配任何資料庫。
Table_name,Column_name
這些列中的值必須是照字面意思的表或列名,不允許模式和空白。
某些範圍列被伺服器視為大小寫敏感的,其餘不是。這些原則總結在下表中。特別注意Table_name值總是被看作大小寫敏感的,即使在查詢中的表名的大小寫敏感性對待視伺服器執行的主機的檔案系統而定(UNIX下是大小寫敏感,而Windows不是)。
表3 授權表範圍列的大小寫敏感性
列
Host
User
Password
Db
Table_name
Column_name
大小寫敏感性
No
Yes
Yes
Yes
Yes
No
2.2.2 查詢訪問驗證
每次你發出一個查詢,伺服器檢查你是否有足夠的許可權執行它,它以user、db、tables_priv和columns_priv的順序檢查,知道它確定你有適當的訪問許可權或已搜尋所有表而一無所獲。更具體的說:
伺服器檢查user表匹配你開始連線的記錄以檢視你有什麼全域性許可權。如果你有並且它們對查詢足夠了,伺服器則執行它。
如果你的全域性許可權不夠,伺服器為你在db表中尋找並將該記錄中的許可權加到你的全域性許可權中。如果結果對查詢足夠,伺服器執行它。
如果你的全域性和資料庫級組合的許可權不夠,伺服器繼續查詢,首先在tables_priv表,然後columns_priv表。
如果你在檢查了所有表之後仍無許可權,伺服器拒絕你執行查詢的企圖。
用布林運算的術語,授權表中的許可權被伺服器這樣使用:
user OR tables_priv OR columns_priv
你可能疑惑為什麼前面的描述只引用4個授權表,而實際上有5個。實際上伺服器是這樣檢查訪問許可權:
user OR (db AND host) OR tables_priv OR columns_priv
第一個較簡單的表示式是因為host表不受GRANT和REVOKE語句影響。如果你總是用GRANT和REVOKE管理使用者許可權,你絕不需要考慮host表。但是其工作原理你用該知道:
當伺服器檢查資料庫級許可權時,它對於客戶查詢db表。如果Host列是空的,它意味著“檢查host表以找出哪一個主機能訪問資料庫”。
伺服器在host表中查詢有與來自db表的記錄相同的Db列值。如果沒有host記錄匹配客戶主機,則沒有授予資料庫級許可權。如果這些記錄的任何一個的確有一個匹配連線的客戶主機的Host列值,db表記錄和host表記錄結合產生客戶的資料庫級許可權。
然而,許可權用一個邏輯AND(與)結合起來,這意味著除非一個給定的許可權在兩個表中都有,否則客戶就不具備該許可權。以這種方式,你可以在db表中授予一個基本的許可權集,然後使用host表對特定的主機有選擇地禁用它們。如你可以允許從你的域中的所有主機訪問資料庫,但關閉了那些在較不安全區域的主機的資料庫許可權。
前面的描述毫無疑問使訪問檢查聽起來一個相當複雜的過程,特別是你以為伺服器對你發出的每個查詢進行許可權檢查,然而此過程是很快的,因為伺服器其實不從授權表對每個查詢查詢資訊,相反,它在啟動時將表的內容讀入記憶體,然後驗證查詢用的是記憶體中的副本。這大大提高了訪問檢查操作的效能。但有一個非常明顯的副作用。如果你直接修改授權表的內容,伺服器將不知道許可權的改變。
例如,如果你用一條INSERT語句向user表加入一個新記錄來增加一個新使用者,命名在記錄中的使用者將不能連線伺服器。這對管理員新手(有時對有經驗的老手)是很困惑的事情,當時解決方法很簡單:在你改變了它們之後告訴伺服器過載授權表內容,你可以發一條FLUSH PRIVILEGES或執行mysqladmin flush-privileges(或如果你有一個不支援flush-privileges的老版本,用mysqladmin reload。)。
2.2.3 範圍列匹配順序
MySQL伺服器按一種特定方式排序符授權表中的記錄,然後透過按序瀏覽記錄匹配到來的連線。找到的第一個匹配決定了被使用的記錄。理解MySQL使用的排序順序很重要,特別是對user表。
當伺服器讀取user表內容時,它根據在Host和User列中的值排序記錄,Host值起決定作用(相同的Host值排在一起,然後再根據User值排序)。然而,排序不是典序(按詞排序),它只是部分是。要牢記的是字面上的詞優先於模式。這意味著如果你正從client.your.net連線伺服器而Host有client.your.net和%.your.net兩個值,則第一個先選。類似地,%.your.net優先於%.net,然後是%。IP地址的匹配也是這樣的。
總之一句話,越具體越優先。可以參見本文附錄的例項。
2.3 避免授權表風險
本屆介紹一些在你授權時的一些預防措施,以及不明值的選擇帶來的風險。一般地,你要很“吝嗇”地授予超級使用者許可權,即不要啟用user表中條目中的許可權,而使用其它授權表,以將使用者許可權限制於資料庫、表、或列。在user表中的許可權允許於影響到你的伺服器操作或能訪問任何資料庫中的任何表。
不要授予對mysql資料庫的許可權。一個擁有包含授權表資料庫許可權的使用者可能會修改表以獲取對其他任何資料庫的許可權。授予允許一個使用者修改mysql資料庫表的許可權也實際上給了使用者以一個全域性GRANT許可權。如果使用者能直接修改表,這也等價於能夠發出任何你能想象的任何GRANT語句。
FILE許可權尤其危險,不要輕易授權它。以下是一個擁有FILE許可權的人能幹除的事情:
CREATE TABLE etc_passwd (pwd_entry TEXT);
LOAD DATA INFILE "/etc/passwd" into TABLE etc_passwd;
SELECT * FROM etc_passwd;
在發出這些語句後,使用者已經擁有了你的口令檔案的內容了。實際上,伺服器上任何公開可讀檔案的內容都可被擁有FILE許可權的使用者透過網路訪問。
FILE許可權也能被利用來危害沒有設定足夠許可權制的檔案許可權的系統上的資料庫。這就是你為什麼應該設定資料目錄只能由伺服器讀取的原因。如果對應於資料庫表的檔案可被任何人讀取,不只是使用者伺服器賬號的使用者可讀,任何有FILE許可權的使用者也可透過網路連線並讀取它們。下面演示這個過程:
建立一個有一個LONGBLOB列的表:
USER test;
CREATE TABLE tmp (b LONGBLOB);
使用該表讀取每個對應於你想偷取的資料庫表檔案的內容,然後將表內容寫入你自己資料庫的一個檔案中:
LOAD DATA INFILE "./other_db/x.frm" INTO TABLE tmp
FIELDS ESCAPED BY "" LINES TERMINATED BY "";
SELECT * FROM tmp INTO OUTFILE "y.frm"
FIELDS ESCAPED BY "" LINES TERMINATED BY "";
DELETE FROM tmp;
LOAD DATA INFILE "./other_db/x.ISD" INTO TABLE tmp
FIELDS ESCAPED BY "" LINES TERMINATED BY "";
SELECT * FROM tmp INTO OUTFILE "y.ISD"
FIELDS ESCAPED BY "" LINES TERMINATED BY "";
DELETE FROM tmp;
LOAD DATA INFILE "./other_db/x.ISM" INTO TABLE tmp
FIELDS ESCAPED BY "" LINES TERMINATED BY "";
SELECT * FROM tmp INTO OUTFILE "y.ISM"
現在你擁有了一個新表y,它包含other_db.x的內容並且你有全權訪問它。
為避免讓人以同樣的方式攻擊,根據“第一部分 內部安全性-保護你的資料目錄”中的指令設定你的資料目錄上的許可權。你也可以在你啟動伺服器時使用--skip-show-database選項限制使用者對於他們沒用訪問許可權的資料庫使用SHOW DATABASES和SHOW TABLES。這有助於防止使用者找到關於它們不能訪問的資料庫和表的資訊。
ALTER許可權能以不希望的方式使用。假定你想讓user1可以訪問table1但不能訪問tables2。一個擁有ALTER許可權的使用者可以透過使用ALTER TABLE將table2改名為table1來偷樑換柱。
當心GRANT許可權。兩個由不同許可權但都有GRANT許可權的使用者可以使彼此的權利更強大。
|-page-|
2.4 不用GRANT設定使用者
如果你有一個早於3.22.11的MySQL版本,你不能使用GRANT(或REVOKE)語句設定使用者及其訪問許可權,但你可以直接修改授權表的內容。如果你理解GRANT語句如何修改授權表,這很容易。那麼你透過手工發出INSERT語句就能自己做同樣的事情。
當你發出一條GRANT語句時,你指定一個使用者名稱和主機名,可能還有口令。對該使用者生成一個user表記錄,並且這些值記錄在User、Host和Password列中。如果你在GRANT語句中指定全域性許可權,這些許可權記錄在記錄的許可權列中。其中要留神的是GRANT語句為你加密口令,而INSERT不是,你需要在INSERT中使用PASSWORD()函式加密口令。
如果你指定資料庫級許可權,使用者名稱和主機名被記錄在db表的User和Host列。你為其授權的資料庫記錄在Db列中,你授予的許可權記錄在許可權列中。
對於表級和列級許可權,效果是類似的。在tables_priv和columns_priv表中建立記錄以記錄使用者名稱、主機名和資料庫,還有相關的表和列。授予的許可權記錄在許可權列中。
如果你還記得前面的介紹,你應該能即使不用GRANT語句也能做GRANT做的事情。記住在你直接修改授權表時,你將通知伺服器過載授權表,否則他不知道你的改變。你可以執行一個mysqladmin flush-privileges或mysqladmin reload命令強迫一個過載。如果你忘記做這個,你會疑惑為什麼伺服器不做你想做的事情。
下列GRANT語句建立一個擁有所有權的超級使用者。包括授權給別人的能力:
GRANT ALL ON *.* TO anyname@localhost IDENTIFIED BY "passwd"
WITH GRANT OPTION
該語句將在user表中為anyname@localhost建立一個記錄,開啟所有許可權,因為這裡是超級使用者(全域性)許可權儲存的地方,要用INSERT語句做同樣的事情,語句是:
INSERT INTO user VALUES("localhost","anyname",PASSWORD("passwd"),
"Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y","Y")
你可能發現它不工作,這要看你的MySQL版本。授權表的結構已經改變而且你在你的user表可能沒有14個許可權列。用SHOW COLUMNS找出你的授權表包含的每個許可權列,相應地調整你的INSERT語句。 下列GRANT語句也建立一個擁有超級使用者身份的使用者,但是隻有一個單個的許可權:
GRANT RELOAD ON *.* TO flush@localhost IDENTIFIED BY "flushpass"
本例的INSERT語句比前一個簡單,它很容易列出列名並只指定一個許可權列。所有其它列將設定為預設的"N":
INSERT INTO user (Host,Password,Reload) VALUES("localhost","flush",PASSWORD("flushpass"),"Y")
資料庫級許可權用一個ON db_name.*子句而不是ON *.*進行授權:
GRANT ALL ON sample.* TO boris@localhost IDENTIFIED BY "ruby"
這些許可權不是全域性的,所以它們不儲存在user表中,我們仍然需要在user表中建立一條記錄(使得使用者能連線),但我們也需要建立一個db表記錄記錄資料庫集許可權:
INSERT INTO user (Host,User,Password) VALUES("localhost","boris",PASSWORD("ruby"))
INSERT INTO db VALUES("localhost","sample_db","boris","Y","Y","Y","Y","Y","Y","N","Y","Y","Y")
"N"列是為GRANT許可權;對末尾的一個資料庫級具有WITH GRANT OPTION的GRANT語句,你要設定該列為"Y"。
要設定表級或列級許可權,你對tables_priv或columns_priv使用INSERT語句。當然,如果你沒有GRANT語句,你將沒有這些表,因為它們在MySQL中同時出現。如果你確實有這些表並且為了某些原因想要手工操作它們,要知道你不能用單獨的列啟用許可權。
你設定tables_priv.Table_priv或columns_priv.Column_priv列來設定包含你想啟用的許可權值。例如,要對一個表啟用SELECT和INSERT許可權,你要在相關的tables_priv的記錄中設定Table_priv為"Select,Insert"。
如果你想對一個擁有MySQL賬號的使用者修改許可權,使用UPDATE而不是INSERT,不管你增加或撤銷許可權都是這樣。要完全刪除一個使用者,從使用者使用的每個表中刪除記錄。
如果你願意避免發一個查詢來直接修改全權表,你可以看一下MySQL自帶的mysqlaccess和mysql_setpermissions指令碼。
附錄1 小測驗
在你剛剛新安裝了一個MySQL伺服器,在你增加了一個允許連線MySQL的使用者,用下列語句:
GRANT ALL ON samp_db.* TO fred@*.snake.net IDENTIFIED "cocoa"
而fred碰巧在伺服器主機上有個賬號,所以他試圖連線伺服器:
%mysql -u fred -pcocoa samp_db
ERROR 1045: Access denied for user: ´fred@localhost´ (Using password: YES)
為什麼?
原因是:
先考慮一下mysql_install_db如何建立初始許可權表和伺服器如何使用user表記錄匹配客戶連線。在你用mysql_install_db初始化你的資料庫時,它建立類似這樣的user表:
Host User
localhost
pit.snake.net
localhost
pit.snake.net root
root
頭兩個記錄允許root指定localhost或主機名連線本地伺服器,後兩個允許匿名使用者從本地連線。當增加fred使用者後,
Host User
localhost
pit.snake.net
localhost
pit.snake.net
%.snake.net root
root
fred
在伺服器啟動時,它讀取記錄並排序它們(首先按主機,然後按主機上的使用者),越具體越排在前面:
Host User
localhost
localhost
pit.snake.net
pit.snake.net
%.snake.net root
root
fred
有localhost的兩個記錄排在一起,而對root的記錄排在第一,因為它比空值更具體。pit.snake.net的記錄也類似。所有這些均是沒有任何萬用字元的字面上的Host值,所以它們排在對fred記錄的前面,特別是匿名使用者排在fred之前。
結果是在fred試圖從localhost連線時,Host列中的一個空使用者名稱的記錄在包含%.snake.net的記錄前匹配。該記錄的口令是空的,因為預設的匿名使用者沒有口令。因為在fred連線時指定了一個口令,由一個錯配且連線失敗。
這裡要記住的是,雖然用萬用字元指定使用者可以從其連線的主機是很方便。但你從本地主機連線時會有問題,只要你在table表中保留匿名使用者記錄。
一般地,建議你刪除匿名使用者記錄:
mysql> DELETE FROM user WHERE User="";
更進一步,同時刪除其他授權表中的任何匿名使用者,有User列的表有db、tables_priv和columns_priv。
附錄2 使一個新的MySQL安裝更安全
在你自己安裝了一個新的MySQL伺服器後,你需要為MySQL的root使用者指定一個目錄(預設無口令),否則如果你忘記這點,你將你的MySQL處於極不安全的狀態(至少在一段時間內)。
在Unix(Linux)上,在按照手冊的指令安裝好MySQL後,你必須執行mysql_install_db指令碼建立包含授權表的mysql資料庫和初始許可權。在Windows上,執行分發中的Setup程式初始化資料目錄和mysql資料庫。假定伺服器也在執行。
當你第一次在機器上安裝MySQL時,mysql資料庫中的授權表是這樣初始化的:
你可以從本地主機(localhost)上以root連線而不指定口令。root使用者擁有所有許可權(包括管理許可權)並可做任何事情。(順便說明,MySQL超級使用者與Unix超級使用者有相同的名字,他們彼此毫無關係。)
匿名訪問被授予使用者可從本地連線名為test和任何名字以test_開始的資料庫。匿名使用者可對資料庫做任何事情,但無管理許可權。
從本地主機多伺服器的連線是允許的,不管連線的使用者使用一個localhost主機名或真實主機名。如:
% mysql -h localhost test
% mysql -h pit.snake.net test
你以root連線MySQL甚至不指定口令的事實只是意味著初始安裝不安全,所以作為管理員的你首先要做的應該是設定root口令,然後根據你設定口令使用的方法,你也可以告訴伺服器過載授權表是它知道這個改變。(在伺服器啟動時,它過載表到記憶體中而可能不知道你已經修改了它們。)
對MySQL 3.22和以上版本,你可以用mysqladmin設定口令:
% mysqladmin -u root password yourpassword
對於MySQL的任何版本,你可以用mysql程式並直接修改mysql資料庫中的user授權表:
% mysql -u root mysql
mysql>UPDATE user SET password=PASSWORD("yourpassword") WHERE User="root";
如果你有MySQL的老版本,使用mysql和UPDATE。
在你設定完口令後,透過執行下列命令檢查你是否需要告訴伺服器過載授權表:
% mysqladmin -u root status
如果伺服器仍然讓你以root而不指定口令而連線伺服器,過載授權表:
% mysqladmin -u root reload
在你設定了root的口令後(並且如果需要過載了授權表),你將需要在任何時候以root連線伺服器時指定口令。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/94384/viewspace-600294/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 服務端指南 資料儲存篇 | MySQL(06) 資料庫安全性服務端MySql資料庫
- MySQL基本用法-系統安全性MySql
- MySQL 入門指南MySql
- 檔案系統和檔案 API 安全性缺失指南API
- Mssql和Mysql的相關安全性分析(轉)MySql
- PHPer 面試指南-MySQL 篇PHP面試MySql
- PHPer面試指南-MySQL 篇PHP面試MySql
- MySQL效能優化指南MySql優化
- Mysql監控快速指南MySql
- 【MySQL】資料安全性討論思維導圖MySql
- java安全編碼指南之:檔案和共享目錄的安全性Java
- MySQL 之 LEFT JOIN 避坑指南MySql
- MySQL指南之基礎知識MySql
- MySQL 不完全入門指南MySql
- MySQL分頁效能優化指南MySql優化
- MySQL指南之SQL語句基礎MySql
- MySQL MHA部署 Part 5 MHA部署指南MySql
- 寫給新手的MySQL入門指南MySql
- Ubuntu Linux:MySQL安裝指南(轉)UbuntuLinuxMySql
- oracle安全性Oracle
- MySQL資料庫的效能優化指南MySql資料庫優化
- MySQL終端管理資料庫操作指南MySql資料庫
- mysql4.1資料轉換指南(轉)MySql
- 採用ODBC介面訪問MySQL指南 (轉)MySql
- SOA安全性基礎知識:測試SOA安全性
- MySQL Workbench 中文使用指南 - 如何使用 Workbench 操作 MySQL 資料庫教程MySql資料庫
- jmeter學習指南之操作 mysql 資料庫JMeterMySql資料庫
- MySQL資料庫伺服器搭建指南-VeCloudMySql資料庫伺服器Cloud
- MySQL & MariaDB Online DDL 參考指南MySql
- MySQL 安裝指南 二進位制安裝MySql
- MySQL DBA備份工具Xtrabackup使用指南(轉)MySql
- IT安全性如何提高
- API的安全性API
- MySQL 頁完全指南——淺入深出頁的原理MySql
- mysql57小版本升級操作指南MySql
- 4A安全性分析
- PHP安全性漫談PHP
- Web安全性測試Web