MySQL中文参考手册
译者:晏子
第一章,
前一章, 下一章,
最后一章,目录.
MySQL有一个先进但非标准的安全/权限系统。本节描述它的工作原理。
MySQL权限系统的主要功能是证实连接到一台给定主机的一个用户,并且赋予该用户在一个数据库上select、
insert、update和delete的权限。
附加的功能包括有一个匿名的用户和对于MySQL特定的功能例如LOAD
DATA INFILE 进行授权及管理操作的能力。
由MySQL使用用户名和口令的方法与Unix或Windows使用的方式有很多不同之处:
- MySQL使用于认证目的的用户名,与Unix用户名(登录名字)或Windows用户名无关。缺省地,大多数MySQL客户尝试使用当前Unix用户名作为MySQL用户名登录,但是这仅仅为了方便。客户程序允许用
-u 或--user 选项指定一个不同的名字,这意味着无论如何你不能使得一个数据库更安全,除非所有的MySQL用户名都有口令。任何人可以试图用任何名字连接服务器,而且如果他们指定了没有口令的任何名字,他们将成功。
- MySQL用户名最长可以是16各字符;典型地,Unix用户名限制为8个字符。
- MySQL口令与Unix口令没关系。在你使用登录到一台Unix机器口令和你使用在那台机器上存取一个数据库的口令之间没有必要有关联。
- MySQL加密口令使用了一个Unix登录期间所用的不同算法,见7.4.12
杂项函数一节中描述
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 。
- 缺省用户名是你的Unix登录名。
- 如果没有
-p ,则没有提供口令。
这样, 对一个Unix用户joe ,下列命令是等价的:
shell>mysql -h localhost -u joe
shell>mysql -h localhost
shell>mysql -u joe
shell>mysql
其它MySQL客户程序有同样表现。
在Unix系统上,当你进行一个连接时,你可以指定要使用的不同的缺省值,这样你不必每次在你调用一个客户程序是在命令行上输入他们。这可以有很多方法做到:
如果连接参数以多种方法被指定,在命令行上被指定的值优先于在配置文件和环境变量中指定的值,而在配置文件指定的值优先于在环境变量指定的值。
以一种暴露的可被其他用户发现的方式指定你的口令是不妥当的。当你运行客户程序时,你可以使用下列方法指定你的口令,还有每个方法的风险评估:
- 使用一个在命令行上
-pyour_pass 或--password=your_pass 的选项。这很方便但是不安全,因为你的口令对系统状态程序(例如ps )变得可见,它可以被其他的用户调用来显示命令行。(一般MySQL客户在他们的初始化顺序期间用零覆盖命令行参数,但是仍然有一个短暂间隔时间内参数值可见的。)
- 使用一
个-p 或--password 选项(没有指定your_pass 值)。在这种情况下,客户程序请求来自终端的口令:
shell>mysql - u user_name - p
Enter password: ********
客户回应“*”字符到作为输入你的口令的终端使得旁观者不能看见它。因为它对其他用户不可见,与在命令行上指定它相比,这样进入你的口令更安全。然而,这个输入一个口令的方法仅仅为你交互式运行程序是合适的。如果你想要从非交互式运行的一个脚本调用一个客户,就没有从终端输入入口令的机会。
- 在一个配置文件中存储你的口令。例如,你可你的主目录的“.my.cnf”文件中的
[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权限来显示或杀死其他用户启动的线程。
总的说来,只授予权限给需要他们的那些用户是一个好主意,但是你应该在授予某个权限时试验特定的警告:
- grant权限允许用户放弃他们的权限给其他用户。2个有不同的权限并有grant权限的用户可以合并权限。
- alter权限可以用于通过重新命名表来推翻权限系统。
- file权限可以被滥用在服务器上读取任何世界可读(world-readable,即任何人可读)的文件到一张数据库表,然后其内容能用
SELECT 被存取。
- shutdown权限通过终止服务器可以被滥用完全拒绝为其他用户服务,
。
- precess权限能被用来察看当前执行的查询的普通文本,包括设定或改变口令查询。
- 在
mysql 数据库上的权限能被用来改变口令和其他存取权限信息。(口令被加密存储,所以一个恶意的用户不能简单地读取他们。然而,有足够的权限,同一个用户能用不同的一个代替一个口令。)
有一些事情你不能用MySQL权限系统做到:
- 你不能明显地指定一个给定用户应该被拒绝存取。即,你不能明显地匹配一个用户并且然后拒绝连接。
- 你不能指定一个用户有权创建立或抛弃一个数据库中的表,也不能创建或抛弃数据库本身。
MySQL权限系统保证所有的用户可以严格地做他们假定被允许做的事情。当你连接一个MySQL服务器时,
你的身份由你从那连接的主机和你指定的用户名来决定,系统根据你的身份和你想做什么来授予权限。
MySQL在认定身份中考虑你的主机名和用户名字,是因为有很小的原因假定一个给定的用户在因特网上属于同一个人。例如,用户从whitehouse.gov 连接的bill 不必和从mosoft.com 连接bill 是同一个人。
MySQL通过允许你区分在不同的主机上碰巧有同样名字用户来处理它:你可以对从whitehouse.gov 连接授与bill 一个权限集,而为从microsoft.com 的连接授予一个不同的权限集。
MySQL存取控制包含2个阶段:
- 阶段1:服务器检查你是否允许连接。
- 阶段2:假定你能连接,服务器检查你发出的每个请求。看你是否有足够的权限实施它。例如,如果你从数据库中一个表精选(select)行或从数据库抛弃一个表,服务器确定你对表有select权限或对数据库有drop权限。
服务器在存取控制的两个阶段使用在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 服务器,然后初始化授权表,包含下列权限集合:
- MySQL
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 用户名和口令。
当你试着联接MySQL服务器时,如果你碰到Access
denied 错误,显示在下面的表指出一些你能用来更正这个问题的动作:
- 你是在安装MySQL以后运行
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”文件为你的主机增加一个条目。
- 如果你从一个3.22.11以前的版本更新一个现存的MySQL安装到3.22.11版或以后版本,你运行了
mysql_fix_privilege_tables 脚本吗?如果没有,运行它。在GRANT 语句变得能工作时,授权表的结构用MySQL
3.22.11修改 。
- 如果你直接对授权表做修改(使用
INSERT 或UPDATE 语句)并且你的改变似乎被忽略,记住,你必须发出一个FLUSH
PRIVILEGES 语句或执行一个mysqladmin flush-privileges 命令导致服务器再次读入表,否则你的改变要道下一次服务器被重启时再生效。记住在你设定root 口令以后,你将不需要指定它,直到在你清洗(flush)权限以后,因为服务器仍然不会知道你改变了口令!
- 如果你的权限似乎在一个会话(session)当中改变了,可能是一个超级用户改变了他们。再次装入授权表作用于新客户连接,但是它也影响现存的连接,如6.9
权限改变何时生效小节所述。
- 为了测试,用
--skip-grant-tables 选项启动mysqld 守护进程,然后你可以改变MySQL授权表并且使用mysqlaccess 脚本检查你的修改是否有如期的效果。当你对你的改变满意时,执行mysqladmin
flush-privileges 告诉mysqld 服务器开始使用新的权限表。注意:再次装入授权表覆盖了--skip-grant-tables 选项。这允许你告诉服务器开始使用授权表,而不用停掉并重启它。
- 如果你有一个Perl、Python或ODBC程序的存取问题,试着用
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 表中有一个条目,正确地匹配在错误消息给出的主机名和用户名。
- 如果当你试着从一个不是MySQL服务器正在运行的主机上连接时,你得到下列错误,那么在
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权限。
- 记住,客户程序将使用在配置文件或环境变量被指定了的连接参数。如果当你不在命令行上指定他们时,一个客户似乎正在发送错误的缺省连接参数,检查你的环境和在你的主目录下的“.my.cnf”文件。你也可以检查系统范围的MySQL配置文件,尽管更不可能将在那里指定那个客户的连接参数。见4.15.4
选项文件。如果当你没有任何选项运行一个客户时,你得到
Access denied ,确认你没在任何选项文件里指定一个旧的口令!见4.15.4
选项文件。
- 如果任何其它事情失败,用调试选项(例如,
--debug=d,general,query )启动mysqld 守护进程。这将打印有关尝试连接的主机和用户信息,和发出的每个命令的信息。见G.1
调试一个MySQL服务器。
- 如果你有任何与MySQL授权表的其它问题,而且觉得你必须邮寄这个问题到邮寄表,总是提供一个MySQL授权表的倾倒副本(dump)。你可用
mysqldump
mysql 命令倾倒数据库表。象平时一样,用mysqlbug 脚本邮寄你的问题。在一些情况下你可能用--skip-grant-tables 重启mysqld 以便能运行mysqldump 。
当你连接一个MySQL服务器时,你通常应该使用一个口令。口令不以明文在连接上传输。
所有其它信息作为能被任何人读懂的文本被传输。如果你担心这个,你可使用压缩协议(MySQL3.22和以上版本)使事情变得更难。甚至为了使一切更安全,你应该安装ssh (见http://www.cs.hut.fi/ssh)。用它,你能在一个MySQL服务器与一个MySQL客户之间得到一个加密的TCP/IP连接。
为了使一个MySQL系统安全,强烈要求你考虑下列建议:
下列mysqld 选项影响安全:
--secure
- 由
gethostbyname() 系统调用返回的IP数字被检查,确保他们解析回到原来的主机名。这对某些外人通过模仿其它主机获得存取权限变得更难。这个选项也增加一些聪明的主机名检查。在MySQL3.21里,选择缺省是关掉的,因为它有时它花很长时间执行反向解析。MySQL
3.22缓存主机名并缺省地启用了这个选项。
--skip-grant-tables
- 这个选项导致服务器根本不使用权限系统。这给每个人以完全存取所有的数据库的权力!(通过执行
mysqladmin
reload ,你能告诉一个正在运行的服务器再次开始使用授权表。)
--skip-name-resolve
- 主机名不被解析。所有在授权表的
Host 的列值必须是IP数字或localhost 。
--skip-networking
- 在网络上不允许TCP/IP连接。所有到
mysqld 的连接必须经由Unix套接字进行。这个选项对使用MIT-pthreads的系统是不合适的,因为MIT-pthreads包不支持Unix套接字。
第一章,
前一章, 下一章,
最后一章,目录.
|