MySQL安全

1. 安全準則mysql

任何想要在鏈接到公網的計算機上使用MySQL的人都應該閱讀本節,以免最多見的安全錯誤。算法

在討論安全性時,有必要考慮徹底保護整個服務器主機,而不只僅是MySQL服務器,以防止全部類型的攻擊:竊聽,更改,重放和拒絕服務。咱們並不涵蓋可用性和容錯的全部方面。sql


MySQL使用基於訪問控制列表(ACL)的安全策略管理全部的鏈接、查詢以及用戶嘗試執行的其它操做。MySQL還支持客戶端和服務器之間的SSL加密鏈接。這裏討論的許多概念並不只僅是針對MySQL的,而是幾乎適用於全部應用程序的。數據庫


運行MySQL時,請遵循如下準則:編程

● 不要給除了root帳號之外的任何人訪問mysql.user表的權限!這是相當重要的。安全


● 瞭解MySQL訪問權限系統的工做原理。使用grant和revoke語句來控制對MySQL的訪問。不要授予比必要更多的權限。決不授予全部主機的訪問權限。bash

檢查清單:服務器

        ■ 運行mysql -u root. 若是不須要密碼便可鏈接成功,那麼任何人均可以以root帳號的身份鏈接到你的MySQL服務器;網絡

        ■ 使用show grants語句來檢查全部帳號的權限。而後使用revoke語句刪除那些沒必要要的訪問權限;tcp


● 不要在你的數據庫中存儲明文密碼。由於若是你的計算機遭到入侵,那麼入侵者能夠獲取完整的密碼列表並使用它們。相反,使用SHA2()或其它單向散列函數並存儲散列值。


要防止使用彩虹表進行密碼恢復,請勿直接在普通密碼上使用這些函數;而是應該選擇一些字符串做爲salt,並使用hash ( hash ( password ) + salt)的值。


● 不要從字典中選擇密碼。由於存在破解密碼的特殊程序。即便像"xfish98"這樣的密碼也很糟糕。更好的是"duag98",它包含的也是單詞"fish",但在標準鍵盤上取了"fish"中每一個字母左側的那個字母。另外一種方法是使用句子中每一個單詞的首字母提取的密碼(例如,"Four score and seven years ago"的密碼是"Fsasya")。這樣的密碼很容易記住和鍵入,但對於不知道句子的人卻很難猜出。並且,你還能夠用數字取代數字單詞,以得到短語"4 score and 7 years ago",從而產生密碼"4sa7ya",這更難以猜想。


● 投資於防火牆。這能夠保護你免受來自各類軟件全部類型漏洞中至少50%的傷害。將MySQL放在防火牆後面或放在非軍事區域(DMZ)中。

檢查清單:

        ■ 嘗試使用nmap等工具從Internet上掃描你的端口。MySQL的默認端口爲3306。這個端口不該該可以被不受信任的主機所訪問,即不受信任的主機應不能訪問此端口。檢查你的MySQL端口是否打開的一個簡單方法是,嘗試從一些遠程計算機上執行如下命令,其中server_host是運行MySQL服務器的主機的主機名或IP地址

若是telnet掛起或鏈接被拒絕,那麼說明端口被限制了,這就是指望的結果。若是鏈接成功並收到一些垃圾字符,則說明端口是打開的,那麼此時你應該在防火牆或路由器上關閉該端口,除非你真的有一個很好的理由保持打開。


● 訪問MySQL的應用程序不該該信任用戶輸入的任何數據,應該使用適當的防護性編程技術編寫這些應用程序。


● 不要在互聯網上傳輸普通的(未加密的)數據。全部有時間和能力攔截它的人均可能獲取到這些信息,並將其用於本身的目的。相反,咱們應該使用加密的協議,如SSL或SSH。MySQL支持內部SSL鏈接。另外一種技術是使用SSH端口轉發來建立用於通訊的加密(和壓縮)隧道。


● 學習使用tcpdump和strings實用工具。在大多數狀況下,你能夠經過如下命令來檢查MySQL數據流是否未加密:


若是你沒有看到明文數據,這並不老是意味着信息是加密的。若是你須要高安全性,請諮詢安全專家。



2. 保證密碼安全

密碼出如今MySQL內的幾個上下文中。下面的部分提供了指導原則,使終端用戶和管理員可以確保這些密碼的安全,避免暴露它們。還有一個關於MySQL如何在內部使用密碼散列的討論。


2.1 終端用戶密碼安全指南

MySQL用戶應該使用如下準則來保證密碼安全。


當你運行客戶端程序鏈接到MySQL服務器時,以一種將其暴露給其餘用戶的方式來指定密碼是不明智的。這裏列出了在運行客戶端程序時能夠用來指定密碼的方法,以及每種方法的風險評估。簡而言之,最安全的方法是讓客戶端程序提示輸入密碼,或者在恰當保護的選項文件中指定密碼。


      ● 在命令行上使用 -pyour_pass 」或「--password=your_pass」選項。例如:

        

這種方法很方便,但不安全。在某些系統中,系統狀態程序能夠看到你的密碼,好比其餘用戶能夠調用ps,以顯示命令行。在初始化序列期間,MySQL客戶端一般會用零覆蓋命令行中的密碼參數。可是,仍然有一段短暫的時間間隔,值是可見的。此外,在某些系統上,這種覆蓋策略是無效的,密碼對於ps仍舊可見。


若是你的操做環境設置爲在終端窗口的標題欄中顯示當前命令,那麼只要命令正在運行,密碼仍然可見,即便命令已經在窗口內容區域中滾動了。


      ● 在命令行上使用「-p」或「--password」選項,而不指定密碼值。在這種狀況下,客戶端程序會以交互的方式請求密碼:

*表示輸入密碼的位置。當你輸入密碼時,密碼不會顯示出來。


輸入密碼比在命令行上指定密碼更安全,由於它對其餘用戶不可見。可是,輸入密碼的方法僅適用於以交互方式運行的程序。若是你想從非交互運行的腳本中調用客戶端,則沒法從鍵盤輸入密碼。在某些系統上,你甚至可能會發現腳本的第一行被讀取並錯誤地解釋爲密碼。


      ● 將你的密碼存儲在選項文件中。例如,在Unix上,你能夠在主目錄中的.my.cnf文件的[client]部分列出密碼:

爲了保證密碼的安全,除了你之外,任何人都不能訪問這個文件。爲了確保這一點,請將文件訪問模式設置爲400或600。例如:

要在命令行上指定包含密碼的特定選項文件,請使用「--defaults-file=file_name」選項,其中file_name是文件的完整路徑名。例如:


      ● 將你的密碼存儲在「MYSQL_PWD」環境變量中。


這種指定MySQL密碼的方法是很是不安全的,不該該被使用。某些版本的ps包含了顯示運行進程環境的選項。在某些系統上,若是你設置了「MYSQL_PWD」,那麼你的密碼就會被暴露給運行ps的任何其餘用戶。即便在沒有這種版本的ps的系統上,假設沒有其餘用戶能夠檢查進程環境的方法也是不明智的。


在Unix上,MySQL客戶端會將執行過的語句寫入一個歷史文件。默認狀況下,此文件名爲「.mysql_history」,並在你的主目錄中建立。例如create user, grant和set password之類的SQL語句中的密碼是以純文本的形式寫入的,所以若是你使用了這些語句,則它們將被記錄在歷史文件中。爲了保證這個文件的安全,請使用與「.my.cnf」文件相同的訪問限制模式。


若是你的命令解釋器被配置爲維護歷史記錄,則用於保存命令的任何文件都將包含在命令行中輸入的MySQL密碼。例如,bash使用「~/.bash_history」。任何此類文件都應該具備限制性訪問模式。


2.2 管理員密碼安全指南

數據庫管理員應使用如下準則來保證密碼安全。


MySQL在mysql.user表中存儲帳號密碼。不該向任何非管理帳號授予訪問該表的權限。


有權修改插件目錄(plugin_dir系統變量的值)或指定插件目錄位置的my.cnf文件的用戶能夠替換插件,並修改插件提供的功能,包括驗證插件。


應該保護可能寫入密碼的日誌文件等文件。


2.3 密碼和日誌

在create user, grant, set password以及調用password()函數的SQL語句中密碼以純文本的形式寫入。若是這些語句被MySQL服務器記錄,那麼有權訪問日誌的任何人均可以看到密碼。這適用於通常查詢日誌,慢查詢日誌和二進制日誌。


審覈日誌插件生成的審覈日誌文件的內容未加密。爲了安全起見,這個文件應該被寫入一個只有MySQL服務器和有合法理由查看日誌的用戶才能夠訪問的目錄中。


爲了防範日誌文件被曝光,請將它們放在只有MySQL服務器和數據庫管理員纔有權訪問的目錄中。若是服務器將日誌記錄到「mysql」庫表中,則只將這些表的訪問權限授予數據庫管理員。


複製slave將master的密碼存儲在「master.info」文件中。限制此文件只能由數據庫管理員訪問。


使用受限訪問模式來保護包含日誌表或包含密碼的日誌文件的數據庫備份。



3. 保護MySQL免受攻擊

當鏈接到MySQL服務器時,你應該使用密碼。密碼不會在鏈接上以明文的形式傳輸。在MySQL4.1.1中,客戶端鏈接序列中的密碼處理通過升級,變得很是安全。若是你仍然使用的是4.1.1以前的密碼,則加密算法不如新算法那樣強大。經過一些努力,聰明的攻擊者能夠嗅探客戶端和服務器之間的流量,從而破解密碼。


其它全部信息都以純文本的形式傳輸,任何可以查看鏈接的人均可以閱讀。若是客戶端和服務器之間的鏈接經過了一個不受信任的網絡,而且你爲此感到擔心,那麼你可使用壓縮協議使流量更加難以解密。你還可使用MySQL的內部SSL支持,使鏈接更加安全。或者,使用SSH在MySQL服務器和MySQL客戶端之間創建加密的TCP/IP鏈接。


要確保MySQL系統安全,你務必仔細考慮如下建議:

● 要求全部MySQL帳號都要有密碼。客戶端程序不必定知道運行它的人的身份。對於客戶端/服務器應用程序,用戶能夠爲客戶端程序指定任何用戶名,這很常見。例如,若是other_user沒有密碼,那麼任何人均可以經過調用mysql -u other_user db_name以其餘人的身份鏈接到服務器。若是全部的帳號都有密碼,那麼使用其餘用戶的帳號進行鏈接就會變得更加困難。


● 確保惟一對數據庫目錄具備讀或寫權限的Unix帳號是用於運行mysqld的帳號。


● 不要以Unix root用戶的身份運行MySQL服務器。這是很是危險的,由於這將使得任何具備「file」權限的帳號都可以使MySQL服務器以Unix root用戶的身份建立文件(例如,~root/.bashrc文件)。爲了防止這種狀況,mysqld拒絕以root身份運行,除非使用「--user=root」選項顯式指明。


mysqld應該做爲一個普通的、非特權用戶來運行。你能夠建立一個單獨的Unix帳號,名爲mysql,該帳號專門用於管理MySQL,從而使一切更加安全。要以不一樣的Unix用戶啓動mysqld,請在my.cnf選項文件的組[mysqld]中添加一個用戶選項,例如:


不管你是手動仍是使用mysqld_safe或mysql.server啓動服務器,該選項都將使得服務器以指定的用戶身份啓動。


以Unix用戶而非root用戶運行mysqld,並不意味着你須要修改mysql.user表中的root帳號。MySQL帳號的用戶名與Unix帳號的用戶名沒有任何關係。


● 不要將「file」權限授予非管理用戶。具備此權限的任何用戶均可以以mysqld進程的有效用戶ID的身份在文件系統的任何位置寫入文件。這包括服務器的數據目錄,其中含有實現權限表的文件。爲了使「file」權限操做更安全,select ... into outfile生成的文件不會覆蓋現有的文件。


「file」權限也能夠用來讀取全局可讀或者是MySQL服務器(mysqld進程的有效用戶ID)具備讀權限的全部文件。使用此權限,你能夠將任何文件讀入數據庫表。這可能會被濫用,例如,使用「load data」將/etc/passwd加載到表中,而後使用select語句進行顯示。


爲了限制能夠讀取和寫入文件的位置,請將系統變量「secure_file_priv」設置爲特定的目錄。


● 不要將「process」或「super」權限授予非管理用戶。「mysqladmin processlist」和「show processlist」的輸出顯示了當前正在執行的全部語句的文本,因此可以查看服務器進程列表的任何用戶均可以看到其餘用戶發起的語句,如update user set password=password('not_secure');


mysqld爲擁有「super」權限的用戶保留了一個額外的鏈接,這樣即便全部正常的鏈接都在使用中,MySQL的root帳號也能夠登陸並檢查服務器的活動。


「super」權限能夠終止客戶端鏈接,經過更改系統變量的值來改變服務器的運行,以及控制服務器的複製。


● 不容許使用到庫表的符號連接。若是你以root身份運行mysqld,這一點尤其重要,由於這會致使任何對服務器數據目錄具備寫權限的人均可以刪除系統中的任何文件。可使用--skip-symbolic-links選項禁用此功能。


● 存儲過程和視圖應該參照第20.6節 "存儲過程和視圖的訪問控制"給出的安全準則來編寫。


● 若是你不信任你的DNS,則應在權限表中使用IP地址而不是主機名。不管如何,在使用含有通配符的主機名建立權限表條目時,你都應該很是當心。


● 若是要限制單個帳號的鏈接數量,能夠經過設置系統變量「max_user_connections」來實現。grant語句還支持資源控制選項,以限制帳號容許使用的服務器資源。


● 若是插件目錄對於服務器來講是可寫的,那麼用戶可使用select ... into dumpfile將可執行代碼寫入目錄中的文件。爲了防止這種狀況,你能夠設置「plugin_dir」目錄,使其僅對服務器開放讀權限,或者將「--secure-file-priv」設置爲能夠安全地進行select寫入的目錄。



4. 以普通用戶運行MySQL

在Linux上,對於使用MySQL倉庫、RPM軟件包或Debian軟件包執行的安裝,MySQL服務器mysqld應以本地用戶「mysql」的身份啓動。以其餘操做系統用戶的身份啓動服務器不受init腳本的支持,其做爲安裝的一部分被包含在系統中


在Unix上(或使用tar或tar.gz包執行安裝的Linux上),MySQL服務器能夠由任何用戶啓動和運行。可是出於安全考慮,你應該避免以Unix root身份運行服務器。要將mysqld更改成以普通的非特權用戶user_name的身份運行,你必須執行如下操做:

① 若是服務器正在運行,請使用「mysqladmin shutdown」中止服務器。

② 更改數據庫目錄和文件,以便user_name對其具備讀權限和寫權限(你可能須要使用Unix root用戶身份執行此操做):


若是不執行此操做,服務器將沒法訪問數據庫或表,當它以user_name運行時。

若是MySQL數據目錄中的目錄或文件是符號連接,則chown -R可能不會跟隨符號連接。這時,你須要查看這些連接,手動更改它們指向的目錄和文件。

③ 以user_name用戶身份啓動服務器。另外一種方法是以Unix root用戶運行mysqld,同時使用「--user=user_name」選項。這樣的話,mysqld首先啓動,而後在接受任何鏈接以前切換爲user_name用戶。

④ 要在系統啓動時自動以特定的用戶運行MySQL服務器,請在/etc/my.cnf選項文件或數據目錄中的my.cnf選項文件的[mysqld]組中添加一個用戶選項,例如:



若是你的Unix機器自己不安全,你應該爲權限表中的「root」帳號分配密碼。不然,在該機器上具備登陸帳號的任何用戶均可以使用--user=root選項運行mysql客戶端並執行任何操做。不管何時,你都應該主動爲MySQL帳號分配密碼,特別是當主機上存在其餘Unix帳號時,更應如此。


5. load data local的安全問題

「load data」語句能夠加載位於服務器主機上的文件;若是指定了「local」關鍵字,則能夠加載位於客戶主機上的文件。


「local」版的「load data」有兩個潛在的安全問題:

● 從客戶主機到服務器主機的文件傳輸由MySQL服務器發起。理論上,服務器能夠命令客戶端程序傳輸服務器指定的任何文件,而不是由客戶端程序在「load data」語句中指定的文件。因此,攻擊者可使用一個虛假的服務器冒充MySQL,或者直接在MySQL服務器外部添加補丁,以達到訪問客戶主機文件的目的。這樣的服務器能夠訪問客戶主機上客戶端用戶有權訪問的任何文件。更糟糕的是,除了「load data local」,實際上服務器能夠對任何的語句響應一個文件傳輸請求,因此一個更根本的問題是客戶端不該該鏈接到不受信任的服務器。


● 在Web環境中,客戶端程序是從Web服務器進行鏈接的。用戶可使用「load data local」讀取Web服務器有權訪問的任何文件(假設用戶能夠對SQL Server運行任何語句)。在這種環境中,相對於MySQL服務器,客戶端是Web服務器,而不是鏈接到Web服務器的用戶運行的遠程程序。


爲了不出現「load data」問題,客戶端應避免使用「local」。爲避免鏈接到不受信任的服務器,客戶端可使用「--ssl-verify-server-cert」選項和相應的CA證書來創建安全的鏈接並驗證服務器身份。


爲了使管理員和應用程序可以管理本地數據加載功能,「local」配置以下:

● 在服務器端:

■「local_infile」系統變量控制服務器端的「local」功能。根據「local_infile」設置的不一樣,服務器拒絕或容許由已開啓「local」的客戶端發起的本地數據加載。「local_infile」默認開啓。

■ 要顯式地使服務器拒絕或容許「load data local」語句(無論客戶端程序和庫在構建時或運行時如何配置),請在啓動mysqld的同時禁用或啓用「local_infile」。「local_infile」也能夠在運行時設置。

● 在客戶端:

■「enabled_local_infile」CMake選項控制MySQL客戶端庫「local」功能的靜態編譯。沒有顯式指定的客戶端,根據MySQL構建時指定的「enabled_local_infile」設置,禁用或啓用「local」功能。

MySQL二進制發佈版中的客戶端庫在編譯時默認啓用了「enabled_local_infile」。若是從源碼編譯MySQL,則根據沒有顯式指定的客戶端是否應該禁用或啓用「local」功能,將「enabled_local_infile」配置爲禁用或啓用。

■ 使用C API的客戶端程序能夠經過調用mysql_options()禁用或啓用「mysql_opt_local_infile」選項,顯式地控制數據加載。

■ 對於mysql客戶端,默認禁用本地數據加載。要顯式地禁用或啓用它,請使用「--local-infile=0」或「--local-infile[=1]」選項。

■ 對於mysqlimport客戶端,默認禁用本地數據加載。要顯式地禁用或啓用它,請使用「--local=0」或「--local=[1]」選項。

■ 若是你是在Perl腳本或讀取選項文件[client]組的其它程序中使用「load data local」,則能夠向該組添加「local-infile」選項。爲了防止不理解該選項的程序出現問題,請使用「loose-」前綴:



在全部狀況中,客戶端成功使用「ocal」加載操做也要求服務器容許它。


若是服務器或客戶端禁用了「local」功能,那麼嘗試發起「load data local」語句的客戶端將收到如下錯誤消息:



6. 客戶端編程安全指南

訪問MySQL的應用程序不該該信任用戶輸入的任何數據,他們能夠經過在Web表單,URL或你構建的任何應用程序中輸入特殊的或轉義的字符序列來嘗試欺騙你的代碼。確保你的應用程序在用戶輸入相似「; drop database mysql;」的語句時仍然是安全的。這是一個極端的例子,但若是你不爲此作準備,黑客使用相似技術將引起極大的安全漏洞和數據丟失。


一個常見的錯誤是隻保護字符串數據值。記得要檢查數值型數據。若是應用程序在用戶輸入「234」時生成諸如「select * from table where id = 234」的查詢,則用戶能夠輸入「234 or 1 = 1」,使應用程序生成查詢「select * from table where id = 234 or 1 = 1」。結果,服務器檢索表中的每一行。這會暴露全部數據行,並致使服務器負載過大。防止此類攻擊的最簡單的方法是在數值常量周圍使用單引號:select * from table where id = '234'。若是用戶輸入額外的信息,它們都將稱爲字符串的一部分。在數值上下文中,MySQL會自動將此字符串轉換爲數字,並自動剝離末尾的非數字字符,相似於「atoi」或「atof」函數。


有時人們認爲,若是一個數據庫只包含公開可用的數據,那麼它就不須要被保護。這是不正確的。即便容許顯示數據庫中的任何數據行,你仍然應該防止拒絕服務攻擊(例如,那些基於上一段中的技術致使服務器資源浪費的攻擊)。不然,你的服務器就沒法對合法用戶做出響應。


檢查清單:

● 啓用嚴格的SQL模式,以告知服務器對其接受的數據值進行更嚴格的限制。參見第5.1.8節 "服務器SQL模式"。

● 嘗試在全部Web表單中輸入單引號和雙引號。若是你遇到任何類型的MySQL錯誤,請當即調查問題。

● 嘗試經過向其中添加%22("),%23(#)和%27(')來修改動態URL。

● 嘗試使用前面示例中所示的字符,將動態URL中的數據類型從數值類型修改成字符類型。你的應用程序應該對這些和相似的攻擊是安全的。

● 嘗試在數值字段中輸入字符、空格和特殊符號,而不是數字。你的應用程序應該在將字段值傳遞給MySQL以前刪除這些非法字符,或者直接生成一條錯誤提示。將未經檢查的值傳遞給MySQL是很是危險的!

● 在將數據傳遞給MySQL以前檢查數據的大小。

● 應用程序鏈接數據庫的帳號不該是管理帳號。不要給你的應用程序任何不須要的訪問權限。

許多應用程序編程接口提供了轉義數據值中特殊字符的方法。若是使用得當,這將防止應用程序用戶輸入致使應用程序生成與你打算的效果不一樣的語句的值:

● MySQL C API:使用mysql_real_escape_string() API調用。

● MySQL++:對查詢流使用escape和quote修飾符。

其它編程接口可能具備相似的功能。

相關文章
相關標籤/搜索