MySQL用户权限系统

MySQL 专栏收录该内容
9 篇文章 0 订阅

一 MySQL用户权限系统

两年多前写的或者说翻译的,较为青涩。

MySQL权限系统主要功能是验证从给定地址(host)登录的用户(user),同时将用户与数据库的权限如SELECT、INSERT、UPDATE和DELETE等关联起来。其他功能包括访客功能,授权MySQL 特定功能如LOAD_DATA_INFILE和管理操作。

有以下几点,MySQL权限系统无法完成:

  1. 不可以显示拒绝给定用户的访问。也就说,不可以显示匹配一个用户后,然后拒绝对方连接。

  2. 不可以定义一个用户在一个database中拥有create和drop table的权限,却没有create和drop这个database自身的权限。

说明:grant create, drop on dbname.* to user@host;

上述语句会在mysql.db表中创建对应记录。

user@host用户则拥有了在dbname中create、drop table的权限,同时拥有create、drop该db的权限。使用drop database dbname后,在mysql.db表中依然会有这一项记录,user@host可以再次创建该db。mysql.db中该记录直到drop user或revoke才会删除记录。

  1. 用户的密码是全局的。不可以对特定database、table或routine设置特定密码。

MySQL的权限系统接口包含CREATE USER,GRANT和REVOKE等SQL语句。

服务器将权限信息存储在mysql database的grant tables中。MySQL服务器启动时读取这些表的内容到内存中,然后会基于grant tables在内存中的拷贝做访问控制决策。

MySQL权限系统确保所有用户只可能执行被授予的操作。作为一个用户,在连接MySQL server时候,用户身份通过user name和发起连接的host来标识。当用户发送请求以后,系统根据用户身份和执行操作授予权限。

MySQL同时考虑用户的host和user name用于验证身份,是因为没有理由去假设一个给定用户是属于任意host上的。例如从office.example.com发起连接的用户joe可能与从home.example.com发起连接的joe不是同一个人。MySQL能够识别具有同一name而不同host的用户。用户可以授权(grant)一组权限给从office.example.com连接来的joe,同时给一组不同的权限到从home.example.com连接来的joe。如果需要查看给定用户具有哪些权限,可以使用show grants语句,例如:

SHOW GRANTS FOR ‘joe’@’office.example.com’;

SHOW GRANTS FOR ‘joe’@’home.example.com’;

当用户运行客户端连接server时,MySQL访问控制包含两个阶段:

Stage 1:server基于用户身份和是否能够验证正确的密码来接受或拒绝连接。

Stage 2:连接后,server会检查用户发出的每条语句来决定用户是否有足够的权限来执行。例如:用户试图从一个database的表中select rows或者drop一个表,那么server就会验证用户是否具有表的SELECT权限或database的DROP权限。

说明:用户可以具有只create、drop某张表的权限,这时不需要database的create、drop权限。

grant create, drop on tableName[db.tableName] to username@host;

对应信息会存储在mysql.tables_priv表中。

在用户已经连接MySQL时,如果用户的权限被修改了,这些修改不一定会在用户发出下一条请求时立即生效。详细情况见(TODO)

1.1 MySQL提供的权限。

MySQL提供了适用于不同上下文和不同级别操作的权限:

  1. 管理员权限允许用户管理MySQL服务器。这些权限是全局的,不特定于某个database。

  2. database权限适用于某个database和所有在该database中的对象。这些权限可以被授予到特定database,也可以到全局应用到所有database。

说明:

授予到特定database:grant priv_type on dbName.* to user@host;

授予到全局database:grant priv_type on *.* to user@host;

  1. 对于数据库内对象的权限,这些对象包括tables、indexes、views和stored routines,授权可以针对在一个database中的特定对象,也可以针对在一个database中的某种类型的所有对象,或者全局所有database的某种类型的对象。

用户的权限信息存储在mysql database的表user、db、host、tables_priv、columns_priv和procs_priv中。MySQL server在启动时会读取这些表格的内容到内存,然后会在一些情况下重新加载它们。访问决策就基于这些grant tables在内存中的拷贝。

下面这张表展示了在GRANT和REVOKE语句中的权限名字,和在grant tables中对应列的名字,还有权限适用的context。

说明:MySQL源代码实现中,privilege是用unsigned long(32位版)表示,所以权限最多32个。可以看到下表中权限也只有32个。这种方式便捷之处是权限检查直接用按位与操作(want_access & master_access)。

PrivilegeColumnContext
CREATECreate_privdatabases, tables, or indexes
DROPDrop_privdatabases, tables, or views
GRANT OPTIONGrant_privdatabases, tables, or stored routines
LOCK TABLESLock_tables_privdatabases
REFERENCESReferences_privdatabases or tables
EVENTEvent_privdatabases
ALTERAlter_privtables
DELETEDelete_privtables
INDEXIndex_privtables
INSERTInsert_privtables or columns
SELECTSelect_privtables or columns
UPDATEUpdate_privtables or columns
CREATE TEMPORARY TABLESCreate_tmp_table_privtables
TRIGGERTrigger_privtables
CREATE VIEWCreate_view_privviews
SHOW VIEWShow_view_privviews
ALTER ROUTINEAlter_routine_privstored routines
CREATE ROUTINECreate_routine_privstored routines
EXECUTEExecute_privstored routines
FILEFile_privfile access on server host
CREATE TABLESPACECreate_tablespace_privserver administration
CREATE USERCreate_user_privserver administration
PROCESSProcess_privserver administration
PROXYsee proxies_priv tableserver administration
RELOADReload_privserver administration
REPLICATION CLIENTRepl_client_privserver administration
REPLICATION SLAVERepl_slave_privserver administration
SHOW DATABASESShow_db_privserver administration
SHUTDOWNShutdown_privserver administration
SUPERSuper_privserver administration
ALL [PRIVILEGES]server administration
USAGEserver administration

下面提供MySQL权限的通用描述。

(1)ALL or ALL PRIVILEGES权限:是用于速记的标记符。它代表对于给定权限级别所有权限是可用的(除了GRANT OPTION)。例如,在grant all在全局或table级别就grant了所有全局权限或所有table级别权限。

(2)ALTER权限:允许用户使用ALTER TABLE来改变表的结构。重命名一个table需要ALTER和DROP权限在旧table上的,同时ALTER、CREATE和INSERT在新表上的。

(3)ALTER ROUTINE权限:在alter或drop stored routines(procedures and functions)时需要。注:没有drop routine。

(4)CREATE权限:允许创建database和table。

(5)CREATE ROUTINE权限:创建stored routines(procedures and functions)。

(6)CREATE TABLESPACE权限:create, alter or drop tablespace和log file groups。

(7)CREATE TEMPORARY TABLES权限:create temporary tables使用CREATE TEMPORARY TABLE语句。

这里其他在临时表上的操作如INSERT、UPDATE、SELECT等需要额外的包含临时表database上的对应权限或者拥有相同名字永久表的权限。

如果希望临时表和永久表的权限保持隔离,通常的解决方案是为临时表专门创建一个database。然后对这个database,授予用户CREATE TEMPORARY TABLES权限和其他需要的权限。

(8)CREATE USER权限:允许用户CREATE USER、DROP USER、RENAME USER和REVOKE ALL PRIVILEGES。

(9)CREATE VIEW权限:允许用户CREATE VIEW。

(10)DELETE权限:允许从database的table中删除行。、

(11)DROP权限:允许用户drop存在的databases、tables、views。对于partitioned table,语句ALTER TABLE…DROP PARTITION需要DROP权限。DROP权限同时在TRUNCATE TABLE时也需要。如果你grant DROP权限对于mysql database的到一个用户,那么这个用户可以drop掉MySQL权限控制系统所存储的database。

(12)EVENT权限:create、alter、drop或see events对于EVENT Scheduler。

(13)EXECUTE权限:执行stored routine(procedures and functions)。

(14)FILE权限:允许用户读和写在server端的files使用LOAD DATA INFILE和SELECT … INTO OUTFILE和LOAD_FILE()函数。一个用户有FILE权限可以读取MySQL server端的任意文件,全局可读的和MySQL server可读的。(也就是说用户可以读取任意database目录文件)。FILE权限同时允许用户在MySQL server具有写权限的地方创建新的文件,包括server的data目录。作为安全策略,server不会重写已经存在的文件。

(15)GRANT OPTION权限:user可以给其他用户权限或者撤销其他用户该user自己拥有的权限。

(16)INDEX权限:允许用户创建或删除index。INDEX适用于已经存在的tables。如果用户拥有一张表的create权限,那么用户可以在create table语句中定义index。

(17)INSERT权限:允许在database的tables中插入行。对于ANANLYZE TABLE、OPTIMIZE TABLE和REPAIR TABLE这些table维护语句,需要INSERT权限。

(18)LOCK TABLE权限:允许显式使用LOCK TABLES语句锁住用户具有SELECT权限的表格。这包括使用写锁,阻止其他的sessions读取锁住的table。

(19)PROCESS权限:关于展示在server上线程执行的信息。这个权限允许用户SHOW PROCESSLIST或者用mysqladmin processlist来观察属于其他用户的线程。用户总是可以看自己的线程。PROCESS权限同时允许了使用SHOW ENGINE。

(20)PROXY权限:允许用户冒充或者被理解成另一个用户。详细看Proxy Users。

(21)REFERENCES权限当前未使用。

说明:在Oracle中有REFERENCES权限,用来引用其他表做外键。

(22)RELOAD权限:允许用户使用FLUSH语句。同时允许等价于FLUSH操作的mysqladmin命令,如flush-hosts、flush-logs、flush-privileges、flush-status、flush-tables、flush-threads、refresh和reload。

reload命令告诉server重新加载grant tables到内存中。flush-privileges与reload意思一致。refresh命令关闭并重新打开log files同时刷新所有表。其他flush-xxx命令与表现于refresh类型,但是更具体,对于一些场景更适用。例如:如果你只想刷新所有log files,那么flush-logs比refresh更合适。

(23) REPLICATION CLIENT权限:允许用户使用SHOW MASTER STATUS和SHOW SLAVE STATUS。在MySQL5.5.25以后,同时允许用户SHOW BINARY LOGS语句。

(24)REPLICATION SLAVE权限:slave server连接当前server作为它们的master的账号时,应该赋予该权限。没有这个权限,slave无法请求master server上databases的更新。

(25)SELECT权限:允许用户从database的table中select rows。select语句需要SELECT权限只有这些语句真正的从表中检索数据。一些SELECT语句不访问tables,那么可以执行不需要任何database的允许。例如使用SELECT语句作为简单计算,不引用任何table。

SELECT 1+1;

说明:MySQL对于select 1+1 from mysql.tables_priv;虽然没有访问tables_priv但能也会检查tables_priv的权限信息。

SELECT权限也会在其他需要读取列值的语句中需要。例如在UPDATE语句中col_name=expr,SELECT权限是需要的。DELETE与UPDATE语句中WHERE访问到的列也需要SELECT权限。

(26)SHOW DATABASE权限:允许用户查看database的名字通过SHOW DATABASE语句。对于没有这项权限的用户,只能看到他们就有一些权限的database,同时在server启动带有—skip-show-database选项时,完全不能使用该语句。NOTE:任何一个全局权限都会是database的一个权限。

(27)SHOW VIEW权限:允许用户SHOW CREATE VIEW。

(28)SHUTDOWN权限:允许用户使用mysqladmin shutdown命令。这个没有对应的SQL命令。

(29)SUPER权限允许账户使用CHANGE MASTER TO、KILL或者mysqladmin kill来kill属于某一账户下的线程。(用户总是可以kill自己的线程),PURGE BINARY LOGS,SET GLOBAL修改全局系统变量,mysqladmin debug命令,使用或禁用logging,执行updates即使系统变量read only设置了,starting和stopping在slave server上的备份,规范任意账户stored programs和view的定义属性,允许用户依然连接(一次)尽管连接数达到了max_connections的设置。

create和alter stroed functions如果binanry logging生效,用户也需要SUPER权限。

(30)TRIGGER权限:允许trigger操作。用户create、drop或者execute triggers对某张表时,需要有这张表的TRIGGER权限。

(31)UPDATE权限:允许database中tables的rows被更新。

(32)USAGE权限:代表NO PRIVILEGES。它被用来在全局层使用GRANT来修改账户属性,例如资源限制或SSL的特点,同时不影响应经存在的账户权限。

应该只给用户需要的权限。要特别慎重在给FILE权限和管理员权限的时候。

  1. FILE权限可能被滥用读取database table的任意文件。这包括了全局刻度文件盒server的data目录。

  2. GRANT OPTION权限可以使用户将权限给其他用户。两个具有不同权限的用户通过GRANT OPTION权限可以整合权限。

  3. ALTER权限可能改变权限控制通过改变table的名字。(说明:这一点是因为MySQL是使用table name作为自己的主键。)

  4. SHUTDOWN权限可能会被滥用通过关闭server来拒绝服务。

  5. PROCESS权限可以用来查看当前执行语句的plain text,包括设置或改变密码的语句。

  6. SUPER权限可能用来关闭其他session或者改变server如何操作。

  7. 给mysql database的权限可能被用来改变密码和其他访问权限信息。密码是加密存储,所以恶意用户无法直接读取他们来知道plain text的密码。但是,一个用户具有user表Password写权限可以改变用户的密码,然后使用该账户登录。

1.2 权限系统的授权表(GRANT TABLES)

通常用户操作mysql database下的grant tables是间接的通过GRANT和REVOKE语句来设置账户和控制各账户权限。这里讨论grant tables的结构和server如何用这些内容来于clients交互。

mysql database包含一下grant information:

user: 包含用户账号,全局权限和其他非权限信息的列。

db:包含database级别的权限信息。

host:废弃了。

tables_priv: 包含table级别的权限。

columns_priv:包含column级别的权限。

procs_priv:包含procedure和function的权限信息。

proxies_priv:包含proxy-user的权限信息。

其他在mysql database中不包含权限信息的表格有:

event:包含event scheduler信息。

func:包含用户定义的函数。

help_xx:用于server端的help。

plugin:包含server plugins的信息。

proc:包含stored procedures and functions信息。

servers:用于共享存储引擎中。

time_zone_xxx:包含了time zone的信息。

_log 带有该标识的表:用于logging。

对mysql database中表的修改通常是server作为语句CREATE SUER、GRANT和CREATE PROCEDURE的响应。直接使用语句INSERT、UPDATE和DELETE修改这些表格是不鼓励的,同时server是可以忽略由上述操作的结果行的。

每个grant table包含了范围列和权限列。

scope column决定了在这些table中每一行row(entry)的作用范围,即该行会用在哪个context。例如, user表中host、user值分别为’thomas.loc.gov’ 和’bob’的可以用来验证从’thomas.loc.gov’上客户端连接来的’bob’。类似,db中的一行host、USER、DB分别为’thomas.loc.gov’ 、’bob’和’reports’会在该用户访问’reports’时使用。tables_priv和columns_priv表包含了适用于访问tables或table/columns的信息。procs_priv存储了stored routine对应信息。

权限列指明了有哪些权限被授予,也就是哪些操作是可以的。server会整合grant tables这些信息来形成一个完整的用户权限描述。

server使用grant tables通过以下方式:

(1)user table用来觉得是接受还是拒绝连接。如果接受连接,user表中任意权限都是全局权限,可以用在该server所有database上。

NOTE:由于任意全局权限都可以看出对所有database的权限。具有任意全局权限就可以使用户使用show databases或者查schemata table在information_schema database中。

(2)db table:用来决定从某host登录的user可以访问哪些database。权限列决定哪些操作可以执行。一个在数据库层级的权限适用于该database和所有该database下的对象,例如所有tables和存储的programs。

(3)host 是用于当用户希望db table中的某一行适用于若干个host时候用。例如希望一个用户可以在若干个host上访问某个数据库,那么就可以在db中使host为空,然后用需要的hosts填充host表。

NOTE:host表必须用INSERT、UPDATE、DELETE语句做。不可以通过GRANT和REVOKE间接使用。大多数MySQL完全不需要这张表。

(4)tables_priv和columns_priv表与db table类似,但粒度更细,适用于table级别和column级别。在table级别被授予的权限适用于这张表和所有列。一个在列上授予的权限仅在指定列上有效。

(5)procs_priv表使用于stored routines。权限在routine级别适用于指定的一个routine。

(6)proxies_priv表用来表示哪些users可以作为proxies和proxy user是否可以grant proxy权限给其他users。

Server使用mysql database中的user、db和host在权限验证的两个阶段都用。user、table表的列如下所示。

Table user and db Table Columns

Table Nameuserdb
Scope columnsHostHost
UserDb
PasswordUser
Privilege columnsSelect_privSelect_priv
Insert_privInsert_priv
Update_privUpdate_priv
Delete_privDelete_priv
Index_privIndex_priv
Alter_privAlter_priv
Create_privCreate_priv
Drop_privDrop_priv
Grant_privGrant_priv
Create_view_privCreate_view_priv
Show_view_privShow_view_priv
Create_routine_privCreate_routine_priv
Alter_routine_privAlter_routine_priv
Execute_privExecute_priv
Trigger_privTrigger_priv
Event_privEvent_priv
Create_tmp_table_privCreate_tmp_table_priv
Lock_tables_privLock_tables_priv
References_privReferences_priv
Reload_priv
Shutdown_priv
Process_priv
File_priv
Show_db_priv
Super_priv
Repl_slave_priv
Repl_client_priv
Create_user_priv
Create_tablespace_priv
Security columnsssl_type
ssl_cipher
x509_issuer
x509_subject
plugin
authentication_string
Resource control columnsmax_questions
max_updates
max_connections
max_user_connections

mysql.user表plugin、authentication_string列可以存储验证插件信息。

如果插件列对于一个用户空的,那么server验证用户用mysql_native_password或mysql_old_password。

在访问控制的第二阶段,server执行请求验证来保证每个客户端拥有其发出请求的足够权限,除了user、db、host还有tables_priv和columns_priv表的信息。

Table tables_priv and columns_priv Table Columns

Table Nametables_privcolumns_priv
Scope columnsHostHost
DbDb
UserUser
Table_nameTable_name
Column_name
Privilege columnsTable_privColumn_priv
Column_priv
Other columnsTimestampTimestamp
Grantor

列Timestamp和Grantor是用当前时间和当前用户值设置的。但是,他们并没有被用到。

说明:这里Grantor没有用到,drop user grantor后并不会影响其授予的权限。

涉及到stored routines的需要参考procs_priv表格,其拥有的列如下:

Table procs_priv Table Columns

Table Nameprocs_priv
Scope columnsHost
Db
User
Routine_name
Routine_type
Privilege columnsProc_priv
Other columnsTimestamp
Grantor

Routine_type列是一个enum变量值有’FUNCION’或’PROCEDURE’来表示routine的类型。这一列可以使权限被分别授予给相同名字的function和procedure。

Timestamp和Grantor还没有使用。

proxied_priv table在MySQL5.5.7中加入,记录了proxy users信息。它包含下面这些列:

host、user:这两列标识具有PROXY权限的用户账号。

proxied_host、proxied_user:这些列表示proxied user的账户

grantor、timestamp:当前未使用。

with grant:表示proxy 账户是否具有grant PROXY权限给其他账户。

作用域的列(scope列)包含strings。

Table 6.6 Grant Table Scope Column Types

Column NameType
Host, Proxied_hostCHAR(60)
User, Proxied_userCHAR(16)
PasswordCHAR(41)
DbCHAR(64)
Table_nameCHAR(64)
Column_nameCHAR(64)
Routine_nameCHAR(64)

为了权限检查目的,对于User、Proxied_user、Password、Db和Table_name的比较是大小写敏感的,对于Host、Proxied_host, Column_name, 和Routine_name的比较是大小写不敏感的。

在user、db和host三张表中,每个权限都是被列在单独的column中,使用ENUM(‘N’,’Y’) DEFAULT ‘N’值来描述。也就是说,每个权限都可以disabled或enabled,默认是disabled。

在tables_priv、columns_priv和procs_priv表中,权限信息用SET列信息描述。只有在该列中列出的权限是enabled。

Table Set-Type Privilege Column Values

Table NameColumn NamePossible Set Elements
tables_privTable_priv‘Select’, ‘Insert’, ‘Update’, ‘Delete’, ‘Create’, ‘Drop’, ‘Grant’, ‘References’, ‘Index’, ‘Alter’, ‘Create View’, ‘Show view’, ‘Trigger’
tables_privColumn_priv‘Select’, ‘Insert’, ‘Update’, ‘References’
columns_privColumn_priv‘Select’, ‘Insert’, ‘Update’, ‘References’
procs_privProc_priv‘Execute’, ‘Alter Routine’, ‘Grant’

管理权限,例如RELOAD或SHUTDOWN权限只能在user表中指定。

FILE权限也只能在user表中指定,读取或写server host上文件是独立于database的。

mysqld server在启动时读取grant tables的内容到内存中。用户可以让server重新加载grant tables内容通过发送FLUSH PRIVILEGS语句或者执行mysqladmin flush-privileges或者mysqladmin reload命令。

当用户修改账户权限时,最好验证下权限的更改。可以使用SHOW GRANTS语句来检查给定用户的权限。

说明:show grants for user@host;

1.3 指定账户名

MySQL账户名包含了userName和hostName。这使得可以为从不同host上连接来的users创建相同userName。这一部分描述了如何写账户名,包括特殊值和通配符规则。

在SQL语句,例如CREATE USER、GRANT和SET PASSWORD,写账户名使用下面的规则:

  1. 账户名语法:’user_name’@ ‘host_name’。

  2. 对于仅包含user_name的账户等价于’user_name’@’%’。例如’me’等价于’me’@’%’。

  3. user_name和host如果是合法的不用引号的表示符,那么可以不需要引号。在user_name包含特殊字符例如’-‘或者host_name包含特殊字符或通配符(例如’%’)的时候,那么引号就是必须的,例如’test-user’@’%.com’。

  4. 引号含住user_name与host_name作为标示符或者strings,可以使用重音符“`”,单引号“’”或者双引号“””。

  5. user_name和host_name是分离的,如果用引号,需要分别添加。即使用:’me’@’localhost’,而不是’me@localhost’,后者相当于’me@localhost’@’%’。

  6. 引用 CURRENT_USER或CURRENT_USER()函数等价于指定当前客户端的user_name和host_name。

MySQL存储账户名字时将user_name和host_name分别作为列放在mysql database的授权表中:

  1. user table每个账户存一行。User和Host列分别存储user_name和host_name。这张表也标识了账户具有的全局权限。

  2. 其他grant tables标识一个账户在databases中拥有的databases和objects的权限。这些表都有User和Host列存储账户名字。在这些表中的每一行都与user表中的User和Host相关联。

User name和host name有一些特定值或通配符约定,描述如下:

user name是非空值表示从字面上匹配user name当连接来的时候,或是空值表示匹配任意user name。一个账户带有空的user name表示匿名账户。为了在SQL语句中指定匿名账户,使用引号引住空的user_name,像”@’localhost’。

host_name部分可以采用多种形式,并允许通配符。

  1. host值可以是一个host_name或者IP地址。’localhost’表示本地host,IP’127.0.0.1’表示IPv4回路接口,IP ‘::1’表示IPv6的回路接口。

  2. 用户可以使用通配符”%”和”_”在host name或者IP地址中。这些具有和模式匹配LIKE相同的意义。例如,host值为’%’表示匹配任意host name,’%.mysql.com’匹配任何在mysql.com域的host,’192.168.1.%’匹配任意在192.168.1的host。

因为用户可以使用IP通配符在host中,有用户可以利用此功能命名主机192.168.1.somewhere.com。为了使这种尝试失败,MySQL不允许匹配以数字和点号开头的host_name。这样,如果你有一个host name像1.2.example.com,它是永远不会匹配到账户的host部分的。IP通配符值只可以匹配IP地址,不可以是host name。

  1. 对于指定一个IPv4地址作为host值,用户可以指定子网掩码表明有多少位是用于子网号码。子网掩码不能用于IPv6地址。

语法是host_ip/netmask,例如:

CREATE USER ‘david’@’192.58.197.0/255.255.255.0’;

这使得david可以从任意具有如下IP条件的host客户端登录:

client_ip & netmask = host_ip

即,CREATE USER语句表明:

client_ip & 255.255.255.0 = 192.58.197.0

的IP地址符合上述条件。可以连接到MySQL server的范围有192.58.197.0 to 192.58.197.255。

子网掩码只能告诉server使用8、16、24、或32位地址,例如

  • 192.0.0.0/255.0.0.0:任意host在192.的A类地址上。

  • 192.168.0.0/255.255.0.0: 任意192.168 的B类地址上。

  • 192.168.1.0/255.255.255.0:任意在 192.168.1 的C类地址上。

  • 192.168.1.1:指定的IP地址。

下面的子网掩码会无效,因为它的掩码是28位,28不是8的倍数:

192.168.0.1/255.255.255.240

Server会使用DNS解析返回的客户端host_name或IP地址来匹配账户名中的host,而不是客户端的host。除了账户中host值是定义为子网掩码符号,否则比较只是做字符匹配,即使host值是一个IP地址。这就表明,用户需要制定账户中的host值和DNS使用的格式一致。这里举例说明要注意的问题:

  • 假设一个主机在本地网络上具有完全限定的名称host1.example.com。如果DNS返回名也是host1.example.com,那么就可以使用这个名字作为host值。但是如果DNS返回host1,那么就要用host代替。

  • 如果DNS返回给定host的IP一致是192.168.1.2,那么就使用该值,而不是192.168.01.2。类似,匹配host模式也是192.168.1.%,而不是192.168.01.%。

为了减少上述问题,建议检查DNS返回的host_name和地址,使用与其一致的格式作为MySQL的账户名。

1.4 访问控制,阶段1,连接验证

用户试图连接MySQL服务器时,服务器基于用户的身份以及用户是否能通过供应正确的密码验证身份来接受或拒绝连接。如果不是,服务器完全拒绝你的访问,否则,服务器接受连接,然后进入阶段2并且等待请求。

用户的身份基于2个信息:

  • 用户从那个主机连接

  • 用户的MySQL用户名

身份检查使用3个user表的列(Host, User和Password)。服务器只有在user表记录的Host和User列匹配客户端主机名和用户名并且提供了正确的密码时才接受连接。

如果User列的值是非空的,那么user_name必须与连接的完全一致。若干User值是空的,那么它匹配任意user_name。如果user表中的行匹配具有空的user_name的连接,那么user背认为是一个匿名用户。这表明空user_name可以用于连接的所有进一步访问检查(在阶段2)。

Password列可以是空的。这不是通配符,也不意味着匹配任何密码,它意味着用户必须不指定一个密码进行连接。如果验证是使用插件形式,而插件可能不使用Password列的内容,那么有可能需要额外的验证。

user表中的非空Password值代表加密的密码。MySQL不以任何人可以看的明文文本格式存储密码,相反,正在试图联接的用户提供的密码被加密(使用PASSWORD( )函数),在连接过程中使用加密的密码检查密码是否正确。(加密后的密码未通过连接即可实现)。从MySQL角度,加密的密码是实际密码,因此你不应让其它人访问它!特别是,绝对不要让非管理用户读mysql数据库中的表。

MySQL使用强鉴定方法(最先在MySQL 4.1中适用)在前面的版本中在连接进程中的密码保护较好。即使TCP/IP包被截取也很安全。

说明:TCP/IP包被截取如何安全。本身就是SSL连接?

下面的例子显示出各种user表中Host和User值的组合如何应用于到来的连接:

Host ValueUserValuePermissible Connections
‘thomas.loc.gov’‘fred’fred, connecting from thomas.loc.gov
‘thomas.loc.gov’Any user, connecting from thomas.loc.gov
‘%’‘fred’fred, connecting from any host
‘%’Any user, connecting from any host
‘%.loc.gov’‘fred’fred, connecting from any host in the loc.gov domain
‘x.y.%’‘fred’fred, connecting from x.y.net, x.y.com, x.y.edu, and so on; this is probably not useful
‘144.155.166.177’‘fred’fred, connecting from the host with IP address**144.155.166.177**
‘144.155.166.%’‘fred’fred, connecting from any host in the 144.155.166 class C subnet
‘144.155.166.0/255.255.255.0’‘fred’Same as previous example

到来的连接中的客户端名和用户名可能与user表中的多行匹配。例如,由fred从thomas.loc.gov的连接匹配多个条目。

如果有多个匹配,服务器必须选择使用哪个条目。按照下述方法解决问题:

(1)服务器在启动时读入user表后进行排序。

(2)然后当用户试图连接时,以排序的顺序浏览条目

(3)服务器使用与客户端和用户名匹配的第一行。

server端排序规则是,按最具体的(most-specific )Host值优先排序。原义的host_name和IP地址算是最具体的(IP地址的具体级别 specificity是不受子网掩码影响的,192.168.1.13 和192.168.1.0/255.255.255.0被认为是同一具体级别)。匹配符’%’算是最不具体的级别。空字符’’也表明任意host,但排在’%’后面。有相同Host值的行首先以最具体的User值排序(空User值意味着“任何用户”并且是最不具体的)。对于具有相同具体级别的Host和User值,排序是不确定。

为了看上述规则如何工作,假设user表是下面的样子:

+———–+———-+-

| Host | User | …

+———–+———-+-

| % | root | …

| % | jeffrey | …

| localhost | root | …

| localhost | | …

+———–+———-+-

当server端读取table到内存中,使用上述规则对行排序。排序后如下:

+———–+———-+-

| Host | User | …

+———–+———-+-

| localhost | root | …

| localhost | | …

| % | jeffrey | …

| % | root | …

+———–+———-+-

当客户端试图连接时,服务器浏览排序的行并使用找到第一匹配的。对于由jeffrey从localhost的连接,表内有两个条目匹配:Host和User值为’localhost’和”的条目,和值为’%’和’jeffrey’的条目。’localhost’行首先匹配,所以服务器会使用这一行。

给另一个例子,假设user表如下:

+—————-+———-+-

| Host | User | …

+—————-+———-+-

| % | jeffrey | …

| thomas.loc.gov | | …

+—————-+———-+-

排序表如下:

+—————-+———-+-

| Host | User | …

+—————-+———-+-

| thomas.loc.gov | | …

| % | jeffrey | …

+—————-+———-+-

由jeffrey从thomas.loc.gov的连接与第一行匹配,而由jeffrey从whitehouse.gov的连接被第二个匹配。

NOTE:普遍的误解是认为,对给定的用户名,当服务器试图对连接寻找匹配时,明确命名user的row将首先被使用。但这不符合事实。先前的例子说明了这点,在那里由jeffrey从thomas.loc.gov的连接没被包含’jeffrey’作为User列值的行匹配,但是由没有用户名的行匹配!结果是,jeffrey被鉴定为匿名用户,即使他连接时指定了用户名。

如果你能够连接服务器,但你的权限不是你期望的,你可能被鉴定为其它账户。要想找出服务器用来鉴定你的账户,使用CURRENT_USER()函数。它返回user_name@host_name格式的值,说明User和Host 值匹配user表记录。假定jeffrey连接并发出下面的查询:

mysql> SELECT CURRENT_USER();

+—————-+

| CURRENT_USER() |

+—————-+

| @localhost |

+—————-+

这儿显示的结果说明user表行有空的User列值。换句话说,服务器将jeffrey视为匿名用户。

另一个方法是打印user table然后手动排序看哪一个被匹配。

1.5 访问控制,阶段2:请求验证

一旦用户建立了连接,服务器进入访问控制的阶段2。对在此连接上进来的每个请求,服务器都会检查用户要执行什么操作,然后检查是否有足够的权限来执行。这正是在授权表中的权限列发挥作用的地方。这些权限可以来自user、db、host、tables_priv、 columns_priv或procs_priv表。

user表在全局基础上授予赋予你的权限,该权限不管当前的数据库是什么均适用。例如,如果user表授予你DELETE权限, 你可以删除在服务器主机上从任何数据库删除行!建议只把user表的权限授予给需要它们的人,例如数据库管理员。对其他用户,你应该把在user表中的权限设成’N’并且仅在特定级别上授权。用户可以为特定的数据库、表、列或routine授权。

db和host表授予特定数据库的权限。在这些表中的范围列的值可以采用以下形式:

  • 空白user值在db table中是匹配匿名用户。非空白值从字面上匹配。user_name没有通配符。

  • 通配符字符“%”并“_”可用于两个表的Host和Db列。它们与用LIKE操作符执行的模式匹配操作具有相同的含义。如果授权时你想使用其中的某个字符,必须使用反斜现引用。例如,要想在数据库名中包括下划线(‘_’),在GRANT语句中用‘\_’来指定。

  • 在db表的’%’Host值意味着“任何主机”,在db表中空Host值意味着“对进一步的信息咨询host表”。

  • 在host表的’%’或空Host值意味着“任何host”。

  • 在两个表中的’%’或空Db值意味着“任何database”。

服务器启动时读取db和host表在并排序(同时它读user表)。db表在Host、Db和User范围列上排序,并且host表在Host和Db范围列上排序。对于user表,首先根据最具体的值最后根据最不具体的值排序,并且当服务器寻找匹配条目时,它使用它找到的第一匹配。

tables_priv、columns_priv和procs_priv表授予具体的表、列和routine权限。这些表的范围列的值可以有如下形式

  • 通配符“%”并“_”可用在使用在两个表的Host列。

  • 在两个表中的’%’或空Host意味着“任何host”。

  • 在两个表中的Db、Table_name和Column_name列不能包含通配符或空。

server对tables_priv和columns_priv表根据Host、Db和User列被排序。这类似于db表的排序,因为只有Host列可以包含通配符,排序更简单。

server使用排序的tables去验证每一个它接收到的请求。对于管理权限请求,例如SHUTDOWN或RELOAD,server只检查user table里的行,因为只有这张表有具体的管理员权限信息。

对数据库有关的请求(INSERT、UPDATE等等),服务器首先通过查找user表行来检查用户的全局权限。如果行允许请求的操作,访问被授权。如果在user表中全局权限不够,服务器通过检查db和host表确定特定的用户数据库权限:

  1. 服务器在db表的Host、Db和User列上查找匹配。Host和User对应连接用户的主机名和MySQL用户名。Db列对应用户想要访问的数据库。如果没有Host和User的行,访问被拒绝。

  2. 如果db表中有匹配的行而且它的Host列不是空的,该行定义用户的指定数据库的权限。

  3. 如果匹配的db表的行的Host列是空的,它表示host表列举被允许访问数据库的主机。在这种情况下,在host表中作进一步查找以发现Host和Db列上的匹配。如果没有host表行匹配,访问被拒绝。如果有匹配,用户在指定数据库的权限是在db和host表的行的权限的交集(而不是并集!)计算,即在两个行都是’Y’。(这样你可以授予在db表行中的一般权限,然后用host表行按host为基础有选择地限制它们。)

在确定了由db和host表行授予的数据库特定的权限后,服务器把他们与由user表授予的全局权限相加。如果结果允许请求的操作,访问被授权。否则,服务器检查在tables_priv和columns_priv表中的用户的表和列权限并把它们加到用户权限中。基于此结果允许或拒绝访问。对于stored-routine操作,服务器使用procs_priv table。

用布尔术语表示,前面关于用户权限如何计算的描述可以这样总结:

global privileges

OR (database privileges AND host privileges)

OR table privileges

OR column privileges

它可能不明显,为什么呢,如果全局user行的权限最初发现对请求的操作不够,服务器以后把这些权限加到数据库、表并列的特定权限。原因是请求可能要求超过一种类型的权限。例如,如果用户执行INSERT INTO … SELECT语句,用户就需要有INSERT和SELECT权限。用户的权限可能是在user表中被授予一个权限而db表中被授予了另一个权限。在这种情况下,用户拥有执行请求的必要权限,但是单独一个授权表无法表现出来,所以两个表中授予的权限必须组合起来。

说明:这一点需要注意,需要先把user的权限和db的权限加起来。那么是不是要把所有的权限都组合起来啊。

host表不受GRANT或REVOKE语句的影响,因此在大多数MySQL安装中没有使用。如果你直接修改它,你可以用于某种专门目的,例如用来在本地网络中维护一个具有所有权限的安全服务器列表。

用户也可以使用host表指定安全的host。假定用户有一台机器public.your.domain,它位于用户认为不安全的公共区域,用户可以用下列的host表的行表示允许除了那台机器外的网络上所有主机的访问:

+——————–+—-+-

| Host | Db | …

+——————–+—-+-

| public.your.domain | % | … (all privileges set to ‘N’)

| %.your.domain | % | … (all privileges set to ‘Y’)

+——————–+—-+-

1.6 权限更改何时生效

当mysqld启动时,所有授权表的内容被读进内存并且从此时生效。

如果用户使用账户管理语句如GRANT、REVOKE、SET PASSWORD或RENAME USER来间接修改grant tables,那么server会发现这些变化并重新加载grant tables到内存。

如果用户手动地修改授权表(使用INSERT、UPDATE或DELETE等等),用户应该执行mysqladmin flush-privilegesmysqladmin reload告诉服务器再装载授权表,否则你的更改将不会生效,除非用户重启服务器。

要告诉server重新加载grant tables,需要执行flush-privileges操作。这可以通过发送FLUSH PRIVILEGES语句或者执行mysqladmin flush-privileges 或mysqladmin reload命令。

一个已经存在的连接,授权表重新加载权限影响的情况如下:

  • Table和Column权限变化会在客户端的下一个请求就发生影响。

  • 数据库权限变化会在客户端在下一次执行use db_name的时候发生影响。

NOTE:客户端应用有可能会缓存database name,因此这会导致除非改变database或者flush privileges,db权限改变会对他们不可见。

  • 全局权限和密码变化是不会影响当前连接的客户端的。这些改变只有在后面的连接中才能反应出来。

说明:这里其实和MySQL的实现有关,MySQL存在一个SecureCtx类(相当于session中有),里面存储了一个连接的user_priv和当前的db_priv。在使用use db_name时才会更新这里的权限。如果用户没有使用use db_name,而是用db_name.table_name来直接访问表,那么database的权限也会立即生效。由于session中有user_priv,所以只能下次连接时才会生效。

1.7 拒绝访问的原因

当用户试着联接MySQL服务器时,如果碰到问题,下面各项可以帮助用户解决这些问题:

  • 确保服务器在运行。如果服务器没有运行,则你不能连接服务器。如果用户试图连接服务器并看到下述消息,可能是服务器没有运行:

shell> mysql

ERROR 2003: Can’t connect to MySQL server on ‘host_name’ (111)

shell> mysql

ERROR 2002: Can’t connect to local MySQL server through socket

‘/tmp/mysql.sock’ (111)

  • 也可能服务器正在运行,但用户可能使用与服务器上侦听的不一样的TCP/IP端口、命名管道或Unix套接字文件。用户可以调用客户端程序,指定—port选项来指示正确的端口来指示正确的命名管道或–socket选项来指示正确的Unix套接字文件。要找出套接字文件,用户可以使用命令:

shell> netstat -ln | grep mysql

  • 必须正确设置授权表,以便服务器可以使用它们进行访问控制。对于某些分发版类型(例如Windows中的二进制分发版或Linux中的RPM分发版),安装过程初始化包含 授权表的mysql数据库。如果分发版没有这样做,你必须运行mysql_install_db脚本来手动初始化授权表。、

确定是否要初始化授权表的一个方法是寻找数据目录下的mysql目录(数据目录名通常为data或var,位于MySQL安装目录下)。应保证MySQL数据库目录有文件“user.MYD”。否则,执行mysql_install_db程序。运行程序并启动服务后,执行下面命令来测试初始权限:

shell> mysql -u root test

server应该同意连接并不报错。

  • 在新安装后,用户应该连接server并设置用户及他们的访问许可:

shell> mysql -u root mysql

server会让用户连接,因为MySQL root用户初始时没有密码。这也是安全风险,所以设置其他MySQL用户时也要为root用户设置密码。

  • 如果用户将一个现存的MySQL安装升级到较新的版本,需要运行mysql_fix_privilege_tables脚本。增加新功能后,授权表的结构可能会改变,因此更新后应确保表的结构随之更新。

  • 如果客户端程序试图连接时收到以下错误信息,说明服务器需要新的形式的密码,而当前客户端无法生成:

shell> mysql

Client does not support authentication protocol requested

by server; consider upgrading MySQL client

  • 记住客户端程序使用选项文件或环境变量中指定的连接参数。如果客户端程序发送不正确的默认连接参数,而你没有在命令行中指定,检查环境变量和适用的选项文件。例如,当你不用任何选项运行客户端程序,得到Access denied错误,确保你没有在选项文件中指定旧密码!

你可以通过使用–no-defaults选项调用客户端程序来禁用选项文件。例如:

shell> mysqladmin –no-defaults -u root version

  • 如果遇到下述错误,说明root密码错误:

shell> mysqladmin -u root -p*xxxx* ver

Access denied for user ‘root’@’localhost’ (using password: YES)

如果你未指定密码时出现前面的错误,说明某个选项文件中的密码不正确。试试前面所说的–no-defaults选项。

  • 如果你使用SET PASSWORD、INSERT或UPDATE更改密码,你必须使用 PASSWORD()函数加密密码。如果你不使用PASSWORD()函数,密码将不生效。例如,下面的语句设置密码,但没能加密,因此用户后面不能连接:

  • SET PASSWORD FOR ‘abe’@’host_name’ = ‘eagle’;

应这样设置密码:

SET PASSWORD FOR ‘abe’@’host_name’ = PASSWORD(‘eagle’);

  • 当用户使用GRANT或CREATE USER语句或mysqladmin password命令指定密码时,不需要PASSWORD()函数,它们会自动使用PASSWORD()来加密密码。

  • localhost是用户本地主机名的一个同义词,并且也是如果你不明确地指定主机而客户端尝试连接的默认主机。

要想在这种系统上避免该问题,你可以使用–host=127.0.0.1选项来明确命名服务器主机。这样将通过TCP/IP协议来连接本地mysqld服务器。你还可以指定–host选项使用TCP/IP,使用实际的本机主机名。在这种情况下,主机名必须指定为服务器主机上的user表行,即使你在服务器上运行客户端程序。

  • 当尝试用mysql -u user_name与数据库连接时,如果你得到一个Access denied错误,可能会遇到与user表有关的问题,通过执行mysql -u root mysql并且执行下面的SQL语句进行检查:

SELECT * FROM user;

  • Access denied错误消息将告诉你,你正在用哪个用户尝试登录,你正在试图连接哪个主机,是否使用了密码。通常,你应该在user表中有一行,正确地匹配在错误消息给出的主机名和用户名。例如,如果遇到包含using password: NO的错误信息,说明你登录时没有密码。

  • 如果当你试着从一个不是MySQL server正在运行的host上连接时,遇到下列错误,那么在user表中没有Host值匹配那台主机的行:

Host … is not allowed to connect to this MySQL server

可以通过组合你正在试图连接的用户/主机名设置一个账户来修正它。如果你不知道正连接的机器的IP号或主机名,应该把一个’%’行作为Host列值放在user表中。在试图从客户端器连接以后,通过SELECT USER()查询显示你如何真正进行连接。(然后用在日志文件上面显示出的实际的主机名代替user表中的’%’行。否则,你将得到一个不安全的系统,因为它允许从任何主机上以任何用户名连接。)

在Linux中,发生该错误的另一个原因可能是你正使用于你所使用版本的glibc库不同版本的库编译的二进制MySQL版本。在这种情况下,你应升级操作系统或glibc,或下载MySQL版本的源码分发版并自己编译。源码RPM一般很容易编译并安装,因此不是大问题。

  • 如果你连接时指定host_name,但得到错误消息主机名未显示或显示为IP号,表示当MySQL服务器在解析客户host的IP到name时遇到错误:

shell> mysqladmin -u root -p*xxxx* -h some_hostname ver

Access denied for user ‘root’@” (using password: YES)

如果用户以root连接然后获取如下错误,表明用户没有在user表中具有User列为root值的行,同时mysqld无法解析客户端的host_name。

Access denied for user ”@’unknown’

这些错误表明DNS有问题。要想修复,执行mysqladmin flush-hosts来重设内部 DNS主机名缓存。

一些常用的解决方案包括:

  • 试试找出DNS服务器的错误并修复。

  • 在MySQL授权表中指定IP号而不是主机名。

  • 在/etc/hosts中放入客户端名。

  • 用–skip-name-resolve选项启动mysqld。

  • 用–skip-host-cache选项启动mysqld。

  • 在Unix中,如果你在同一台机器上运行服务器和客户端,连接到localhost。连接到的localhost的Unix连接使用Unix套接字文件而不是TCP/IP。

  • 在Windows中,你在同一台机器上运行服务器和客户端并且服务器支持命名管道连接,连接主机名(周期)。连接使用命名管道而不是TCP/IP。

  • 如果mysql -u root test工作但是mysql -h your_hostname -u root test导致Access denied(your_hostname是本地机的实际主机名),那么在user表中可能没有你的主机的正确名字。这里的一个普遍的问题是在user表行中的Host值指定一个唯一的主机名,但是你系统的名字解析例程返回一个完全正规的域名(或相反)。例如,如果你在user表中有一个主机是’pluto’的行,但是你的DNS告诉MySQL你的主机名是’pluto.example.com’,行将不工作。尝试把一个行加到user表中,它包含你主机的IP号作为Host列的值。(另外,你可以把一个行加到user表中,它有包含一个通配符如’pluto.%’的Host值。然而,使用以“%”结尾的主机名是不安全的并且不推荐!)

  • 如果mysql -u user_name test工作但是mysql -u user_name other_db_name不工作,你没有为给定的用户授予other_db_name数据库的访问权限。

  • 当在服务器上执行mysql -u user_name时,它工作,但是在其它远程客户端上执mysql -h host_name -u user_name时,它却不工作,你没有为给定的用户授予从远程主机访问服务器的权限。

  • 如果你不能弄明白你为什么得到Access denied,从user表中删除所有Host包含通配符值的行(包含“%”或“_”的条目)。一个很普遍的错误是用Host=’%’和User=’some_user‘插入一个新行,认为这将允许你指定localhost从同一台机器进行连接。它不工作的原因是 默认权限包括一个有Host=’localhost’和User=”的行,因为那个行的Host值’localhost’比’%’更具体,当从localhost连接时,它用于指向新行!正确的步骤是插入Host=’localhost’和User=’some_user‘的第2个行,或删除Host=’localhost’和User=”行。删除条目后,记住用FLUSH PRIVILEGES语句重载授权表。

  • 如果你得到下列错误,可以与db或host表有关:

Access to database denied

如果从db表中选择了在Host列有空值的条目,需要保证在host表中有一个或多个相应的条目,指定db表中的条目适用哪些主机。

  • 如果你能够连接MySQL服务器,但如果在使用命令SELECT … INTO OUTFILE或LOAD DATA INFILE语句时,你得到Access denied错误,在user表中的条目可能没有启用FILE权限。

  • 如果你直接更改授权表(例如,使用INSERT、UPDATE或DELETE语句)并且你的更改好像被忽略了,记住你必须执行FLUSH PRIVILEGES语句或mysqladmin flush-privileges命令让服务器来重读授权表。否则,直到服务器下次重启,你的更改方有效。记住用UPDATE命令更改root密码后,在清空权限前,你不需要指定新密码,因为服务器还不知道你已经更改了密码!

  • 如果你的权限似乎在一个会话过程中改变了,可能是一个管理员改变了他们。再次装入授权表会影响新客户端连接,但是它也影响现存的连接

  • 如果你有Perl、Python或ODBC程序的存取问题,试着用mysql -u user_name db_name或mysql -u user_name -p*your_pass* db_name与服务器连接。如果你能用mysql客户端进行连接,这是程序的一个问题而不是访问权限的问题。(注意在-p和密码之间没有空格;也可以使用–password=your_pass语法指定密码。如果使用-p选项,MySQL提示你输入密码。)

  • 为了测试,用–skip-grant-tables选项启动mysqld守护进程,然后你可以改变MySQL授权表并且使用mysqlaccess脚本检查你的修改是否有如期的效果。当你对你的改变满意时,执行mysqladmin flush-privileges告诉mysqld服务器开始使用新的 授权表。(再次装入授权表覆盖了–skip-grant-tables选项。这允许你告诉服务器开始使用授权表,而不用停掉并重启它)。

  • 如果任何其它事情失败,用调试选项(例如,–debug=d,general,query)启动mysqld服务器。这将打印有关尝试连接的主机和用户信息,和发出的每个命令的信息。

  • 2
    点赞
  • 3
    评论
  • 3
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值