譯者:晏子 (clyan@sohu.com)
GB 碼主頁:http://linuxdb.yeah.net
Big5 轉碼者:statue
(statue@bbs.yzu.edu.tw)
詞彙轉換:彭武興 (wilson@mailbox.com.tw)
Big5 碼主頁:
http://cnpa.yzu.edu.tw/~cfc/docs/mysqldoc_big5/manual_toc.html
Big5 碼分站:
http://php.wilson.gs/mysqldoc/big5/manual_toc.html
MySQL有一個先進但非標準的安全/權限系統。本節描述它的工作原理。
MySQL權限系統的主要功能是証實連接到一台給定主機的一個用戶,並且賦予該用戶在一個資料庫上select、 insert、update和delete的權限。
附加的功能包括有一個匿名的用戶和對於MySQL特定的功能例如LOAD
DATA INFILE
進行授權及管理操作的能力。
由MySQL使用用戶名和密碼的方法與Unix或Windows使用的方式有很多不同之處:
-u
或--user
選項指定一個不同的名字,這意味著無論如何你不能使得一個資料庫更安全,除非所有的MySQL用戶名都有密碼。任何人可以試圖用任何名字連接伺服器,而且如果他們指定了沒有密碼的任何名字,他們將成功。
PASSWORD()
和ENCRYPT()
函數部分。當你想要存取一個MySQL伺服器時,MySQL客戶程式一般要求你指定連接參數:你想要聯接的主機、你的用戶名和你的密碼。例如,mysql
客戶可以像這樣啟動(可選的參數被包括在“[”和“]”之間):
shell> mysql [-h host_name][-u user_name][-pyour_pass ]
-h
, -u
和-p
選項的另一種形式是--host=host_name
、--user=user_name
和--password=your_pass
。注意在-p
或--password=
與跟隨它後面的密碼之間沒有空格。
注意:在命令行上指定一個密碼是不安全的!隨後在你系統上的任何用戶可以通過打類似這樣的命令發現你的密碼:ps
auxww
。見4.15.4 選項文件。
對於命令行沒有的聯接參數,mysql
使用內定值:
localhost
。-p
,則沒有提供密碼。 這樣, 對一個Unix用戶joe
,下列命令是等價的:
shell>mysql -h localhost -u joe shell>mysql -h localhost shell>mysql -u joe shell>mysql
其它MySQL客戶程式有同樣表現。
在Unix系統上,當你進行一個連接時,你可以指定要使用的不同的內定值,這樣你不必每次在你調用一個客戶程式是在命令行上輸入他們。這可以有很多方法做到:
[client]
小節裡指定連接參數。文件的相關小節看上去可能像這樣:
[client] host=host_name user=user_name password=your_pass
MYSQL_HOST
指定,MySQL用戶名字可用USER
指定(僅對
Windows),密碼可用MYSQL_PWD
指定(但是這不安全,見下一節)
。 如果連接參數以多種方法被指定,在命令行上被指定的值優先於在配置文件和環境變數中指定的值,而在配置文件指定的值優先於在環境變數指定的值。
以一種暴露的可被其他用戶發現的方式指定你的密碼是不妥當的。當你運行客戶程式時,你可以使用下列方法指定你的密碼,還有每個方法的風險評估:
-pyour_pass
或--password=your_pass
的選項。這很方便但是不安全,因為你的密碼對系統狀態程式(例如ps
)變得可見,它可以被其他的用戶調用來顯示命令行。(一般MySQL客戶在他們的初始化順序期間用零覆蓋命令行參數,但是仍然有一個短暫間隔時間內參數值可見的。)個-p
或--password
選項(沒有指定your_pass
值)。在這種情況下,客戶程式請求來自終端的密碼:
shell>mysql - u user_name - p Enter password: ********
客戶回應“*”字符到作為輸入你的密碼的終端使得旁觀者不能看見它。因為它對其他用戶不可見,與在命令行上指定它相比,這樣進入你的密碼更安全。然而,這個輸入一個密碼的方法僅僅為你交互式運行程式是合適的。如果你想要從非交互式運行的一個腳本調用一個客戶,就沒有從終端輸入入密碼的機會。
[client]
節列出你的密碼:
[client] password=your_pass
如果你在“.my.cnf”裡面儲存密碼,文件應該不是組或世界可讀或可寫的。保証文件的存取模式是400
或600
。見4.15.4 選項文件。
MYSQL_PWD
環境變數中儲存密碼,但是這個方法必須想到是極不安全的且應該不使用。ps
的某些版本包括顯示運行進程的環境的選項﹔如果你設定MYSQL_PWD
,你的密碼將對所有人是顯而易見的,甚至在沒有這樣一個版本的ps
系統上,假設沒有其他方法觀察到進程環境是不明智的。
總之,最安全的方法是讓客戶程式提示密碼或在一個適當保護的“.my.cnf”文件中指定密碼。
權限資訊用user
、db
、host
、tables_priv
和columns_priv
表被儲存在mysql
資料庫中(即在名為mysql
的資料庫中)。在MySQL啟動時和在6.9 權限修改何時生效所說的情況時,伺服器讀入這些資料庫表內容。
本手冊所用的涉及由MySQL提供的權限名稱顯示在下表,還有在授權表中每個權限的表列名稱和每個權限有關的上下文:
權限 | 列 | 上下文 |
select | Select_priv |
表 |
insert | Insert_priv |
表 |
update | Update_priv |
表 |
delete | Delete_priv |
表 |
index | Index_priv |
表 |
alter | Alter_priv |
表 |
create | Create_priv |
資料庫、表或索引 |
drop | Drop_priv |
資料庫或表 |
grant | Grant_priv |
資料庫或表 |
references | References_priv |
資料庫或表 |
reload | Reload_priv |
伺服器管理 |
shutdown | Shutdown_priv |
伺服器管理 |
process | Process_priv |
伺服器管理 |
file | File_priv |
在伺服器上的文件存取 |
select、insert、update和delete權限允許你在一個資料庫現有的表上實施操作。
SELECT
語句只有在他們真正從一個表中檢索行是才需要select權限,你可以執行某個SELECT
語句,甚至沒有任何到伺服器上的資料庫裡的存取任何東西的許可。例如,你可使用mysql
客戶作為一個簡單的計算器:
mysql> SELECT 1+1; mysql> SELECT PI()*2;
index權限允許你創建或拋棄(刪除)索引。
alter權限允許你使用ALTER TABLE
。
create和drop權限允許你創建新的資料庫和表,或拋棄(刪除)現存的資料庫和表。
注意:如果你將mysql
資料庫的drop權限授予一個用戶,該用戶能拋棄儲存了MySQL存取權限的資料庫!
grant權限允許你把你自己擁有的那些權限授給其他的用戶。
file權限給予你用LOAD DATA INFILE
和SELECT ...
INTO OUTFILE
語句讀和寫伺服器上的文件,任何被授予這個權限的用戶都能讀或寫MySQL伺服器能讀或寫的任何文件。
其餘的權限用於管理性操作,它使用mysqladmin
程式實施。下表顯示mysqladmin
支配每個管理性權限允許你執行的命令:
優惠 | 權限擁有者允許執行的命令 |
reload | reload , refresh , flush-privileges , flush-hosts ,
flush-logs , flush-tables |
shutdown | shutdown |
precess | processlist , kill |
reload
命令告訴伺服器再讀入授權表,refresh
命令清洗所有表並打開和關閉記錄文件,flush-privileges
是reload
的一個同義詞,其它flush-*
命令執行類似refresh
的功能,但是範圍更有限,並且在某些情況下可能更好用。例如,如果你只是想清洗記錄文件,flush-logs
比refresh
是更好的選擇。
shutdown
命令關掉伺服器。
processlist
命令顯示在伺服器內執行的執行緒的資訊。kill
命令殺死伺服器執行緒。你總是能顯示或殺死你自己的執行緒,但是你需要process權限來顯示或殺死其他用戶啟動的執行緒。
總的說來,只授予權限給需要他們的那些用戶是一個好主意,但是你應該在授予某個權限時試驗特定的警告:
SELECT
被存取。
mysql
資料庫上的權限能被用來改變密碼和其他存取權限資訊。(密碼被加密儲存,所以一個惡意的用戶不能簡單地讀取他們。然而,有足夠的權限,同一個用戶能用不同的一個代替一個密碼。)有一些事情你不能用MySQL權限系統做到:
MySQL權限系統保証所有的用戶可以嚴格地做他們假定被允許做的事情。當你連接一個MySQL伺服器時, 你的身份由你從那連接的主機和你指定的用戶名來決定,系統根據你的身份和你想做什麼來授予權限。
MySQL在認定身份中考慮你的主機名和用戶名字,是因為有很小的原因假定一個給定的用戶在網際網路上屬於同一個人。例如,用戶從whitehouse.gov
連接的bill
不必和從mosoft.com
連接bill
是同一個人。
MySQL通過允許你區分在不同的主機上碰巧有同樣名字用戶來處理它:你可以對從whitehouse.gov
連接授與bill
一個權限集,而為從microsoft.com
的連接授予一個不同的權限集。
MySQL存取控制包含2個階段:
伺服器在存取控制的兩個階段使用在mysql
的資料庫中的user
、db
和host
表,在這些授權表中字段如下:
表名稱 | user |
db |
host |
範圍字段 | Host |
Host |
Host |
User |
Db |
Db |
|
Password |
User |
||
權限字段 | Select_priv |
Select_priv |
Select_priv |
Insert_priv |
Insert_priv |
Insert_priv |
|
Update_priv |
Update_priv |
Update_priv |
|
Delete_priv |
Delete_priv |
Delete_priv |
|
Index_priv |
Index_priv |
Index_priv |
|
Alter_priv |
Alter_priv |
Alter_priv |
|
Create_priv |
Create_priv |
Create_priv |
|
Drop_priv |
Drop_priv |
Drop_priv |
|
Grant_priv |
Grant_priv |
Grant_priv |
|
Reload_priv |
|||
Shutdown_priv |
|||
Process_priv |
|||
File_priv |
對存取控制的第二階段(請求証實),如果請求涉及表,伺服器可以另外參考tables_priv
和columns_priv
表。這些表的字段如下:
表名稱 | tables_priv |
columns_priv |
範圍字段 | Host |
Host |
Db |
Db |
|
User |
User |
|
Table_name |
Table_name |
|
Column_name |
||
權限字段 | Table_priv |
Column_priv |
Column_priv |
||
其他字段 | Timestamp |
Timestamp |
Grantor |
每個授權表包含範圍字段和權限字段。
範圍字段決定表中每個條目的範圍,即,條目適用的上下文。例如,
一個user
表條目的Host
和User
值為'thomas.loc.gov'
和'bob'
將被用於証實來自主機thomas.loc.gov
的bob
對伺服器的連接。同樣,一個db
表條目的Host
、User
和Db
字段的值是'thomas.loc.gov'
、'bob'
和'reports'
將用在bob
從主機聯接thomas.loc.gov
存取reports
資料庫的時候。
tables_priv
和columns_priv
表包含範圍字段,指出每個條目適用的表或表/列的組合。
對於檢查存取的用途,比較Host
值是忽略大小寫的。User
、Passwor
d、Db
和Table_name
值是區分大小寫的。Column_name
值在MySQL3.22.12或以後版本是忽略大小寫的。
權限字段指出由一個表條目授予的權限,即,可實施什麼操作。伺服器組合各種的授權表的資訊形成一個用戶權限的完整描述。為此使用的規則在6.8 存取控制, 階段2:請求証實描述。
範圍字段是字符串,如下所述﹔每個字段的內定值是空字符串:
字段名 | 類型 | |
Host |
CHAR(60) |
|
User |
CHAR(16) |
|
Password |
CHAR(16) |
|
Db |
CHAR(64) |
(tables_priv 和columns_priv 表為CHAR(60) ) |
在user
、db
和host
表中,所有權限字段被聲明為ENUM('N','Y')
--每一個都可有值'N'
或'Y'
,並且內定值是'N'
.
在tables_priv
和columns_priv
表中,權限字段被聲明為SET
字段:
表名 | 字段名 | 可能的集合成員 |
tables_priv |
Table_priv |
'Select', 'Insert', 'Update', 'Delete', 'Create', 'Drop', 'Grant', 'References',
'Index', 'Alter' |
tables_priv |
Column_priv |
'Select', 'Insert', 'Update', 'References' |
columns_priv |
Column_priv |
'Select', 'Insert', 'Update', 'References' |
簡單地說,伺服器使用這樣的授權表:
user
表範圍字段決定是否允許或拒絕到來的連接。對於允許的連接,權限字段指出用戶的全局(超級用戶)權限。
db
和host
表一起使用: db
表範圍字段決定用戶能從哪個主機存取哪個資料庫。權限字段決定允許哪個操作。
db
條目應用於若干主機時,host
表作為db
表的擴展被使用。例如,如果你想要一個用戶能在你的網路從若干主機使用一個資料庫,在用戶的db
表的Host
條目設為空值,然後將那些主機的每一個移入host
表。這個機制詳細描述在6.8 存取控制, 階段2:請求証實。
tables_priv
和columns_priv
表類似於db
表,但是更精致:他們在表和列級應用而非在資料庫級。
注意管理權限(reload, shutdown, 等等)僅在user
表中被指定。這是因為管理性操作是伺服器本身的操作並且不是特定資料庫,因此沒有理由在其他授權表中列出這樣的權限。事實上,只需要請教user
表來決定你是否執行一個管理操作。
file權限也僅在user
表中指定。它不是管理性權限,但你讀或謝在伺服器主機上的文件的的能力獨立於你正在存取的資料庫。
當mysqld
伺服器啟動時,讀取一次授權表內容。對授權表的更改生效在6.9 權限更改何時生效描述。
當你修改授權表的內容時,確保你按你想要的方式更改權限設置是一個好主意。為幫助診斷問題,見6.13 “存取拒絕引起”
錯誤的原因。對於安全問題上的忠告,見6.14 怎麼對使MySQL安全對抗解密高手。
一個有用的診斷工具是mysqlaccess
腳本,由Carlier Yves
提供給MySQL分發。使用--help
選項調用mysqlaccess
查明它怎樣工作。注意:mysqlaccess
僅用user
、db
和host
表僅檢查存取。它不檢查表或列級權限。
當你試圖聯接一個MySQL伺服器時,伺服器基於你的身份和你是否能通過供應正確的密碼驗証身份來接受或拒絕連接。如果不是,伺服器完全具結你的存取,否則,伺服器接受連接,然後進入階段2並且等待請求。
你的身份基於2個資訊:
身份檢查使用3個user
表(Host
, User
和Password
)範圍字段執行。伺服器只有在一個user
表條目匹配你的主機名和用戶名並且你提供了正確的密碼時才接受連接。
在user
表範圍字段可以如下被指定:
Host
值可以是主機名或一個IP數字,或'localhost'
指出本地主機。
Host
字段裡使用通配符字符“%”和“_”。
Host
值'%'
匹配任何主機名,一個空白Host
值等價於'%'
。注意這些值匹配能創建一個連接到你的伺服器的任何主機!
User
字段中不允許,但是你能指定空白的值,它匹配任何名字。如果user
表匹配到來的連接的條目有一個空白的用戶名,用戶被認為是匿名用戶(沒有名字的用戶),而非客戶實際指定的名字。這意味著一個空白的用戶名被用於在連接期間的進一步的存取檢查(即,在階段2期間)。
Password
字段可以是空白的。這不意味著匹配任何密碼,它意味著用戶必須不指定一個密碼進行連接。
非空白Password
值代表加密的密碼。 MySQL不以任何人可以看的純文本格式儲存密碼,相反,正在試圖聯接的一個用戶提供的密碼被加密(使用PASSWORD()
函數),並且與儲存了user
表中的已經加密的版本比較。如果他們匹配,密碼是正確的。
下面的例子顯示出各種user
表中Host
和User
條目的值的組合如何應用於到來的連接:
Host 值 |
User 值 |
被條目匹配的連接 |
'thomas.loc.gov' |
'fred' |
fred , 從thomas.loc.gov 連接 |
'thomas.loc.gov' |
'' |
任何用戶, 從thomas.loc.gov 連接 |
'%' |
'fred' |
fred , 從任何主機連接 |
'%' |
'' |
任何用戶, 從任何主機連接 |
'%.loc.gov' |
'fred' |
fred , 從在loc.gov 域的任何主機連接 |
'x.y.%' |
'fred' |
fred , 從x.y.net 、x.y.com ,x.y.edu 等聯接。(這或許無用) |
'144.155.166.177' |
'fred' |
fred , 從有144.155.166.177 IP 地址的主機連接 |
'144.155.166.%' |
'fred' |
fred , 從144.155.166 C類子網的任何主機連接 |
既然你能在Host
字段使用IP通配符值(例如,'144.155.166.%'
匹配在一個子網上的每台主機),有可能某人可能企圖探究這種能力,通過命名一台主機為144.155.166.somewhere.com
。為了阻止這樣的企圖,MySQL不允許匹配以數字和一個點起始的主機名,這樣,如果你用一個命名為類似1.2.foo.com
的主機,它的名字決不會匹配授權表中Host
列。只有一個IP數字能匹配IP通配符值。
一個到來的連接可以被在user
表中的超過一個條目匹配。例如,一個由fred
從thomas.loc.gov
的連接匹配多個條目如上所述。如果超過一個匹配,伺服器怎麼選擇使用哪個條目呢?伺服器在啟動時讀入user
表後通過排序來解決這個問題,然後當一個用戶試圖連接時,以排序的順序瀏覽條目,第一個匹配的條目被使用。
user
表排序工作如下,假定user
表看起來像這樣:
+-----------+----------+- | Host | User | ... +-----------+----------+- | % | root | ... | % | jeffrey | ... | localhost | root | ... | localhost | | ... +-----------+----------+-
當伺服器在表中讀取時,它以最特定的Host
值為先的次序排列('%'
在Host
列裡意味著“任何主機”並且是最不特定的)。有相同Host
值的條目以最特定的User
值為先的次序排列(一個空白User
值意味著“任何用戶”並且是最不特定的)。最終排序的user
表看起來像這樣:
+-----------+----------+- | Host | User | ... +-----------+----------+- | localhost | root | ... | localhost | | ... | % | jeffrey | ... | % | root | ... +-----------+----------+-
當一個連接被嘗試時,伺服器瀏覽排序的條目並使用找到的第一個匹配。對於由jeffrey
從localhost
的一個連接,在Host
列的'localhost'
條目首先匹配。那些有空白用戶名的條目匹配連接的主機名和用戶名。('%'/'jeffrey'
條目也將匹配,但是它不是在表中的第一匹配。)
這是另外一個例子。假定user
桌子看起來像這樣:
+----------------+----------+- | Host | User | ... +----------------+----------+- | % | jeffrey | ... | thomas.loc.gov | | ... +----------------+----------+-
排序後的表看起來像這樣:
+----------------+----------+- | Host | User | ... +----------------+----------+- | thomas.loc.gov | | ... | % | jeffrey | ... +----------------+----------+-
一個由jeffrey
從thomas.loc.gov
的連接被第一個條目匹配,而一個由jeffrey
從whitehouse.gov
的連接被第二個匹配。
普遍的誤解是認為,對一個給定的用戶名,當伺服器試圖對連接尋找匹配時,明確命名那個用戶的所有條目將首先被使用。這明顯不是事實。先前的例子說明了這點,在那裡一個由jeffrey
從thomas.loc.gov
的連接沒被包含'jeffrey'
作為User
字段值的條目匹配,但是由沒有用戶名的題目匹配!
如果你有伺服器連接的問題,列印出user
表並且手工排序它看看第一個匹配在哪兒進行。
一旦你建立了一個連接,伺服器進入階段2。對在此連接上進來的每個請求,伺服器檢查你是否有足夠的權限來執行它,它基於你希望執行的操作類型。這正是在授權表中的權限字段發揮作用的地方。這些權限可以來子user
、db
、host
、tables_priv
或columns_priv
表的任何一個。授權表用GRANT
和REVOKE
命令操作。見7.26 GRANT
和REVOKE
句法。(你可以發覺參考6.6 權限系統怎樣工作很有幫助,它列出了在每個權限表中呈現的字段。)
user
表在一個全局基礎上授予賦予你的權限,該權限不管當前的資料庫是什麼均適用。例如,如果user
表授予你delete權限,
你可以刪除在伺服器主機上從任何資料庫刪除行!換句話說,user
表權限是超級用戶權限。只把user
表的權限授予超級用戶如伺服器或資料庫主管是明智的。對其他用戶,你應該把在user
表中的權限設成'N'
並且僅在一個特定資料庫的基礎上授權,
使用db
和host
表。
db
和host
表授予資料庫特定的權限。在範圍字段的值可以如下被指定:
Host
和Db
字段。
db
表的'%'Host
值意味著“任何主機”,在db
表中一個空白Host
值意味著“對進一步的資訊咨詢host
表”。host
表的一個'%'
或空白Host
值意味著“任何主機”。'%'
或空白Db
值意味著“任何資料庫”。User
值匹配匿名用戶。 db
和host
表在伺服器啟動時被讀取和排序(同時它讀user
表)。db
表在Host
、Db
和User
範圍字段上排序,並且host
表在Host
和Db
範圍字段上排序。對於user
表,排序首先放置最特定的值然後最後最不特定的值,並且當伺服器尋找匹配入條目時,它使用它找到的第一個匹配。
tables_priv
和columns_priv
表授予表和列特定的權限。在範圍字段的值可以如下被指定:
Host
字段。
'%'
或空白Host
意味著“任何主機”。Db
、Table_name
和Column_name
字段不能包含通配符或空白。
tables_priv
和columns_priv
表在Host
、Db
和User
字段上被排序。這類似於db
表的排序,盡管因為只有Host
字段可以包含通配符,但排序更簡單。
請求証實進程在下面描述。(如果你熟悉存取檢查的原始程式,你會注意到這裡的描述與在代碼使用的算法略有不同。描述等價於代碼實際做的東西﹔它只是不同於使解釋更簡單。)
對管理請求(shutdown、reload等等),伺服器僅檢查user
表條目,因為那是唯一指定管理權限的表。如果條目許可請求的操作,存取被授權了,否則拒絕。例如,如果你想要執行mysqladmin
shutdown
,但是你的user
表條目沒有為你授予shutdown權限,存取甚至不用檢查db
或host
表就被拒絕。(因為他們不包含Shutdown_priv
行列,沒有這樣做的必要。)
對資料庫有關的請求(insert、update等等),伺服器首先通過查找user
表條目來檢查用戶的全局(超級用戶)權限。如果條目允許請求的操作,存取被授權。如果在user
表刈釪局權限不夠,伺服器通過檢查db
和host
表確定特定的用戶資料庫權限:
db
表的Host
、Db
和User
字段上查找一個匹配。
Host
和User
對應連接用戶的主機名和MySQL用戶名。Db
字段對應用戶想要存取的資料庫。如果沒有Host
和User
的條目,存取被拒絕。
db
表中的條目有一個匹配而且它的Host
字段不是空白的,該條目定義用戶的資料庫特定的權限。
db
表的條目的Host
字段是空白的,它表示host
表列舉主機應該被允許存取資料庫的主機。在這種情況下,在host
表中作進一步查找以發現Host
和Db
字段上的匹配。如果沒有host
表條目匹配,存取被拒絕。如果有匹配,用戶資料庫特定的權限以在db
和host
表的條目的權限,即在兩個條目都是'Y'
的權限的交集(而不是並集!)計算。(這樣你可以授予在db
表條目中的一般權限,然後用host
表條目按一個主機一個主機為基礎地有選擇地限制它們。)在確定了由db
和host
表條目授予的資料庫特定的權限後,伺服器把他們加到由user
表授予的全局權限中。如果結果允許請求的操作,存取被授權。否則,伺服器檢查在tables_priv
和columns_priv
表中的用戶的表和列權限並把它們加到用戶權限中。基於此結果允許或拒絕存取。
用布爾術語表示,前面關於一個用戶權限如何計算的描述可以這樣總結:
global privileges OR (database privileges AND host privileges) OR table privileges OR column privileges
它可能不明顯,為什麼呢,如果全局user
條目的權限最初發現對請求的操作不夠,伺服器以後把這些權限加到資料庫、表和列的特定權限。原因是一個請求可能要求超過一種類型的權限。例如,如果你執行一個INSERT
... SELECT
語句,你就都要insert和select權限。你的權限必須如此以便user
表條目授予一個權限而db
表條目授予另一個。在這種情況下,你有必要的權限執行請求,但是伺服器不能自己把兩個表區別開來﹔兩個條目授予的權限必須組合起來。
host
表能被用來維護一個“安全”伺服器列表。在TcX,host
表包含一個在本地的網路上所有的機器的表,這些被授予所有的權限。
你也可以使用host
表指定不安全的主機。假定你有一台機器public.your.domain
,它位於你不認為是安全的一個公共區域,你可以用下列的host
表條目子允許除了那台機器外的網路上所有主機的存取:
+--------------------+----+- | Host | Db | ... +--------------------+----+- | public.your.domain | % | ... (所有權限設為 'N') | %.your.domain | % | ... (所有權限設為 'Y') +--------------------+----+-
當然,你應該總是測試你在授權表中的條目(例如,使用mysqlaccess
)讓你確保你的存取權限實際上以你認為的方式被設置。
當mysqld
啟動時,所有的授權表內容被讀進儲存器並且從那點生效。
用GRANT
、REVOKE或SET PASSWORD
對授權表施行的修改會立即被伺服器注意到。
如果你手工地修改授權表(使用INSERT
、UPDATE等等),你應該執行一個FLUSH
PRIVILEGES
語句或運行mysqladmin flush-privileges
告訴伺服器再裝載授權表,否則你的改變將不生效,除非你重啟伺服器。
當伺服器注意到授權表被改變了時,現存的客戶連接有如下影響:
USE db_name
命令生效。 全局權限的改變和密碼改變在下一次客戶連接時生效。
在安裝MySQL後,你通過運行scripts/mysql_install_db
安裝初始的存取權限。見4.7.1 快速安裝概述。 scripts/mysql_install_db
腳本啟動mysqld
伺服器,然後初始化授權表,包含下列權限集合:
root
用戶作為可做任何事情的一個超級用戶被創造。連接必須由本地主機發出。注意:出世的root
密碼是空的,因此任何人能以root
而沒有一個密碼進行連接並且被授予所有權限。
一個'test'
或以'test_'
開始的名字的資料庫做任何時期事情,連接必須由本地主機發出。這意味著任何本地用戶能連接並且視為匿名用戶。
mysqladmin shutdown
或mysqladmin
processlist
。 注意:對Win32的初始權限是不同的。見4.12.4 在Win32上運行MySQL。
既然你的安裝初始時廣開大門,你首先應該做的事情之一是為MySQL
root
用戶指定一個密碼。你可以做如下(注意,你使用PASSWORD()
函數指定密碼):
shell> mysql -u root mysql mysql> UPDATE user SET Password=PASSWORD('new_password') WHERE user='root'; mysql> FLUSH PRIVILEGES;
在MySQL 3.22和以上版本中,你可以使用SET PASSWORD
語句:
shell> mysql -u root mysql mysql> SET PASSWORD FOR root=PASSWORD('new_password');
設置密碼的另一種方法是使用mysqladmin
命令:
shell> mysqladmin -u root password new_password
注意:如果你使用第一種方法在user
表裡直接更新密碼,你必須告訴伺服器再次讀入授權表(用FLUSH
PRIVILEGES
),因為否則改變將不被注意到。
一旦root
密碼被設置,此後當你作為root
與伺服器連接時,你必須供應那個密碼。
你可能希望讓root
密碼為空白以便當你施行附加的安裝時,你不需要指定它或測試,但是保証在任何真實的生產工作中使用你的安裝之前,設置它。
看看scripts/mysql_install_db
腳本,看它如何安裝內定的權限。你可用它作為一個研究如何增加其他用戶的基礎。
如果你想要初始的權限不同於上面描述的那些,在你運行mysql_install_db
之前,你可以修改它。
為了完全重建權限表,刪除在包含mysql
資料庫的目錄下所有“*.frm”,“*.MYI”和“*.MYD”文件。(這是在資料庫目錄下面命名為“mysql”的目錄,當你運行mysqld
--help
時,它被列出。)然後運行mysql_install_db
腳本,可能在首先編輯它擁有你想要的權限之後。
注意:對於比MySQL 3.22.10舊的版本,你不應該刪除“*.frm”文件。如果你偶然做了,你應該在運行mysql_install_db
之前你的MySQL分發中拷回它們。
你可以有2個不同的方法增加用戶:通過使用GRANT
語句或通過直接操作MySQL授權表。比較好的方法是使用GRANT
語句,因為他們是更簡明並且好像錯誤少些。
下面的例子顯示出如何使用mysql
客戶安裝新用戶。這些例子假定權限根據以前的章節描述的內定被安裝。這意味著為了改變,你必須在mysqld
正在運行同一台機器上,你必須作為MySQL
root
用戶連接,並且root
用戶必須對mysql
資料庫有insert權限和reload管理權限。另外,如果你改變了root
用戶密碼,你必須如下的mysql
命令指定它。
你可以通過發出GRANT
語句增加新用戶:
shell> mysql --user=root mysql mysql> GRANT ALL PRIVILEGES ON *.* TO monty@localhost IDENTIFIED BY 'something' WITH GRANT OPTION; mysql> GRANT ALL PRIVILEGES ON *.* TO monty@"%" IDENTIFIED BY 'something' WITH GRANT OPTION; mysql> GRANT RELOAD,PROCESS ON *.* TO admin@localhost; mysql> GRANT USAGE ON *.* TO dummy@localhost;
這些GRANT
語句安裝3個新用戶:
monty
'something'
做這個。注意,我們必須對monty@localhost
和monty@"%"
發出GRANT
語句。如果我們增加localhost
條目,對localhost
的匿名用戶條目在我們從本地主機連接接時由mysql_install_db
創建的條目將優先考慮,因為它有更特定的Host
字段值,所以以user
表排列順序看更早到來。admin
localhost
沒有一個密碼進行連接並且被授予reload和process管理權限的用戶。這允許用戶執行mysqladmin
reload
、mysqladmin refresh
和mysqladmin flush-*
命令,還有mysqladmin
processlist
。沒有授予資料庫有關的權限。他們能在以後通過發出另一個GRANT
語句授權。
dummy
'N'
--USAGE
權限類型允許你無需權限就可設置一個用戶。它假定你將在以後授予資料庫相關的權限。
你也可以直接通過發出INSERT
語句增加同樣的用戶存取資訊,然後告訴伺服器再次裝入授權表:
shell> mysql --user=root mysql mysql> INSERT INTO user VALUES('localhost','monty',PASSWORD('something'), 'Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y') mysql> INSERT INTO user VALUES('%','monty',PASSWORD('something'), 'Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y') mysql> INSERT INTO user SET Host='localhost',User='admin', Reload_priv='Y', Process_priv='Y'; mysql> INSERT INTO user (Host,User,Password) VALUES('localhost','dummy',''); mysql> FLUSH PRIVILEGES;
取決於你的MySQL版本,對上述,你可能必須使用一個不同數目'Y'
值(在3.22.11以前的版本有更少的權限列)。對admin
用戶,只用在3.22.11開始的版本具有的更加可讀的INSERT
擴充的語法。
注意,為了設置一個超級用戶,你只需創造一個user
表條目,其權限字段設為'Y'
。不需要db
或host
表的條目。
在user
表中的權限列不是由最後一個INSERT
語句明確設置的(對dummy
用戶),因此那些列被賦予內定值'N'
。這是GRANT
USAGE
做的同樣的事情。
下列例子增加一個用戶custom
,他能從主機localhost
、server.domain
和whitehouse.gov
連接。他只想要從localhost
存取bankaccount
資料庫,從whitehouse.gov
存取expenses
資料庫和從所有3台主機存取customer
資料庫。他想要從所有3台主機上使用密碼stupid
。
為了使用GRANT
語句設置個用戶的權限,運行這些命令:
shell> mysql --user=root mysql mysql> GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP ON bankaccount.* TO custom@localhost IDENTIFIED BY 'stupid'; mysql> GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP ON expenses.* TO custom@whitehouse.gov IDENTIFIED BY 'stupid'; mysql> GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP ON customer.* TO custom@'%' IDENTIFIED BY 'stupid';
通過直接修改授權表設置用戶權限,運行這些命令(注意,在結束時FLUSH
PRIVILEGES
):
shell> mysql --user=root mysql mysql> INSERT INTO user (Host,User,Password) VALUES('localhost','custom',PASSWORD('stupid')); mysql> INSERT INTO user (Host,User,Password) VALUES('server.domain','custom',PASSWORD('stupid')); mysql> INSERT INTO user (Host,User,Password) VALUES('whitehouse.gov','custom',PASSWORD('stupid')); mysql> INSERT INTO db (Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_priv, Create_priv,Drop_priv) VALUES ('localhost','bankaccount','custom','Y','Y','Y','Y','Y','Y'); mysql> INSERT INTO db (Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_priv, Create_priv,Drop_priv) VALUES ('whitehouse.gov','expenses','custom','Y','Y','Y','Y','Y','Y'); mysql> INSERT INTO db (Host,Db,User,Select_priv,Insert_priv,Update_priv,Delete_priv, Create_priv,Drop_priv) VALUES('%','customer','custom','Y','Y','Y','Y','Y','Y'); mysql> FLUSH PRIVILEGES;
頭3個INSERT
語句增加user
表條目,允許用戶custom
用給定密碼從不同的主機進行連接,但是沒有授予任何許可(所有權限被設置為內定值'N'
)。後3個INSERT
語句增加db
表條目,授予custom
以bankaccount
、expenses
和customer
資料庫權限,但是只能在從正確的主機存取時。通常,在授權表直接被修改時,伺服器必須被告知再次裝入他們(用FLUSH
PRIVILEGES
)以便使權限修改生效。
如果你想要給特定的用戶從一個給定的域上的任何機器上存取權限,你可以發出一個如下的GRANT
語句:
mysql> GRANT ... ON *.* TO myusername@"%.mydomainname.com" IDENTIFIED BY 'mypassword';
為了通過直接修改授權表做同樣的事情,這樣做:
mysql> INSERT INTO user VALUES ('%.mydomainname.com', 'myusername', PASSWORD('mypassword'),...); mysql> FLUSH PRIVILEGES;
你也可以使用xmysqladmin
、mysql_webadmin
甚至xmysql
在授權表中插入、改變和更新值。你可以在MySQL的Contrib目錄找到這些實用程式。
在前面小節的例子裡說明了一個重要的原則:當你使用INSERT
或UPDATE
語句儲存一個非空的密碼時,你必須使用PASSWORD()
函數加密它。這是因為在user
表刈脛加密形式儲存密碼,而不是作為純文本。如果你忘記這個事實,你可能像這樣試圖設置密碼:
shell> mysql -u root mysql mysql> INSERT INTO user (Host,User,Password) VALUES('%','jeffrey','biscuit'); mysql> FLUSH PRIVILEGES
結果是純文本值'biscuit'
作為密碼被儲存在user
表中。在用戶jeffrey
試圖用這個密碼連接伺服器時,mysql
客戶用PASSWORD()
加密它並且將結果送給伺服器,伺服器比較在user
表中的值(它是純文本值'biscuit'
)和加密的密碼(而不是
'biscuit'
),比較失敗並且伺服器拒絕連接:
shell> mysql -u jeffrey -pbiscuit test Access denied
因為當他們被插入user
表時,密碼必須被加密,相反,INSERT
語句應該像這樣被指定:
mysql> INSERT INTO user (Host,User,Password) VALUES('%','jeffrey',PASSWORD('biscuit'));
當你使用SET PASSWORD
語句時,你也必須使用PASSWORD()
函數:
mysql> SET PASSWORD FOR jeffrey@"%" = PASSWORD('biscuit');
如果你使用GRANT ... IDENTIFIED BY
語句或mysqladmin password
命令設置密碼,PASSWORD()
函數是不必要的。他們都考慮到為你加密密碼,多以你可像這樣指定一個密碼'biscuit'
:
mysql> GRANT USAGE ON *.* TO jeffrey@"%" IDENTIFIED BY 'biscuit';
或
shell> mysqladmin -u jeffrey password biscuit
注意: PASSWORD()
不是以在Unix密碼加密的同樣方法施行密碼加密。你不應該假定如果你的Unix密碼和你的MySQL密碼是一樣的,PASSWORD()
將導致與在Unix密碼文件被儲存的同樣的加密值。見6.2 MySQL 用戶名和密碼。
Access denied
錯誤的原因當你試著聯接MySQL伺服器時,如果你碰到Access
denied
錯誤,顯示在下面的表指出一些你能用來更正這個問題的動作:
mysql_install_db
的腳本,來設置初始授權表內容嗎?如果不是,這樣做。見6.10 設置初始MySQL權限。通過執行這個命令測試初始權限:
shell> mysql -u root test
伺服器應該讓你無誤地連接。你也應該保証你在MySQL資料庫目錄有一個文件“user.MYD”。通常,它是“PATH/var/mysql/user.MYD”,在此PATH
是MySQL安裝根目錄的路徑。
shell> mysql -u root mysql
伺服器應該讓你連接,因為MySQL root
用戶初始時沒有密碼。既然那也是一個安全風險,當你正在設置其他MySQL用戶時,設定root
密碼是一件重要的事請。如果你作為root
嘗試連接並且得到這個錯誤:
Access denied for user: '@unknown' to database mysql
這意味著,你沒有一個條目在user
表中的一個User
列值為'root'
並且mysqld
不能為你的客庫解析主機名。在這種情況下,你必須用--skip-grant-tables
選項重啟伺服器並且編輯你的“/etc/hosts”或“\windows\hosts”文件為你的主機增加一個條目。
mysql_fix_privilege_tables
腳本嗎?如果沒有,運行它。在GRANT
語句變得能工作時,授權表的結構用MySQL
3.22.11修改 。 INSERT
或UPDATE
語句)並且你的改變似乎被忽略,記住,你必須發出一個FLUSH
PRIVILEGES
語句或執行一個mysqladmin flush-privileges
命令導致伺服器再次讀入表,否則你的改變要道下一次伺服器被重啟時再生效。記住在你設定root
密碼以後,你將不需要指定它,直到在你清洗(flush)權限以後,因為伺服器仍然不會知道你改變了密碼!
--skip-grant-tables
選項啟動mysqld
背景執行程式,然後你可以改變MySQL授權表並且使用mysqlaccess
腳本檢查你的修改是否有如期的效果。當你對你的改變滿意時,執行mysqladmin
flush-privileges
告訴mysqld
伺服器開始使用新的權限表。注意:再次裝入授權表覆蓋了--skip-grant-tables
選項。這允許你告訴伺服器開始使用授權表,而不用停掉並重啟它。
mysql -u
user_name db_name
或mysql -u user_name -pyour_pass db_name
與伺服器連接。如果你能用mysql
客戶連接,這是你程式的一個問題而不是存取權限的問題。(注意在-p
和密碼之間沒有空格﹔你也能使用--password=your_pass
句法指定密碼。)INSERT
, UPDATE
或SET
PASSWORD
語句設置密碼,你必須使用PASSWORD()
函數。如果你用GRANT
... INDENTIFIED BY
語句或mysqladmin password
命令指定密碼,PASSWORD()
函數是不需要的。見6.12 怎樣設置密碼。 localhost
是你本地主機名的一個同義詞,並且也是如果你不明確地指定主機而客戶嘗試連接的內定主機。然而,如果你正在運行於一個使用MIT-pthreads的系統上,連接localhost
是不行的(localhost
連接使用Unix套接字進行,它沒被
MIT-pthreads支援),為了在這樣的系統上避免這個問題,你應該使用--host
選項明確地命名伺服器主機,這將做一個
TCP/IP連接到mysqld
伺服器。在這種情況下,你必須有在伺服器主機上的user
表中條目的你真實的主機名。(即使你在伺服器同一台的主機上運行一個客戶程式,這也是真的。)mysql -u user_name db_name
與資料庫連接時,如果你得到一個Access
denied
錯誤,你可能有與user
桌有關的問題,通過執行mysql
-u root mysql
並且發出下面的SQL語句檢查: mysql> SELECT * FROM user;
結果應該包含一個有Host
和User
列的條目匹配你的計算機主機名和你的MySQL用戶名。
Access denied
錯誤消息將告訴你,你正在用哪個用戶嘗試登錄,你正在試圖用連接哪個主機,並且你是否正在使用一個密碼。通常,你應該在user
表中有一個條目,正確地匹配在錯誤消息給出的主機名和用戶名。
user
表中沒有匹配那台主機行:
Host ... is not allowed to connect to this MySQL server
你可以通過使用mysql
命令行工具(在伺服器主機上!)修正它,把你正在試圖連接的用戶/主機名組合新加一行到user
表中。如果你不在運行MySQL
3.22並且你不知道你正在從它連接的機器的IP數字或主機名,你應該把一個'%'
條目作為Host
列值放在user
表中並且在伺服器機器上使用--log
選項重啟mysqld
。在試圖從客戶機器連接以後,在MySQL記錄文件中的資訊將顯示你如何真正進行連接。(然後用在記錄文件上面顯示出的實際的主機名代替user
表中的'%'
條目。否則,你將有一個不安全的系統。)
mysql -u root test
工作但是mysql -h your_hostname -u root
test
導致Access denied
,那麼在user
表中你可能沒有你的主機的正確名字。這裡的一個普遍的問題是在user
表條目中的Host
值指定一個唯一的主機名,但是你系統的名字解析例程返回一個完全正規的域名(或相反)。例如,如果你在user
表中有一個主機是'tcx'
的條目,但是你的
DNS告訴MySQL你的主機名是'tcx.subnet.se'
,條目將不工作。嘗試把一個條目加到user
表中,它包含你主機的IP數字作為Host
列的值。(另外,你可以把一個條目加到user
表中,它有包含一個通配符如'tcx.%'
的Host
值。然而,使用以“%”結尾的主機名是不安全的並且不推薦!)mysql -u user_name test
工作但是mysql -u user_name
other_db_name
不工作,對other_db_name
,你在db
表中沒有沒有一個條目列出。
mysql -u user_name db_name
時,它工作,但是在其它客戶機器上執行mysql
-h host_name -u user_name db_name
時,它卻不工作,你沒有把客戶機器列在user
表或db
表中。
Access denied
,從user
表中刪除所有Host
包含通配符值的條目(包含“%”或“_”的條目)。一個很普遍的錯誤是插入用Host
='%'
和User
='some
user'
插入一個新條目,認為這將允許你指定localhost
從同一台機器進行連接。它不工作的原因是內定權限包括一個有Host
='localhost'
和User
=''
的條目,因為那個條目一個比'%'
更具體的Host
值'localhost'
,當從localhost
連接時,它用於指向新條目!正確的步驟是插入Host
='localhost'
和User
='some_user'
的第2個條目,或刪除Host
='localhost'
和User
=''
條目。
db
或host
表有關的問題:
Access to database denied
如果從db
表中選擇了在Host
列有空值的條目,保証在host
表中有一個或多個相應的條目,指定運用db
表中的哪些主機。如果在使用SQL命令SELECT
... INTO OUTFILE
或LOAD DATA INFILE
時,你得到錯誤,在user
表中的你的條目可能啟用file權限。
Access
denied
,確認你沒在任何選項文件裡指定一個舊的密碼!見4.15.4 選項文件。 --debug=d,general,query
)啟動mysqld
背景執行程式。這將列印有關嘗試連接的主機和用戶資訊,和發出的每個命令的資訊。見G.1 調試一個MySQL伺服器。 mysqldump
mysql
命令傾倒資料庫表。像平時一樣,用mysqlbug
腳本郵寄你的問題。在一些情況下你可能用--skip-grant-tables
重啟mysqld
以便能運行mysqldump
。
當你連接一個MySQL伺服器時,你通常應該使用一個密碼。密碼不以明文在連接上傳輸。
所有其它資訊作為能被任何人讀懂的文本被傳輸。如果你擔心這個,你可使用壓縮協議(MySQL3.22和以上版本)使事情變得更難。甚至為了使一切更安全,你應該安裝ssh
(見http://www.cs.hut.fi/ssh)。用它,你能在一個MySQL伺服器與一個MySQL客戶之間得到一個加密的TCP/IP連接。
為了使一個MySQL系統安全,強烈要求你考慮下列建議:
other_user
沒有密碼,任何人能簡單地用mysql
-u other_user db_name
作為任何其它的人登錄。對客戶機/伺服器應用程式,客戶可以指定任何用戶名是常見的做法。在你運行它以前,你可以通過編輯mysql_install_db
腳本改變所有用戶的密碼,或僅僅MySQL
root
的密碼,像這樣: shell> mysql -u root mysql mysql> UPDATE user SET Password=PASSWORD('new_password') WHERE user='root'; mysql> FLUSH PRIVILEGES;
root
用戶運行MySQL背景執行程式。mysqld
能以任何用戶運行,你也可以創造一個新的Unix用戶mysql
使一切更安全。如果你作為其它Unix用戶運行mysqld
,你不需要改變在user
表中的root
用戶名,因為MySQL用戶名與Unix
用戶名沒關系。你可以作為其它Unix用戶編輯mysql.server
啟動腳本mysqld
。通常這用su
命令完成。對於更多的細節,見18.8 怎樣作為一個一般用戶運行MySQL。
root
用戶密碼放在mysql.server
腳本中,確保這個腳本只能對root
是可讀的。
mysqld
的Unix用戶是唯一的在資料庫目錄下有讀/寫權限的用戶。
mysqladmin processlist
的輸出顯示出當前執行的查詢正文,如果另外的用戶發出一個UPDATE
user SET password=PASSWORD('not_secure')
查詢,被允許執行那個命令的任何用戶可能看得到。mysqld
為有process權限的用戶保留一個額外的連接,
以便一個MySQL root
用戶能登錄並檢查,即使所有的正常連接在使用。
擁有mysqld
背景執行程式權限的文件系統那裡寫一個文件!為了使這更安全一些,用SELECT
... INTO OUTFILE
產生的所有文件對每個人是可讀的,並且你不能覆蓋已經存在的文件。file權限也可以被用來讀取任何作為運行伺服器的Unix用戶可存取的文件。這可能被濫用,例如,通過使用LOAD
DATA
裝載“/etc/passwd”進一個資料庫表,然後它能用SELECT
被讀入。
--secure
選項對mysqld
應該使主機名更安全。在任何情況下,你應該非常小心地使用包含通配符的主機名!
下列mysqld
選項影響安全:
--secure
gethostbyname()
系統調用返回的IP數字被檢查,確保他們解析回到原來的主機名。這對某些外人通過模仿其它主機獲得存取權限變得更難。這個選項也增加一些聰明的主機名檢查。在MySQL3.21裡,選擇內定是關掉的,因為它有時它花很長時間執行反向解析。MySQL
3.22緩存主機名並內定地啟用了這個選項。 --skip-grant-tables
mysqladmin
reload
,你能告訴一個正在運行的伺服器再次開始使用授權表。) --skip-name-resolve
Host
的列值必須是IP數字或localhost
。
--skip-networking
mysqld
的連接必須經由Unix套接字進行。這個選項對使用MIT-pthreads的系統是不合適的,因為MIT-pthreads包不支援Unix套接字。