首页 > 数据库 > > 6.2 mysql 访问权限系统
参考官方文档:
https://dev.mysql.com/doc/refman/5.7/en/privilege-system.html
MySQL权限系统的主要功能是对从给定主机连接的用户进行身份验证,并将该用户与数据库上的权限(如SELECT,INSERT,UPDATE和DELETE)相关联。 其他功能包括具有匿名用户和授予特定于MySQL的功能(如LOAD DATA INFILE和管理操作)的权限。
有些事情你不能用MySQL权限系统来做:
MySQL特权系统的用户界面由SQL语句组成,如CREATE USER,GRANT和REVOKE。
在内部,服务器将权限信息存储在mysql数据库的授权表中(也就是在名为mysql的数据库中)。 MySQL服务器在启动时将这些表的内容读取到内存中,并基于授予表的内存副本对访问控制做出决定。
MySQL特权系统确保所有用户只能执行允许的操作。 作为用户,当您连接到MySQL服务器时,您的身份由连接的主机和您指定的用户名确定。 在连接后发出请求时,系统将根据您的身份和您想要执行的操作授予权限。
因为没有理由认为给定的用户名属于所有主机上的同一个人,所以MySQL会将您的主机名和用户名都视为识别您的身份。 例如,从office.example.com连接的用户joe不是与从home.example.com连接的用户joe是同一个人。 MySQL通过使您能够区分具有相同名称的不同主机上的用户来处理这个问题:您可以授予来自office.example.com的joe连接的一组权限,以及来自home的joe连接的另一组权限.example.com的。 要查看给定帐户具有的权限,请使用SHOW GRANTS语句。 例如:
SHOW GRANTS FOR 'joe'@'office.example.com';
SHOW GRANTS FOR 'joe'@'home.example.com';
当您运行连接到服务器的客户端程序时,MySQL访问控制涉及两个阶段:
阶段1:服务器根据您的身份接受或拒绝连接,以及您是否通过提供正确的密码来验证您的身份。
阶段2:假设您可以连接,服务器会检查您发出的每条语句,以确定您是否有足够的权限来执行它。 例如,如果尝试从数据库中的表中选择行或从数据库中删除表,服务器会验证您是否具有该表的SELECT权限或数据库的DROP权限。
如果您在连接时更改了权限(通过您自己或其他人),那么对于您发布的下一条语句,这些更改不一定会立即生效。
6.2.1 MYSQL 提供的的权限
授予MySQL帐户的权限决定了用户可以执行的操作。 MySQL权限在应用的上下文和不同的操作级别上有所不同:
有关帐户权限的信息存储在mysql系统数据库中的user,db,tables_priv,columns_priv和procs_priv表中(请参见第6.2.2节“授予表”)。 MySQL服务器在启动时将这些表的内容读入内存,并在第6.2.6节“特权更改生效时”中指出的情况下重新加载它们。 访问控制决策基于授权表的内存中副本。
一些MySQL版本引入了对授权表结构的更改以添加新的特权或功能。 为了确保您可以利用任何新功能,请更新您的授权表,以便在升级MySQL时拥有当前结构。
以下列表提供了MySQL中可用权限的一般描述。 特定的SQL语句可能具有比此处指示的更具体的特权要求。 如果是这样,那么有关声明的描述将提供详细信息。
会话创建临时表后,服务器不会在该表上执行进一步的特权检查。 创建会话可以对表执行任何操作,例如DROP TABLE,INSERT,UPDATE或SELECT。
要限制可以读取和写入文件的位置,请将secure_file_priv系统设置为特定的目录。
reload命令会通知服务器将授权表重新加载到内存中。 flush-privileges是reload的同义词。 刷新命令关闭并重新打开日志文件并刷新所有表。 其他flush-xxx命令执行类似于刷新的功能,但是更具体并且在某些情况下可能是优选的。 例如,如果只想刷新日志文件,flush-logs是比刷新更好的选择。
SELECT 1+1;
SELECT PI()*2;
其他读取列值的语句也需要SELECT权限。 例如,对于在UPDATE语句中col_name = expr赋值的右侧引用的列或DELETE或UPDATE语句的WHERE子句中命名的列,需要SELECT。
(delete,update 其实也是要先检索数据的)
用于EXPLAIN的表或视图也需要SELECT特权,包括任何基础的视图表。
如上所述,如果启用二进制日志记录,则可能还需要SUPER权限才能创建或更改存储的功能
当激活触发器时(由有权为与触发器关联的表执行INSERT,UPDATE或DELETE语句的用户),触发器执行要求定义触发器的用户仍具有TRIGGER特权。
向账户授予只有它需要的权限是一个好主意。 您在授予FILE和管理权限时应特别小心:
6.2.2 Grant 表
MySQL系统数据库包含多个授权表,其中包含有关用户帐户及其所拥有的权限的信息。 本节介绍这些表。
要操作授权表的内容,可以使用帐户管理语句(如CREATE USER,GRANT和REVOKE)间接修改它们以设置帐户并控制每个帐户可用的权限。 请参见第13.7.1节“帐户管理语句”。 这里的讨论描述了授权表的底层结构以及服务器在与客户端交互时如何使用其内容。
注意:
使用诸如INSERT,UPDATE或DELETE之类的语句直接修改授权表是不鼓励的,并且风险自负。 服务器可以自由忽略由于此类修改而变得格式错误的行。
从MySQL 5.7.18开始,对于修改授权表的任何操作,服务器会检查该表是否具有预期的结构,如果没有,则会产生错误。 必须运行mysql_upgrade以将表更新为预期的结构。
导致:
因为任何全局特权都被认为是所有数据库的特权,所以任何全局特权都允许用户使用SHOW DATABASES或检查INFORMATION_SCHEMA的SCHEMATA表来查看所有数据库名称。
服务器在访问控制的第一和第二阶段都使用mysql数据库中的user和db表(请参见第6.2节“MySQL Access Privilege System”)。
user表的, plugin ,authentication_string列存储认证插件和凭证信息。
服务器使用帐户行的 plugin 列中指定的插件来验证帐户的连接尝试。
密码过期后可以通过将密码设置为其当前值来“重置”密码。 作为一个良好的政策,最好选择一个不同的密码。
在访问控制的第二阶段,服务器执行请求验证,以确保每个客户端对其发出的每个请求拥有足够的权限。 除了用户和db授权表之外,服务器还可以查询tables_priv和columns_priv表以获取涉及表的请求。 后面的表格在表和列级别提供更好的权限控制。 它们具有下表中显示的列。
要使帐户能够将PROXY特权授予其他帐户,它必须在proxies_priv表中具有一行,并将With_grant设置为1,并将Proxied_host和Proxied_user设置为指示可授予特权的帐户。 例如,在MySQL安装过程中创建的'root'@'localhost'帐户在proxies_priv表中有一行,可以为''@@'' 授予PROXY特权,即为所有用户和所有主机授予PROXY特权。 这使得root可以设置代理用户,也可以委派给其他账户设置代理用户的权限。
表6.7 设置类型权限列值
只有用户表指定管理权限,例如RELOAD和SHUTDOWN。 管理操作是服务器本身的操作,并不是特定于数据库的,因此没有理由在其他授权表中列出这些权限。 因此,服务器只需咨询用户表以确定用户是否可以执行管理操作。
FILE权限也仅在用户表中指定。 这不是管理权限,但用户在服务器主机上读写文件的能力与被访问的数据库无关。
服务器在启动时将授权表的内容读入内存。 您可以通过发出FLUSH PRIVILEGES语句或执行mysqladmin flush-privileges或mysqladmin reload命令来通知它重新加载表。
在修改帐户时,验证您的更改是否具有预期效果是一个好主意。 要检查给定帐户的权限,请使用SHOW GRANTS语句。 例如,要确定授予用户名和主机名值为bob和http://pc84.example.com的帐户的权限,请使用以下语句:
SHOW GRANTS FOR 'bob'@'pc84.example.com';
要显示帐户的非权限属性,请使用SHOW CREATE USER:
SHOW CREATE USER 'bob'@'pc84.example.com';
6.2.3指定帐户名称
MySQL帐户名由用户名和主机名组成。 这样可以为可以从不同主机连接的具有相同名称的用户创建帐户。 本节介绍如何编写帐户名称,包括特殊值和通配符规则。
在SQL语句(如CREATE USER,GRANT和SET PASSWORD)中,帐户名称遵循以下规则:
MySQL在mysql 系统数据库授权表中使用单独的列分别存储主机名和用户名:
用户名和主机名具有某些特殊值或通配符约定,如下所述。
帐户名称的用户名部分可以是与输入连接尝试的用户名字面相匹配的非空白值,也可以是与任何用户名匹配的空值(空字符串)。 具有空白用户名的帐户是匿名用户。 要在SQL语句中指定一个匿名用户,请使用带引号的空用户名部分,如''@'localhost'。
帐户名称的主机名部分可以采用多种形式,并且允许使用通配符:
由于主机值允许使用IP通配符值(例如,'198.51.100.%' 匹配子网上的每台主机),所以有人可以尝试通过命名主机http://198.51.100.somewhere.com来利用此功能。 为了阻止这种尝试,MySQL不会在以数字和点开头的主机名上执行匹配。 例如,如果主机名为http://1.2.example.com,则其名称从不匹配帐户名称的主机部分。 IP通配符值只能匹配IP地址,而不能匹配主机名。
语法是host_ip / netmask。 例如:
CREATE USER 'david'@'198.51.100.0/255.255.255.0';
这使得david可以从具有以下条件的IP地址client_ip的任何客户端主机进行连接:
client_ip & netmask = host_ip
满足此条件的IP地址范围从198.51.100.0到198.51.100.255。
网络掩码通常以位设置为1开始,后续设置为0.示例:
服务器使用系统DNS解析程序返回的值为客户端主机名或IP地址执行帐户名中主机值与客户端主机的匹配。 除了使用网络掩码表示法指定帐户主机值以外,服务器还会将该比较作为字符串匹配执行,即使对于作为IP地址给定的帐户主机值也是如此。 这意味着您应该使用以DNS相同格式指定帐户主机值。 以下是需要注意的问题示例:
6.2.4 访问控制,阶段1:连接验证
当您尝试连接到MySQL服务器时,服务器根据这些条件接受或拒绝连接:
服务器首先检查凭证,然后检查帐户锁定状态。 任一步骤失败都会导致服务器完全拒绝您的访问。 否则,服务器接受连接,然后进入第2阶段并等待请求。
使用三个user表作用域列(Host,User和authentication_string)执行凭据检查。 锁定状态记录在用户表account_locked列中。 仅当某用户表行中的主机和用户列与客户机主机名和用户名匹配时,服务器才会接受连接,客户机提供该行中指定的密码,并且account_locked值为'N'。 第6.2.3节“指定帐户名称”给出了允许的主机和用户值的规则。 帐户锁定可以通过ALTER USER语句进行更改。
您的身份基于两部分信息:
如果user列值不是空白,则传入连接中的用户名必须完全匹配。 如果用户值为空白,则它匹配任何用户名称。 如果user表行空用户名与传入的连接匹配,则该用户被认为是不具有名称的匿名用户,而不是客户端实际指定名称的用户。 这意味着在连接期间(即在第2阶段),所有进一步访问检查都会使用空白用户名。
authentication_string列可以是空白的。 这不是通配符,并不意味着任何密码匹配。 这意味着用户必须连接而不指定密码。 如果服务器使用插件对客户端进行身份验证,则插件实现的身份验证方法可能会或可能不会使用authentication_string列中的密码。 在这种情况下,可能还会使用外部密码对MySQL服务器进行身份验证。
user表中非空白的authentication_string值表示加密的密码。 MySQL不以明文形式存储任何人都能看到的密码。 相反,试图连接的用户提供的密码是加密的(使用由帐户认证插件实施的密码散列方法)。 然后在连接过程中使用加密的密码来检查密码是否正确。 这是在没有经过连接的加密密码的情况下完成的。
从MySQL的角度来看,加密的密码是真正的密码,所以你绝对不应该让任何人访问它。 特别是,不要让非管理员用户读取mysql数据库中的表。
当多个匹配成为可能时,服务器必须确定要使用哪一个。 它解决了这个问题如下:
服务器使用排序规则,排序规则先排列具有最特定主机值的行。 文字主机名和IP地址是最具体的。 (文字IP地址的特异性不受其是否具有网络掩码的影响,因此198.51.100.13和198.51.100.0/255.255.255.0被视为同等具体。)模式'%'表示“任何主机”,并且是最不具体的。 空字符串 ''也意味着任何主机,但在'%'之后排序。 具有相同主机值的行首先与最具体的用户值排序(空白user值意味着“任何用户”,并且是最不特定的)。 对于具有相同特定主机和用户值的行,该顺序是不确定的。
这是另一个例子。 假设用户表如下所示:
+----------------+----------+-
| Host | User | ...
+----------------+----------+-
| % | jeffrey | ...
| http://h1.example.net | | ...
+----------------+----------+-
被排序的表看起来这样:
+----------------+----------+-
| Host | User | ...
+----------------+----------+-
| http://h1.example.net | | ...
| % | jeffrey | ...
+----------------+----------+-
来自http://h1.example.net的jeffrey的连接与第一行匹配,而来自任何主机的jeffrey的连接与第二行匹配。
注意:
认为对于给定的用户名,当服务器试图为连接找到匹配时,首先使用明确指定该用户的所有行是一种常见的误解。 这不是真的。 前面的例子说明了这一点,其中来自jeffrey的来自http://h1.example.net的连接首先不与作为用户列值的包含'jeffrey'的行匹配,而是与没有用户名的行匹配。 因此,jeffrey被认证为匿名用户,即使他在连接时指定了用户名。
如果您能够连接到服务器,但您的权限不符合您的期望,那么您可能会被认证为其他帐户。 要找出服务器用于验证您的帐户,请使用CURRENT_USER()函数。 (请参见第12.14节“信息函数”。)它以user_name @ host_name格式返回一个值,该值指示匹配的用户表格行中的用户和主机值。 假设jeffrey连接并发出以下查询:
mysql> SELECT CURRENT_USER();
+----------------+
| CURRENT_USER() |
+----------------+
| @localhost |
+----------------+
此处显示的结果表明匹配的用户表行具有空白的用户列值。 换句话说,服务器将jeffrey视为匿名用户。
诊断身份验证问题的另一种方法是打印出用户表并手动排序以查看第一个匹配的位置。
6.2.5 访问控制,阶段2:请求验证
建立连接后,服务器进入访问控制的第2阶段。 对于通过该连接发出的每个请求,服务器都会确定要执行的操作,然后检查您是否有足够的权限来执行此操作。 这是授予表中的权限列发挥作用的地方。 这些权限可以来自任何user,db,tables_priv,columns_priv或procs_priv表。
user表授予在全局基础上分配给您的权限,并且该权限不管默认数据库是什么。 例如,如果user表授予您DELETE权限,则可以从服务器主机上的任何数据库中的任何表中删除行! 将用户表中的权限仅授予需要它们的人员是明智的,例如数据库管理员。 对于其他用户,您应该将user表中的所有权限设置为'N',并且仅在更具体的级别授予权限。 您可以授予特定数据库,表,列或例程的权限。
db表授予数据库特定的权限。
服务器将db表读入内存,并在读取用户表的同时对其进行排序。 服务器根据Host,Db和User范围列对db表进行排序。 与用户表一样,排序会将最具体的值放在最前面,将最不特定的值放到最后,当服务器查找匹配的行时,会使用找到的第一个匹配项。
tables_priv,columns_priv和procs_priv表授予特定于表,列特定和特定于例程的特权。 这些表的范围列中的值可以采用以下形式:
服务器根据Host,Db和User列对tables_priv,columns_priv和procs_priv表进行排序。 这与db表排序类似,但更简单,因为只有Host列可以包含通配符。
服务器使用排序表来验证它收到的每个请求。 对于需要管理特权(如SHUTDOWN或RELOAD)的请求,服务器仅检查user表行,因为这是唯一指定管理特权的表。 如果该行允许请求的操作,则服务器授予访问权限,否则拒绝访问。 例如,如果你想执行mysqladmin shutdown,但你的用户表行没有授予SHUTDOWN特权给你,服务器拒绝访问,甚至不检查db表。 (它不包含Shutdown_priv列,所以不需要这样做。)
对于与数据库相关的请求(INSERT,UPDATE等),服务器首先通过查看user行来检查用户的全局特权。 如果该行允许请求的操作,则授予访问权限。 如果用户表中的全局权限不足,则服务器通过检查db表来确定用户的数据库特定权限:
服务器在db表中查找Host,Db和User列上的匹配项。 Host和User列与连接用户的主机名和MySQL用户名匹配。 Db列与User想要访问的数据库相匹配。 如果Host和User没有行,则访问被拒绝。
在确定数据库表行所授予的特定于数据库的特权之后,服务器将它们添加到用户表所授予的全局特权中。 如果结果允许请求的操作,则授予访问权限。 否则,服务器会连续检查tables_priv和columns_priv表中用户的表和列权限,将这些权限添加到用户的权限,并根据结果允许或拒绝访问。 对于存储例程操作,服务器使用procs_priv表而不是tables_priv和columns_priv。
用布尔表达式来表示用户权限计算方式的上述描述可以总结如下:
global privileges
OR (database privileges AND host privileges)
OR table privileges
OR column privileges
OR routine privileges
如果最初发现全局用户行权限不足以满足所请求的操作,则可能不明显的是服务器稍后将这些权限添加到数据库,表和列权限。 原因是请求可能需要多种类型的权限。 例如,如果您执行INSERT INTO ... SELECT语句,则需要INSERT和SELECT权限。 您的权限可能是这样的:用户表行授予一个权限,而数据库表行授予另一个权限。 在这种情况下,您拥有执行请求所需的特权,但服务器无法从任何一个表单自行分配; 必须将两个表中的行授予的权限组合在一起。
6.2.6 当特权更改生效时
当mysqld启动时,它将所有授权表内容读入内存。 内存表在此时对访问控制生效。
如果您使用帐户管理语句(如GRANT,REVOKE,SET PASSWORD或RENAME USER)间接修改授权表,则服务器会通知这些更改并立即再次将授权表加载到内存中。
如果直接使用INSERT,UPDATE或DELETE等语句修改授权表,则更改对特权检查没有影响,直到您重新启动服务器或指示它重新加载表。 如果您直接更改授权表,但忘记重新加载它们,则只有在重新启动服务器之后,更改才会生效。 这可能会让你想知道为什么你的改变似乎没有什么区别!
要通知服务器重新加载授权表,请执行刷新特权操作。 这可以通过发出FLUSH PRIVILEGES语句或执行mysqladmin flush-privileges或mysqladmin reload命令来完成。
授权表重新加载会影响每个现有客户端连接的权限,如下所示:
注意:
客户端应用程序可能缓存数据库名称 因此,如果不实际更改为不同的数据库,这种影响可能对他们不可见。
如果服务器使用--skip-grant-tables选项启动,则不会读取授权表或实施任何访问控制。 任何人都可以连接并做任何事情,这是不安全的。 要使服务器开始读取表并启用访问检查,请刷新权限。
6.2.7 连接到MYSQL 的故障诊断
如果在尝试连接到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)
shell> netstat -ln | grep mysql
要确定是否需要初始化授权表,请在数据目录下查找一个mysql目录。 (数据目录通常名为data或var,位于MySQL安装目录下。)确保在mysql数据库目录中有一个名为user.MYD的文件。 如果不是,则初始化数据目录。 这样做并启动服务器后,您应该能够连接到服务器。
shell> mysql -u root ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: NO)
这意味着在安装过程中已经分配了一个root密码,并且必须提供该密码。 请参阅第2.10.4节“保护初始MySQL帐户”,了解密码分配的不同方式,以及在某些情况下如何找到密码。 如果您需要重置根密码,请参阅B.5.3.2节“如何重置根密码”中的说明。 找到或重置密码后,使用--password(或-p)选项以root用户身份重新登录:
但是,如果您已使用mysqld --initialize-insecure初始化MySQL(参见第2.10.1.1节“使用mysqld手动初始化数据目录”获取详细信息),服务器将允许您以root用户身份进行连接,而无需使用密码。 这是一个安全风险,所以你应该为root帐户设置一个密码;
shell> mysql Client does not support authentication protocol requested by server; consider upgrading MySQL client
您可以通过使用--no-defaults选项调用客户端程序来禁止使用选项文件。 例如:
shell> mysqladmin --no-defaults -u root version
shell> mysqladmin -u root -pxxxx ver Access denied for user 'root'@'localhost' (using password: YES)
如果即使没有指定密码也会出现上述错误,这意味着您在某些选项文件中列出了不正确的密码。 按照上一项中的描述尝试使用--no-defaults选项。
您可以使用--host=127.0.0.1选项来显式命名服务器主机。 这将建立到本地mysqld服务器的TCP / IP连接。 您还可以通过指定使用本地主机的实际主机名的--host选项来使用TCP / IP。 在这种情况下,即使您在与服务器相同的主机上运行客户端程序,也必须在服务器主机上的user表行中指定主机名。
SELECT * FROM user;
结果应该包含一行,其中包含与您的客户端主机名和您的MySQL用户名匹配的主机和用户列。
shell> mysql Host ... is not allowed to connect to this MySQL server
您可以通过为尝试连接时使用的客户端主机名和用户名组合设置帐户来解决此问题。
如果您不知道要连接的计算机的IP地址或主机名,则应在用户表中将'%'作为主机列值。 在尝试从客户端机器连接后,使用SELECT USER()查询来查看您真正连接的方式。 然后将用户表行中的'%'更改为日志中显示的实际主机名。 否则,您的系统将处于不安全状态,因为它允许来自任何主机的给定用户名的连接。
在Linux上,可能发生此错误的另一个原因是您正在使用的版本不同的glibc库编译的二进制MySQL版本。 在这种情况下,您应该升级您的操作系统或glibc,或者下载MySQL版本的源代码分发并自行编译。 源RPM通常很容易编译和安装,所以这不是一个大问题。
shell> mysqladmin -u root -pxxxx -h some_hostname ver Access denied for user 'root'@'' (using password: YES)
如果您尝试以root身份进行连接并出现以下错误,则意味着您的用户列中没有包含'root'用户列值的行,并且该mysqld无法解析客户端的主机名:
Access denied for user ''@'unknown'
这些错误表明DNS问题。 要修复它,请执行mysqladmin flush-hosts以重置内部DNS主机缓存。
一些永久解决方案是:
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69946831/viewspace-3149554/ ,如需转载,请注明出处,否则将追究法律责任。