MySQL访问权限管理

参考文档:

官方文档:https://dev.mysql.com/doc/refman/5.7/en/security.html

这里有你想要的所有...

书籍:《MySQL性能优化金字塔法则》

账户权限操作

详细语法参考官方文档:https://dev.mysql.com/doc/refman/5.7/en/account-management-statements.html

创建用户示例:

CREATE USER 'jack'@'localhost' IDENTIFIED BY '123456';
CREATE USER 'rose'@'192.168.38.110_' IDENDIFIED BY '123456';
CREATE USER 'rose'@'%' IDENTIFIED BY '123456';
CREATE USER 'rose'@'%' IDENTIFIED BY '';
CREATE USER 'rose'@'%';
CREATE USER test;

说明:创建用户时建议遵守规范,比如CREATE USER 'jack'@'localhost' IDENTIFIED BY '123456';

用户名可以加引号也可以不见引号,如果用户名有特殊字符'-'等,必须加引号。

主机白名单必须加单引号,密码也是必须加单引号。

CREATE USER test; 等同于CREATE USER 'test'@'%' IDENTIFIED BY '';

修改用户密码:建议alter user去修改

5.6版本:

SET PASSWORD FOR 'usernamexxx'@'hostxxx' = PASSWORD('newpasswordxxx');
#如果是当前登陆用户用:
SET PASSWORD = PASSWORD("newpasswordxxx"); 

5.7版本:

#兼容了5.6版本set password方法
SET PASSWORD FOR 'usernamexxx'@'hostxxx' = PASSWORD('newpasswordxxx');
#如果是当前登陆用户用:
SET PASSWORD = PASSWORD("newpasswordxxx"); 
#新增alter user方法,和oracle保持一致了
alter user 'usernamexxx'@'hostxxx' identified by 'yourpassword';

8.0版本:

#废弃了set password方法,统一为alter user
alter user 'usernamexxx'@'hostxxx' identified by 'yourpassword';

授权语句示例:

GRANT SELECT, INSERT ON DbXXX.user TO 'jack'@'%';
GRANT ALL PRIVILEGES ON *.* TO 'jack'@'%';
GRANT ALL ON DbXXX.* TO 'jack'@'%'; 
GRANT SELECT(HOST) ON mysql.user to 'test'@'192.168.0.100'; #某表列的查询权限

说明:若想让被授权后的用户也可以授权,加 WITH GRANT OPTION选项。

撤销用户权限示例:

REVOKE privilegexxx ON databasenamexxx.tablenamexxx FROM 'usernamexxx'@'hostxxx';
REVOKE SELECT ON *.* FROM 'jack'@'%';

说明:mysql用户的具体权限要看show grants for 'usernamexxx'@'hostxxx'; 需要主要如果有某个库上all权限和select权限两行,单独revoke all权限后select权限还在,select权限也需要单独去revoke。

删除用户:

DROP USER 'usernamexxx'@'hostxxx';

连接mysql故障排除

下面介绍客户端无法连接服务器的问题,以及解决方法。

• 服务器未启动,可以通过检查服务器进程是否存在来解决(ps aux|grep mysqld,如果未启动则尝试拉起,如果启动失败则检查错误日志排查原因)。通常报错信息类似于

如下所示。

■ 以TCP/IP方式连接:ERROR 2003: Can't connect to MySQL server on 'host_name'(111)。

■ 以Socket方式连接:ERROR 2002: Can't connect to local MySQL server through socket '/tmp/mysql.sock' (111)。

• 客户端连错端口,可以通过检查服务器进程运行的端口(netstat-ln | grepmysqld),找到正确的端口并在客户端指定连接该端口来解决。

• 服务器启动时使用了--skip-networking或者--bind-address = 127.0.0.1选项,它将仅在本地环回接口上侦听TCP/IP连接,并且不会接受远程连接。可以通过去除这些选项并重

启进程来解决。

• 服务器端防火墙未打开MySQL服务器的端口访问权限,可以通过关闭防火墙或者允许MySQL服务器的服务端口对外提供服务来解决。

• 没有使用正确的账号或者密码连接服务器,通常报错信息类似于:ERROR1045(28000): Access denied for user 'root'@'localhost' (using password: NO)。

• 如果数据库是第一次初始化,且使用了mysqld--initialize-secure命令来初始化,则会为root用户生成一个随机密码,在MySQL服务器启动之后需要在错误日志中搜索

password关键字,以找到随机密码进行登录(如果不需要该随机密码,则可以使用mysqld --initialize-insecure命令来初始化数据库),否则会报出拒绝连接的错误。

• 如果只升级了服务器到最新版本,而客户端没有做相应的升级,则可能报出认证协议不支持的错误:Client does not support authentication protocol requested by

server;consider upgrading MySQL client。最好的解决办法是升级客户端版本,不建议修改密码认证插件。

• 服务器达到了最大用户连接数限制,此时可以使用具有Super权限的管理员账号登录数据库,修改最大连接数参数。

• 服务器达到了最大错误连接数限制,反复尝试连接的某些客户端被拒绝连接(例如:使用错误的账号或密码反复尝试连接多次,达到了最大错误连接数),此时可以使用

管理员账号从其他主机登录数据库执行FLUSH HOSTS语句刷新主机缓存信息,或者修改最大错误连接数参数。

提示:MySQL访问权限控制系统有如下限制。

• 不能明确拒绝指定用户的访问,只能明确允许指定用户的访问。例如:使用正确的账号和密码,从被授权访问的主机上访问数据库。

• 不能单独授权用户只可以创建或删除数据库中的表,而不可以创建或删除数据库本身(指定某用户对某表拥有Create和Drop权限之后,该用户就能够创建和删除该表所在的库了)。

• 账号的密码在服务器中的作用域是全局性的。不能将密码与对特定对象的访问权限挂钩(如数据库、表或存储过程与函数等,即账号的密码与是否有权限访问数据库中的具体某个对象无关)。

重置root密码(忘记密码)

第一步:没有密码,关闭数据库只能kill

如果 MySQL 正在运行,首先杀之: killall -TERM mysqld。 

第二步:跳过权限表方式启动mysql

运行mysqld_safe --skip-grant-tables & 

如果此时不想被远程连接:mysqld_safe --skip-grant-tables --skip-networking &

第三步:连接到mysql后更改密码

方法1:mysql5.7、8.0

update mysql.user set authentication_string=password('1234.C0m') where user='root' and Host = 'localhost';

注意的一点是,mysql5.7及其以上版本的user表中已经没有Password字段了,而是将加密后的用户密码存储于authentication_string字段。

方法2:mysql5.6、5.7、8.0均可

set password for 'root'@'localhost'=password('1234.C0m');

注意:skip-grant-tables时,不能用alter user和set password password=('new_passord')来修改密码。

第四步:修改字典表后需要刷新下权限

mysql> flush privileges;

mysql> quit;

第五步:正常启动mysql

killall -TERM mysqld。 

mysqld_safe & #正常方式启动

第六步:登录到数据库后再次修改密码,确保修改成功

mysql5.7及其以上推荐方式:

alter user 'root'@'localhost' identified by '1234.C0m';

mysql5.6:

set password for 'root'@'localhost'=password('1234.C0m');
flush privileges;

权限迁移

5.6版本

shell脚本入参:

export_db_ip=$1         #服务器IP

export_db_port=$2       #MySQL端口

export_user=$3          #导出用户

export_user_passwd=$4   #导出用户密码

shell脚本:

###传入导出服务器及用户信息
export_db_ip=$1
export_db_port=$2
export_user=$3
export_user_passwd=$4

###文件保存目录
path=`pwd`

###导出用户
mysql -B -N -h $export_db_ip -P $export_db_port -u${export_user} -p${export_user_passwd} -e "SELECT CONCAT('\'', user,'\'@\'', host, '\'') FROM user WHERE user != 'mysql.session' and user != 'mysql.sys' AND user != 'root' AND user != ''" mysql > $path/mysql_all_users.txt

###导出用户权限
while read line;
do
    mysql -B -N -h $export_db_ip -P $export_db_port -u${export_user} -p${export_user_passwd} -e "SHOW GRANTS FOR $line";
done < $path/mysql_all_users.txt > $path/mysql_all_users_sql.sql


###每行行尾增加;
sed -i 's/$/;/' $path/mysql_all_users_sql.sql

###立即生效
echo "flush privileges;" >> $path/mysql_all_users_sql.sql

5.7及其以上版本

shell脚本入参:

export_db_ip=$1         #服务器IP

export_db_port=$2       #MySQL端口

export_user=$3          #导出用户

export_user_passwd=$4   #导出用户密码

shell脚本:

#!/bin/bash
#Function export user privileges
#5.7存在问题: show grants for 不会给出密码信息,必须用 show create user
# https://dev.mysql.com/doc/refman/5.7/en/show-grants.html  
# show create user 为5.7版本开始存在,5.6执行报错。


###传入导出服务器及用户信息
export_db_ip=$1
export_db_port=$2
export_user=$3
export_user_passwd=$4

###文件保存目录
path=`pwd`

source /etc/profile
 
pwd=password 
expgrants()  
{  
  mysql -B -h $export_db_ip -P $export_db_port -u${export_user} -p${export_user_passwd} -N $@ -e "SELECT CONCAT(  'SHOW CREATE USER   ''', user, '''@''', host, ''';' ) AS query FROM mysql.user" | \
  mysql -h $export_db_ip -P $export_db_port -u${export_user} -p${export_user_passwd} -f  $@ | \
  sed 's#$#;#g;s/^\(CREATE USER for .*\)/-- \1 /;/--/{x;p;x;}' 
 
  mysql -B -h $export_db_ip -P $export_db_port -u${export_user} -p${export_user_passwd} -N $@ -e "SELECT CONCAT(  'SHOW GRANTS FOR ''', user, '''@''', host, ''';' ) AS query FROM mysql.user" | \
  mysql -h $export_db_ip -P $export_db_port -u${export_user} -p${export_user_passwd} -f  $@ | \
  sed 's/\(GRANT .*\)/\1;/;s/^\(Grants for .*\)/-- \1 /;/--/{x;p;x;}'   
}  
expgrants > $path/mysql_all_users_sql_5.7.sql


###立即生效
echo "flush privileges;" >> $path/mysql_all_users_sql_5.7.sql

最后在目标库上执行导入脚本:

mysql -u root -p < mysql_all_users_sql.sql

开发权限规范

1) 写库账号默认权限为select,insert,update,delete,不要给建表改表(create,alter)的权限,更不能给all权限。

2) 读库账号默认权限为select(配合read-only参数用)。一定要确保从库是只读的(对所有人员)。

3) 根据需要,最好专库专账号,不要一账号管理多个库。碎库特别多的,根据情况处理。

4) 数据库分离的服务器的授权可以根据web服务器数量多少按IP或网段来授权。

5) 安全性和方便管理,是矛盾的,要尽量达到一个平衡的状态,如何使平衡就要根据具体公司和业务来衡量了。

权限系统表

在mysql5.7系统库中,访问权限系统表包含如下几个表。

user

包含用户账户、全局权限和其他非权限列表(安全配置字段和资源控制字段)

db

数据库级别的权限表

tables_priv

表级别的权限表

columns_priv

字段级别的权限表

procs_priv

存储过程和函数权限表

proxies_priv

代理用户权限表

user表

user表提供查询全局权限信息。该表中的账号密码信息在认证1阶段,决定着是否允许用户连接,对于通过账号密码认证阶段的连接,如果同时通过了user表xxx_priv列的权限检查,那么就代表该用户拥有全局权限。该表中记录的权限信息代表用户是否拥有该实例下所有数据库的相应全局权限。

注意:如果在user表中有任意一个权限字段值为Y的,就被认为拥有全局权限,所以用户在使用SHOW

DATABASES或者information_schema下的schemata表查询时,可以查询到所有数据库名称列表。

Host和User

官方称为范围字段,可以理解为这两个字段构成了允许访问的客户端范围,以及客户端可以访问的数据库资源范围(这里没有像db表那样的Db字段限制库范围,可以理解为整个实例范围的数据库)

Host

代表允许用户从哪些主机访问数据库,可以用通配符和DNS。

User

用户名

权限字段,xxx_priv

从Select_priv到Create_tablespace_priv之间的字段,官方称为权限字段。每一个字段对应一个具体的权限,值为Y代表有权限,值为N代表没有权限。

ssl_type

如果用户配置了使用加密SSL连接,则该字段记录用户使用的加密证书类型。

ssl_cipher

表示用于SSL连接握手中可能使用到的密码列表

x509_issuer/x509_subject

x509证书相关字段

plugin

代表用户使用的密码认证插件名称。

authentication_string

表示用户密码的MD5加密字符串

password_expired

表示用户密码是否过期,值为Y表示用户密码会过期,值为N表示用户密码永不过期。

password_last_changed

表示用户密码的最近一次修改时间。如果使用MySQL内建的认证插件(mysql_native_password或sha256_password),则该字段值为非空;如果使用外部认证插件,则该字段值为空;当使用MySQL内建的认证插件时,该字段初始值为CREATE USER、ALTER USER、SET PASSWORD、GRANT语句执行创建用户或者修改密码时的时间。

password_lifetime

如果password_expired字段值为Y,则该字段记录用户剩余的密码未过期天数;假设该字段值为N,则表示用户需要每N天修改一次密码。如果用户未单独指定该值,则使用全局系统变量default_password_lifetime的值代替。当该字段值为NULL且全局系统变量default_password_lifetime为0或者该字段值为0时,表示该用户的密

码永不过期。

 account_locked

代表用户当前状态是锁定状态还是激活可用状态。

max_questions

代表用户每小时的最大并发查询数

max_updates

代表用户每小时的最大并发更新次数

max_connections

代表用户每小时的最大并发连接数

max_user_connections

代表该行记录中的用户每小时的最大并发连接数

db表

db表提供查询库级别的对象权限信息。该表中记录的权限信息代表用户是否可以使用这些权限来访问被授予访问的数据库下的所有对象(表或存储程序)。

Host、Db、User

官方称为范围字段,可以理解为这三个字段构成了允许访问的客户端范围,以及客户端可以访问的数据库资源范围。

Host/user

与user表的Host/user字段含义相同

db

代表该用户权限记录属于哪个库级别范围。
xxx_priv

与user表的xxx_priv字段含义相同,每一个字段都对应相应的权限,值为Y代表有权限,值为N代表没有权限。与user表相比,少了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等字段,即代表这些字段对应的权限是全局范围的,不区分库表级别。

tables_priv表

tables_priv表提供查询表级别的权限信息。与db表类似,但粒度更细,tables_priv表中记录的权限信息代表用户是否可以使用这些权限访问某个表中的所有字段。

Host、Db、User、Table_name

官方称为范围字段,可以理解为这几个字段构成了允许访问的客户端范围,以及客户端可以访问的表对象资源范围。

Table_priv和Column_priv

官方称为权限字段,对应于表级别权限和字段级别权限。需要注意的是,这两个权限字段与user表和db表不同,它们是set类型的,记录着表级别和字段级别的权限集合,而不是对应于具体的某个权限。Table_priv对应于表级别的Select, Insert, Update, Delete, Create, Drop, Grant, References, Index,Alter, Create View, Show view, Trigger权限,Column_priv对应于字段级别的Select, Insert, Update, References权限。

Grantor

代表该表中记录的用户权限是被谁授予的,即该表中的用户权限被授予时的current_user函数返回的用户值(account形式)

Timestamp

代表授予Grantor表示的用户权限时的时间戳

columns_priv表

columns_priv表提供查询字段级别的权限信息。与db表类似,但粒度更细,columns_priv表中记录的权限信息代表用户可以使用这些权限来访问某个表的指定字段。

Host、Db、User、Table_name、Column_name

官方称为范围字段,可以理解为这几个字段构成了允许访问的客户端范围,以及客户端可以访问的字段对象资源范围

Column_priv

官方称为权限字段,与tables_priv表中的Column_priv字段含义相同,也是一个集合类型,对应于字段级别的Select, Insert,Update, References权限

Timestamp

与tables_priv表中的Timestamp字段含义相同

procs_priv表

procs_priv表提供查询存储程序(存储过程和函数)的权限信息。该表中记录的权限信息代表用户是否可以使用这些权限来访问指定的存储程序。

Host、Db、User、Routine_name、Routine_type

官方称为范围字段,可以理解为这几个字段构成了允许访问的客户端范围,以及客户端可以访问的存储对象资源。

Proc_priv

官方称为权限字段,该权限字段也是一个集合类型,代表存储程序的Execute, Alter Routine, Grant权限

Timestamp

与tables_priv表中的Timestamp字段含义相同

Grantor

与tables_priv表中的Grantor字段含义相同

proxies_priv表

proxies_priv表提供查询代理用户的权限信息。该表中记录的权限信息代表用户可以充当哪些用户的代理,以及用户是否可以将Proxy权限授予其他用户。

如果一个用户需要将它的Proxy权限授予其他用户,那么该用户必须在该表中有一行权限信息,且With_grant字段值必须为1。

Host、User

这两个字段的含义与前面的权限表相同

Proxied_host和Proxied_user

表示Proxy权限的来源account(被代理的用户)对应的Host和User字符串

其他字段的含义和tables_priv表相同

权限集说明

mysql中通过show privileges可以看到所有能授予的权限,contex列做了分类。comment列针对每个权限做解释。

All或All privileges

除Grant option之外,代表其他所有权限。

Alter

该权限用于使用ALTER TABLE语句来更改表的结构(除该权限之外,使用ALTER TABLE语句还需要有Create和Insert权限,使用ALTER TABLE RENAME语句需要有旧表上的Alter和Drop权限,新表上的Create和Insert权限)

Alter routine

该权限用于修改或删除存储过程或存储函数

Create

该权限用于创建库和表

Create routine

该权限用于创建存储过程或存储函数

Create tablespace

该权限用于创建、修改、删除表空间文件和日志组文件

Create temporary tables

该权限用于创建临时表。使用CREATE TEMPORARY TABLE语句创建临时表,一旦某会话创建临时表成功后,服务器不会在该表上执行权限检查(因为其他会话看不见此表,创建此表的会话一旦断开,临时表就会自动删除)。即,创建临时表的会话可以对该临时表执行任何操作,例如DROP TABLE、INSERT、UPDATE、SELECT等操作。

Create user

该权限用于使用ALTER USER、CREATE USER、DROP USER、RENAME USER、REVOKE ALL PRIVILEGES语句。

Create view

该权限用于使用CREATE VIEW语句

Delete

该权限用于从数据库表中删除数据记录

Drop

该权限用于删除现有库、表、视图等对象。另外,如果在分区表上使用ALTER TABLE ... DROP PARTITION语句,则必须要有表的Drop权限,执行TRUNCATE TABLE也需要有Drop权限(但要注意,如果将MySQL数据库的Drop权限授予用户,则该用户可以删除存储MySQL访问权限记录的数据库mysql)

Event

该权限用于创建、更改、删除或查看Event Scheduler事件。

Execute

该权限用于执行存储过程或存储函数

File

该权限用于执行LOAD DATA INFILE和SELECT ... INTO OUTFILE语句以及LOAD_FILE()函数来读写服务器主机上的文件。具有File权限的用户可以读取服务器主机上的任何可读文件或MySQL服务器可读文件。(即,用户可读取datadir目录中的任何文

件),File权限还使用户能够在MySQL服务器有写入权限的任何目录下创建新文件。所以,作为安全保护措施,服务器不会覆盖现有文件(即,在执行导出数据到文本时,如果文件名重复,则导出语句无法成功执行)。在MySQL 5.7版本中,可以使用secure_file_priv系统变量限制File权限的读写目录。

Grant option

该权限用于授予或回收其他用户或自己拥有的权限

Index

该权限用于创建或删除索引。Index权限适用于在已存在的表上使用CREATE INDEX语句,如果用户具有Create权限,则可以在CREATE TABLE语句中包含索引定义语句。

Insert

该权限用于向表中插入数据记录行。对于ANALYZE TABLE、OPTIMIZE TABLE和REPAIR TABLE表维护语句也需要Insert权限。

Lock tables

该权限用于使用LOCK TABLES语句对表显式加锁,持有表锁的用户对该表有读写权限,未持有表锁的用户对该表的读写访问会被阻塞。

Process

该权限用于显示有关在服务器上执行的线程信息(即,关于会话正在执行的语句相关状态信息)。拥有该权限的用户在使用SHOW PROCESSLIST语句或mysqladmin processlist命令查看有关线程信息时,除可以看到自己的线程信息之外还可以查看到属于其他账号的线程信息。另外,使用SHOW ENGINE语句以及查看information_schema系统库中的相当一部分表也需要该权限。

Proxy

该权限使用户能够模仿(伪装、代理)另一个用户

References

在创建外键约束时,该权限需要用户具有父表的References权限

Reload

该权限允许用户使用FLUSH语句。拥有该权限的用户还可以使用与FLUSH操作等效的mysqladmin子命令——flush-hosts、flush-logs、flush-privileges、flushstatus、flush-tables、flush-threads、refresh和reload。其中,reload子命令会通知服务器将权限表重新加载到内存中;flush-privileges子命令的作用与reload相同;refresh子命令会通知服务器关闭并重新打开日志文件且刷新所有表。其他flush-xxx子命令也会执行类似于刷新的功能,这些子命令刷新的对象更具体。例如,只想刷新日志文件,则使用flush-logs子命令。

Replication client

该权限用于使用SHOW MASTER STATUS、SHOW SLAVE STATUS和SHOW BINARY LOGS语句。

Replication slave

该权限用于从从库服务器连接到主库服务器并请求主库的binlog日志。如果没有此权限,从库将无法请求主库数据库变更的binlog日志。

Select

该权限用于从数据库表中查询数据行记录。使用SELECT语句只有实际从表中检索行记录时才需要Select权限。但某些SELECT语句不需要访问表,并且可以在没有任何数据库权限的情况下执行。例如,使用SELECT语句拼接的常量表达式:SELECT 1 + 1; SELECT PI()* 2;。另外,使用UPDATE或DELETE语句,当使用WHERE子句指定了某字段的条件值时,也需要该字段的SELECT权限;否则,你会发现可以使用UPDATE不带WHERE子句更新全表,却不能使用WHERE语句指定更新某些行记录。对基表或视图使用EXPLAIN语句,也需要用户对表或视图具有该权限。

Show databases

该权限用于执行SHOW DATABASE语句,对于没有此权限的用户,则只能看到其具有对应访问权限的数据库列表。如果服务器使用了--skip-show-database选项启动,则没有该权限的用户即使对某库有其他访问权限,也不能使用SHOW DATABASES语句查看任何数据库列表(会报错:ERROR 1227 (42000): Access denied; you need (at least one of) the SHOW DATABASES privilege(s) for this operation)。

Show view

该权限用于执行SHOW CREATE VIEW语句。对视图使用EXPLAIN语句也需要此权限。

Shutdown

该权限用于执行SHUTDOWN语句、mysqladmin shutdown命令和mysql_shutdown() C API函数。

Super

该权限下面进行详细解释。

Trigger

该权限用于触发器的操作。用户必须拥有某表的该权限才能针对该表创建、删除、执行或查看其触发器。

Update

该权限用于执行对数据库表中的数据行更新操作。

Usage

该权限代表用户“无任何权限”。全局级别权限,拥有该权限的用户可以登录到数据库服务器中,但在默认配置下除能够执行部分show命令之外,其他任何数据变更和数据库查询操作都无法执行。

super 权限的详细解释:

修改全局系统配置变量需要此权限。对于某些系统变量,修改会话级别的系统配置变量也需要Super权限(如果修改会话级别的系统配置变量需要Super权限,在变量的解

释文档中会进行说明,例如binlog_format、sql_log_bin和sql_log_off)。

■ 对全局事务特征的更改(START TRANSACTION语句)。

■ 从库服务器用于执行启动和停止复制的语句,包括组复制。

■ 从库服务器用于执行CHANGE MASTER TO和CHANGE REPLICATION FILTER语句。

■ 执行PURGE BINARY LOGS和BINLOG语句。

■ 如果视图或存储程序定义了DEFINER属性,则拥有Super权限的用户就算不是该视图或存储程序的创建者,也仍然可以执行该视图或存储程序。

■ 执行CREATE SERVER、ALTER SERVER和DROP SERVER语句。

■ 执行mysqladmin debug命令。

■ 用于InnoDB key自旋。

■ 通过执行DES_ENCRYPT()函数启用读取DES密钥文件。

■ 执行用户自定义函数时启用版本令牌。

■ 超过了最大连接数之后,具有Super权限的账户还可以执行的操作有:

➢使用KILL语句或mysqladmin kill命令来终止属于其他账户的线程(注意:无论是否拥有Super权限,用户总是可以kill自己的线程)。

➢即使服务器总连接数达到max_connections系统变量定义的值,服务器也会接受来自具有Super权限的用户的一个额外连接。

➢即使服务器启用了read_only系统变量,具有Super权限的用户也仍然可以执行数据更新,包括显式的操作更新和隐式的操作更新(账户管理语句GRANT和REVOKE等触发

的表更新)。

➢具有Super权限的用户连接服务器时,服务器不执行init_connect系统变量指定的内容。

➢处于脱机模式(已启用offline_mode系统变量)的服务器不会中断具有Super权限的用户的连接,且仍然接受具有Super权限的用户的新连接请求。

■ 如果启用了二进制日志记录功能,则用户可能还需要Super权限才能创建或更改存储的功能。

MySQL账号访问控制

第一阶段(账号和密码认证)

当用户尝试连接MySQL服务器时,服务器根据如下条件来决定是否接受或拒绝连接。

• 用户的身份信息(账号名称,由user_name@host_name格式组成)以及密码信息是否可以验证通过。

• 用户的账号是否处于锁定状态。

当MySQL服务器接收到一个新的连接请求时,服务器首先检查用户凭证(账号+密码),然后检查账号的锁定状态。任意一个步骤检查失败,都会拒绝连接。如果两个步骤

都通过检查,则进入第二阶段并等待执行请求。

MySQL服务器使用user表中的Host、User、authentication_string三个字段存储的用户凭证信息来执行凭证检查。用户账号的锁定状态记录在user表的account_locked字段中。

用户的身份信息由user_name和host_name两部分组成,对于这两个组成部分有如下认证规则。

• 如果用户名字段即User字段值不为空,则用户尝试连接时就必须传入用户名字符串,且必须完全匹配;如果用户名字段值为空,则在进行认证时可以匹配任意用户名(包

括为空和不为空的用户名,为空的用户名被称为匿名用户)。在账号访问控制的第一阶段匹配到匿名用户的,在第二阶段认证时仍然会使用匿名用户。

• 如果密码信息字段即authentication_string字段值为空,则意味着用户尝试连接服务器时不需要输入密码(注意:密码信息字段与用户名字段不同,当密码信息字段值为空

时,只能匹配空串的密码,不能匹配任意密码)。如果服务器使用了认证插件对客户端进行身份验证,则在插件实现的身份验证方法中可能会也可能不会使用authentication_string

字段中的密码字符串,甚至可能还会使用外部密码认证服务器结合MySQL服务器进行身份验证。

• user表中非空的authentication_string字段值表示加密过的密码字符串(hash加密)。 MySQL在authentication_string字段中不存储明文格式的密码(使用账号认证插件实

现的密码散列方法加密),在连接认证过程中使用加密的密码来检查密码是否正确。从MySQL的角度来看,加密的密码才是真正的密码,所以,在非授权情况下,不要让别人

知道你的密码信息,特别是不要让其拥有对mysql库的访问权限。

下面列举了一些user_name和host_name常用的组合。

• 'fred'@'h1.example.net':表示fred用户从h1.example.net主机连接。

• ''@'h1.example.net':表示任意用户从h1.example.net主机连接。

• 'fred'@'%':表示fred用户从任意主机连接。

• ''@'%':表示任意用户从任意主机连接。

• 'fred'@'%.example.net':表示fred用户从example.net域中的任意主机连接。

• 'fred'@'x.example.%':表示fred用户从x.example.net、x.example.com、x.example.edu任意域名后缀的主机连接(但后缀%限制可能不生效)。

• 'fred'@'192.51.100.177':表示fred用户从IP地址为192.51.100.177的主机连接。

• 'fred'@'192.51.100.%':表示fred用户从192.51.100 C类子网中的任意主机连接。

• 'fred'@'192.51.100.0/255.255.255.0':含义同'fred'@'192.51.100.%'。

客户端传入服务器中的身份标识(主机名和用户名)可能与user表中的多个行记录匹配成功。当客户端尝试连接服务器时,如果在服务器的user表中匹配到多个行记录的身份

认证信息,则服务器必须确定要使用哪一行记录进行许可(不同的身份信息行记录可能对应着不同的权限)。

• 服务器只要将user表读入内存,就会在内存中对用户信息进行排序。

• 当客户端尝试连接时,服务器会按照内存中排好序的内容依次进行匹配。

• 服务器使用与客户端主机名和用户名相匹配的第一行记录进行授权。

在服务器使用的排序规则中,先排序主机名字段值(越精确的值越靠前,字符串主机名和IP地址是最具体的。另外,IP地址的精确度不会受到掩码的影响,例如:

192.51.100.13和192.51.100.0/255.255.255.0被视为具有相同的精确度。通配符“%”表示“任意主机”,被视为精确度较差的主机名。空字符串也意味着“任意主机”,但精确度

比“%”更差,所以排在“%”之后),然后按照用户名字段值进行排序(排序规则与主机名字段值的排序规则类似)。主机名和用户名两个字段值的排序规则有点类似于多列索引中

的排序规则。

select user() 用来查询当前连接的真实用户。select current_user()来查询当前连接使用哪个用户。如下:

mysql -h172.17.0.1 -uroot -p1234.C0m -P5725
21:58:12[5.7.25][email protected][(none)]> select user(),current_user();
+-----------------+----------------+
| user()          | current_user() |
+-----------------+----------------+
| [email protected] | root@%         |
+-----------------+----------------+
1 row in set (0.00 sec)

21:58:44[5.7.25][email protected][(none)]> select user,host from mysql.user where user='root';
+------+-----------+
| user | host      |
+------+-----------+
| root | %         |
| root | localhost |
+------+-----------+
2 rows in set (0.00 sec)

第二阶段(权限检查)

当客户端与MySQL服务器建立连接之后,服务器进入账号访问控制的第二阶段。在第二阶段中,客户端发送给服务器的每个请求,服务器都会检查请求操作的类型,然后检

查是否有足够的访问权限来执行请求操作。该检查工作依赖于mysql库下的user、db、tables_priv、columns_priv、procs_priv、proxies_priv权限表中存放的权限信息。

user表中的权限作用范围是全局性的,当该表中的相应权限类型字段值为'Y'时,就表示对数据库实例中的所有数据库表都有该权限。所以,在大多数时候,我们需要根据具体

的业务环境需求来为需要访问的数据库授予对应的权限,而不是图方便直接为所有库所有表授予所有权限(关于如何授予权限,请参考上文中提到的权限分类内容)。

• 当User字段值为空时表示匿名用户,非空值必须匹配字符串字面本身表示的用户名,用户名不能使用通配符。

• Host字段值不允许为空(虽然授权语句和创建用户的语句可以只写用户名而不写主机名,但实际上存储在表中时会被转换为“%”),但可以使用通配符(“%”和“_”,“%”表示任意主机,“_”表示主机名中的任意一个字符),并且可以使用LIKE关键字来配合通配符进行匹配。

db表中的权限作用范围是数据库级别的,对应数据库内的所有对象。

• User字段和Host字段的表现形式要求与user表相同。

• 与user表类似,服务器会在启动时就将db表中的内容读入内存,并在内存中根据Host、Db和User 三个字段对db表中的数据进行排序。排序会将最具体的值放在最前面,

将最不具体的值放到最后,当服务器进行用户匹配查找时,会使用第一个匹配行进行许可。

tables_priv、columns_priv和procs_priv这三个表中记录着表级别权限、字段级别权限和存储程序权限。

• User字段和Host字段的表现形式要求与user表相同。

• Db、Table_name、Column_name和Routine_name字段不能包含通配符或为空值。

• 与user表类似,服务器会在启动时就将这三个表的内容读入内存,并在内存中根据Host、Db和User三个字段对tables_priv、columns_priv和procs_priv表数据进行排序。

当一个客户端连接在进行第二阶段权限检查时,首先会检查user表,如果所检查权限是user表特有的(在其他权限表中没有该权限),且在user表中该权限记录为Yes,则服务

器授予客户端访问权限,否则直接拒绝而不会继续检查其他权限表(因为在其他权限表中没有该权限,无须检查);如果所检查权限除在user表中有之外,在其他权限表中也有

(例如:DML操作权限),则即使在user表中不允许(毕竟user表中的权限是全局性的),也会继续往下检查db表,然后检查tables_priv表,依此类推。

权限变更影响

当mysqld启动时,将读取所有权限表的内容到内存中。后续所有用户对MySQL服务器的访问权限都是基于内存中保存的这些值进行授予的。

• 如果在MySQL服务器运行期间使用账户管理语句(如GRANT、REVOKE、SETPASSWORD或RENAME USER)间接修改了权限表,则服务器会立即将权限表中的内容重新加载到内存中。

• 如果在MySQL服务器运行期间使用INSERT、UPDATE或DELETE等语句直接修改了权限表,那么对权限表的更改不会立即生效,除非重新启动服务器或者使用FLUSH PRIVILEGES语句或mysqladmin flush-privileges|reload等命令来重新加载权限表。

对于权限表的重新加载,需要注意如下事项。

• 对于表级别和字段级别权限,修改并重新加载权限表之后,对于已经建立的客户端连接,会在对表、字段的下一个请求中生效。对于新建连接,第一个请求即生效。

• 对于库级别权限,修改并重新加载权限表之后,对于已经建立的客户端连接,会在下一次使用USE DB_NAME语句时生效。对于新建连接,第一个请求即生效。

提示:如果回收了某用户对某库的权限,但客户端已经建立连接且当前默认库正好是已回收权限的库,那么若客户端不使用USE DB_NAME语句切换默认库,则可能无法感知到库级别权限发生了变化。

• 对于全局性权限和密码的修改,不影响已建立连接的客户端,只对重新连接或新建连接的客户端生效。

如果服务器启动时使用了--skip-grant-tables选项,则服务器不会读取权限表,也不会进行任何访问权限控制,这个时候任何人都可以免密码登录数据库并做任何事情。所以,除非维护时间窗口,否则禁止启用--skip-grant-tables选项。如果要重新加载权限表,则无须重新启动服务器,只需要执行FLUSH PRIVILEGES语句即可。

安全插件

官方参考:https://dev.mysql.com/doc/refman/5.7/en/security-plugins.html

密码验证插件

https://dev.mysql.com/doc/refman/5.7/en/validate-password.html

安装了5.7的msyql之后,在为 root 用户修改密码 或者 新增用户时,都会碰到一个问题,就是密码的强度不够。这是因为,5.7版本中,默认安装了一个叫 validate password plugin 的插件,它会要求:

MySQL's validate_password plugin is installed by default. This will require that passwords contain at least one upper case letter, one lower case letter, one digit, and one special character, and that the total password length is at least 8 characters

查看数据库密码验证功能情况:

15:07:30[5.7.25][email protected][(none)]> show variables like 'validate_password%';
+--------------------------------------+--------+
| Variable_name                        | Value  |
+--------------------------------------+--------+
| validate_password_check_user_name    | OFF    |
| validate_password_dictionary_file    |        |
| validate_password_length             | 8      |
| validate_password_mixed_case_count   | 1      |
| validate_password_number_count       | 1      |
| validate_password_policy             | MEDIUM |
| validate_password_special_char_count | 1      |
+--------------------------------------+--------+
7 rows in set (0.01 sec)

参数解释:

1).validate_password_dictionary_file 指定密码验证的文件路径;

2).validate_password_length 密码最小长度

3).validate_password_mixed_case_count 密码至少要包含的小写字母个数和大写字母个数;

4).validate_password_number_count 密码至少要包含的数字个数

5).validate_password_policy 密码强度检查等级,对应等级为:0/LOW、1/MEDIUM、2/STRONG,默认为1

注意:

0/LOW:只检查长度;

1/MEDIUM:检查长度、数字、大小写、特殊字符;

2/STRONG:检查长度、数字、大小写、特殊字符字典文件。

6).validate_password_special_char_count密码至少要包含的特殊字符数

如果想永久关闭/打开这个插件的话,只需要在 /etc/my.conf 中 的 [mysqld] 中增加如下的配置:

validate_password=OFF
#相反,若要打开,validate_password=ON

如果想临时打开/关闭这个插件的话,可以如下操作:mysql中执行

INSTALL PLUGIN validate_password SONAME 'validate_password.so'; #加载插件
UNINSTALL PLUGIN validate_password;  #卸载插件

不安全操作之关闭密码安全策略:

例如,创建用户时报错:

mysql> create user 'miner'@'192.168.%' IDENTIFIED BY 'miner123';
ERROR 1819 (HY000): Your password does not satisfy the current policy requirements

报错原因:密码强度不够。

解决方法:(该账号为测试账号,所以采用降低密码策略强度)

mysql> set global validate_password_policy=0;
Query OK, 0 rows affected (0.00 sec)
mysql> set global validate_password_length=4;
Query OK, 0 rows affected (0.00 sec)
mysql> SHOW VARIABLES LIKE 'validate_password%';
+--------------------------------------+-------+
| Variable_name                        | Value |
+--------------------------------------+-------+
| validate_password_dictionary_file    |       |
| validate_password_length             | 4     |
| validate_password_mixed_case_count   | 1     |
| validate_password_number_count       | 1     |
| validate_password_policy             | LOW   |
| validate_password_special_char_count | 1     |
+--------------------------------------+-------+
6 rows in set (0.00 sec)

再次创建用户,成功

猜你喜欢

转载自blog.csdn.net/u010033674/article/details/107617682