歡迎關注筆者的微信公衆號
本文是筆者基於MYSQL8.0官方手冊並結合自身實際經驗編寫,不足之處歡迎指出。編寫不易,歡迎署名轉載。
密碼認證組件
MYSQL是一個組件式架構,MYSQL 8.0+默認啓用了密碼校驗組件validate_password
,它強迫你設置很複雜的密碼以保證數據庫安全,對於個人來說,你不能設置如123456
這樣的密碼,當你在設置這樣的密碼的時候會報如下錯誤:
ERROR 1819 (HY000): Your password does not satisfy the current policy requirements
筆者認爲這對於個人學習而言非常礙事,解決方案有兩種:
-
修改密碼認證策略
-
卸載密碼認證組件
修改密碼認證策略
-
查看當前認證策略
mysql> SHOW VARIABLES LIKE 'validate_password.%';
這幾個認證策略就不一一解釋了,就是字面意思。
-
修改認證策略
-
設置密碼長度爲4個字符
mysql> SET GLOBAL validate_password_length = 4;
-
設置認證策略低級
SET GLOBAL validate_password_policy=LOW;
其他的參數照着改就行,最後最好重啓一下服務器。
-
卸載密碼認證組件
筆者覺得MYSQL 8.0+默認的密碼認證策略非常礙事,所以直接卸載掉了;
-
卸載密碼認證組件
UNINSTALL COMPONENT 'file://component_validate_password';
-
安裝密碼認證組件
INSTALL COMPONENT 'file://component_validate_password';
密碼加密方式
問題
在MYSQL 8.0中包含了一下三種加密插件
-
caching_sha2_password
-
sha256_password
-
mysql_native_password
與mysql_native_password
插件相比,caching_sha2_password
和sha256_password
身份驗證插件提供了更安全的密碼加密。且caching_sha2_password
在性能上優於sha256_password
,因此在MYSQL 8.0中caching_sha2_password
成爲了默認的加密插件。
-
修改MYSQL配置文件:
/etc/my.cnf
vim /etc/my.cnf ... [mysqld] default_authentication_plugin=mysql_native_password ...
-
也可以在服務器啓動時添加命令行參數
--default-authentication-plugin=mysql_native_password 加上 --initialize選項 或者 --initialize-insecure選項
這種方法是爲了兼容那些舊版本客戶端能連接到新版本的MYSQL服務器,最好的做法是不要更改服務端參數,而是升級客戶端,對於Java程序員來說,升級MYSQL驅動到8.0.9或更高的版本。
修改現有賬戶的加密方式
在MYSQL安裝的時候默認創建了'root'@'localhost'
賬號,因此只要修改了賬戶的加密方式舊版的客戶端就可以通過此賬號密碼連接數據庫。
ALTER USER 'root'@'localhost' IDENTIFIED WITH mysql_native_password BY 'password';
訪問控制和賬戶管理
用戶名和密碼
默認情況下,MYSQL將用戶名和連接主機信息存儲在user
表,位於mysql
系統數據庫中。
注意
在MYSQL中,用戶名相同,連接主機不同被認爲是兩個賬號。
%
表示這個賬號可從任何IP地址連接到服務器端。
默認情況下MYSQL只創建了'root'@'localhost'
這個賬號,這隻能從本地連接到數據庫,如果要遠程連接需要創建新的用戶,連接主機IP設爲%
,筆者這裏創建了repl
和root
兩個可從遠程連接的賬戶。
下面是MYSQL關於用戶名和賬戶的一些注意事項:
-
MYSQL的用戶名最大長度限制爲32字符長度,這是一種硬編碼,無法更改。
-
MYSQL的賬戶密碼通過加密插件加密後存儲在
user
表中。 -
如果用戶名和密碼使用了非ASCII字符,那麼可以在連接時通過
MYSQL_SET_CHARSET_NAME
選項指定。 -
MYSQL不支持
ucs2
,utf16
,utf32
,這三種字符集。 -
如果在命令行中通過
--password
或-p
選項指定密碼,那麼密碼與選項之間不允許有空白。$ mysql -u finley -p db_name
以上
db_name
會認爲是連接的數據庫名而不是密碼。$ mysql -u finley -ppassword db_name
MYSQL特權
官方文檔](https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html)
MYSQL中的特權是MYSQL用來判斷賬戶是否具有能執行某種操作的憑證。
MYSQL中特權分三類:
-
管理員特權,這是全局特權,可以操作整個服務器資源,
root
用戶就是管理員特權,可以操作所有資源 -
數據庫特權,適用於數據庫及其中的所有對象。可以爲特定數據庫或全局授予這些特權,以便將它們應用於所有數據庫。
-
可以爲數據庫中的特定對象,數據庫中給定類型的所有對象(例如數據庫中的所有表)或所有對象的全局對象授予表,索引,視圖和存儲例程等數據庫對象的特權。所有數據庫中給定類型的對象。
特權分類
MYSQL特權分爲靜態特權和動態特權,特權是靜態的(內置於服務器中)還是動態的(在運行時定義)對MYSQL行爲有不同的影響。特權是靜態還是動態會影響將其授予用戶帳戶和角色的可用性。
MYSQL的特權信息存儲在mysql
數據庫中,具體的數據表下一章會介紹。
靜態特權
特權 | 表中字段 | 上下文 |
---|---|---|
ALL PRIVILEGES |
Synonym for “all privileges” | Server administration |
ALTER |
Alter_priv |
Tables |
ALTER ROUTINE |
Alter_routine_priv |
Stored routines |
CREATE |
Create_priv |
Databases, tables, or indexes |
CREATE ROLE |
Create_role_priv |
Server administration |
CREATE ROUTINE |
Create_routine_priv |
Stored routines |
CREATE TABLESPACE |
Create_tablespace_priv |
Server administration |
CREATE TEMPORARY TABLES |
Create_tmp_table_priv |
Tables |
CREATE USER |
Create_user_priv |
Server administration |
CREATE VIEW |
Create_view_priv |
Views |
DELETE |
Delete_priv |
Tables |
DROP |
Drop_priv |
Databases, tables, or views |
DROP ROLE |
Drop_role_priv |
Server administration |
EVENT |
Event_priv |
Databases |
EXECUTE |
Execute_priv |
Stored routines |
FILE |
File_priv |
File access on server host |
GRANT OPTION |
Grant_priv |
Databases, tables, or stored routines |
INDEX |
Index_priv |
Tables |
INSERT |
Insert_priv |
Tables or columns |
LOCK TABLES |
Lock_tables_priv |
Databases |
PROCESS |
Process_priv |
Server administration |
PROXY |
See proxies_priv table |
Server administration |
REFERENCES |
References_priv |
Databases or tables |
RELOAD |
Reload_priv |
Server administration |
REPLICATION CLIENT |
Repl_client_priv |
Server administration |
REPLICATION SLAVE |
Repl_slave_priv |
Server administration |
SELECT |
Select_priv |
Tables or columns |
SHOW DATABASES |
Show_db_priv |
Server administration |
SHOW VIEW |
Show_view_priv |
Views |
SHUTDOWN |
Shutdown_priv |
Server administration |
SUPER |
Super_priv |
Server administration |
TRIGGER |
Trigger_priv |
Tables |
UPDATE |
Update_priv |
Tables or columns |
USAGE |
Synonym for "no privileges" |
Server administration |
動態特權
特權 | 上下文 |
---|---|
APPLICATION_PASSWORD_ADMIN |
Dual password administration |
AUDIT_ADMIN |
Audit log administration |
BACKUP_ADMIN |
Backup administration |
BINLOG_ADMIN |
Backup and Replication administration |
BINLOG_ENCRYPTION_ADMIN |
Backup and Replication administration |
CLONE_ADMIN |
Clone administration |
CONNECTION_ADMIN |
Server administration |
ENCRYPTION_KEY_ADMIN |
Server administration |
FIREWALL_ADMIN |
Firewall administration |
FIREWALL_USER |
Firewall administration |
GROUP_REPLICATION_ADMIN |
Replication administration |
INNODB_REDO_LOG_ARCHIVE |
Redo log archiving administration |
NDB_STORED_USER |
NDB Cluster |
PERSIST_RO_VARIABLES_ADMIN |
Server administration |
REPLICATION_APPLIER |
PRIVILEGE_CHECKS_USER for a replication channel |
REPLICATION_SLAVE_ADMIN |
Replication administration |
RESOURCE_GROUP_ADMIN |
Resource group administration |
RESOURCE_GROUP_USER |
Resource group administration |
ROLE_ADMIN |
Server administration |
SESSION_VARIABLES_ADMIN |
Server administration |
SET_USER_ID |
Server administration |
SHOW_ROUTINE |
Server administration |
SYSTEM_USER |
Server administration |
SYSTEM_VARIABLES_ADMIN |
Server administration |
TABLE_ENCRYPTION_ADMIN |
Server administration |
VERSION_TOKEN_ADMIN |
Server administration |
XA_RECOVER_ADMIN |
Server administration |
有關特權的具體解釋請參看官方文檔,這裏就不一一解釋了,大部分特權正常情況下都不會用到的,用到時再參看官方解釋。
授權表
mysql
系統數據庫中包含了幾個授權表,這些表包含有關用戶帳戶及其所擁有特權的信息。
注意
堅決禁止使用INSERT
,UPDATE
,DELETE
語句直接修改這些表,這會有極大地風險,推薦使用CREATE USER
,GRANT
,REVOKE
語句操作權限。
授權表預覽
以下授權表全部位於mysql
數據庫中
user
: 用戶帳戶,靜態全局特權和其他非特權字段。global_grants
: 動態全局特權。db
: 數據庫級特權。tables_priv
: 表級特權。columns_priv
: 字段級特權。procs_priv
: 存儲過程和函數特權。proxies_priv
: 代理用戶權限。default_roles
: 默認用戶角色。role_edges
: Edges for role subgraphs.password_history
: 密碼更改歷史記錄。
在MySQL 8.0中,授權表使用InnoDB
存儲引擎並且是事務性的。在MySQL 8.0之前,授權表使用MyISAM
存儲引擎,並且是非事務性的。授予表存儲引擎的這一更改使帳戶管理語句(例如CREATE USER或GRANT)的行爲也可以隨之更改。以前,命名多個用戶的帳戶管理語句可能對某些用戶成功而對其他用戶失敗。現在,每個語句都是事務性的,並且對於所有指定的用戶都成功,或者回滾,如果發生任何錯誤,則無效。
每個授權表都包含作用域列和特權列:
- 範圍列確定表中每一行的範圍;也就是說,該行適用的上下文。例如,具有“主機”和“用戶”值分別爲“ h1.example.net”和“ bob”的用戶錶行適用於驗證指定了bob用戶名的客戶端從主機h1.example.net與服務器建立的連接。同樣,當bob從主機h1.example.net連接以訪問報告數據庫時,將應用具有Host,User和Db列值分別爲’h1.example.net’,'bob’和’reports’的db錶行。table_priv和columns_priv表包含範圍列,這些範圍列指示每行適用的表或表/列組合。procs_priv作用域列指示每行適用的存儲例程。
- 特權列指示錶行授予哪些特權;也就是說,它允許執行哪些操作。服務器將各種授權表中的信息組合在一起,以形成用戶權限的完整描述。
user
和 db
授權表
Table Name | user |
db |
---|---|---|
Scope columns | Host |
Host |
User |
Db |
|
User |
||
特權字段 | Select_priv |
Select_priv |
Insert_priv |
Insert_priv |
|
Update_priv |
Update_priv |
|
Delete_priv |
Delete_priv |
|
Index_priv |
Index_priv |
|
Alter_priv |
Alter_priv |
|
Create_priv |
Create_priv |
|
Drop_priv |
Drop_priv |
|
Grant_priv |
Grant_priv |
|
Create_view_priv |
Create_view_priv |
|
Show_view_priv |
Show_view_priv |
|
Create_routine_priv |
Create_routine_priv |
|
Alter_routine_priv |
Alter_routine_priv |
|
Execute_priv |
Execute_priv |
|
Trigger_priv |
Trigger_priv |
|
Event_priv |
Event_priv |
|
Create_tmp_table_priv |
Create_tmp_table_priv |
|
Lock_tables_priv |
Lock_tables_priv |
|
References_priv |
References_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 |
||
Create_role_priv |
||
Drop_role_priv |
||
安全字段 | ssl_type |
|
ssl_cipher |
||
x509_issuer |
||
x509_subject |
||
plugin |
||
authentication_string |
||
password_expired |
||
password_last_changed |
||
password_lifetime |
||
account_locked |
||
Password_reuse_history |
||
Password_reuse_time |
||
Password_require_current |
||
User_attributes |
||
資源控制字段 | max_questions |
|
max_updates |
||
max_connections |
||
max_user_connections |
訪問控制
階段一:連接驗證
當客戶端嘗試連接到MySQL服務器時,服務器根據以下條件接受或拒絕連接:
- 提供的的身份憑據以及是否可以通過提供正確的密碼來驗證身份
- 賬戶是否已被鎖定
服務器首先檢查憑據,然後檢查帳戶鎖定狀態。任一步驟失敗都會導致服務器完全拒絕您的訪問。否則,服務器將接受連接,然後進入階段二並等待請求。
憑據校驗主要是通過校驗user
表中的user
,host
,authentication_string
三個字段,賬戶鎖定狀態存儲在user
表中account_locked
字段中。
使用三個用戶表作用域列(主機,用戶和認證字符串)執行憑據檢查。鎖定狀態記錄在用戶表account_locked列中。僅當某些用戶錶行中的“主機”和“用戶”列與客戶端主機名和用戶名匹配,客戶端提供該行中指定的密碼且account_locked值爲“ N”時,服務器才接受連接。僅當某些用戶錶行中的user
和host
字段與客戶端主機名和用戶名匹配,客戶端提供該行中指定的密碼且account_locked
值爲N
時,服務器才接受連接。
客戶端身份基於以下兩個信息:
- 客戶端主機,即域名或IP地址
- 用戶名,例如:
root
如果user
列的值是非空白的,則傳入連接中的用戶名必須完全匹配。如果user
值爲空白,則它與任何用戶名匹配。如果與傳入連接匹配的用戶錶行的用戶名爲空,則該用戶將被視爲沒有名稱的匿名用戶,而不是具有客戶端實際指定的名稱的用戶。這意味着在連接持續時間內(即在第2階段),將使用空白用戶名進行所有進一步的訪問檢查。
user
表中的非空白authentication_string
值表示加密的密碼。MySQL不會將密碼存儲爲明文,任何人都可以看到。而是將嘗試連接的用戶提供的密碼進行加密(使用由帳戶身份驗證插件實現的密碼哈希方法)。然後在檢查密碼是否正確時在連接過程中使用加密的密碼。這樣就不會在連接上傳輸加密密碼。
mysql> select user, authentication_string from user where user = 'root' and host = '%';
+------+--------------------------------------------------------------------------------------------- ---------+
| user | authentication_string |
+------+-------------------------------------------------------------------------------------------------------+
| root | $A$005$%V,~wy({Nt{pt)1k?I,g6lMrrsej3UC5taF4V6/clkpGaYogHf7FIzwgijo3h9 |
+------+-------------------------------------------------------------------------------------------------------+
1 row in set (0.00 sec)
警告
從MySQL的角度來看,加密密碼是真實密碼,因此您絕不應該允許任何人訪問它。特別是,請勿授予非管理用戶對
mysql
系統數據庫中表的讀取權限。
下表顯示了user
表中user
和host
的各種組合如何應用於傳入連接:
user |
host |
Permissible Connections |
---|---|---|
'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.* 域名連接 |
'fred' |
'198.51.100.177' |
fred 只能從IP地址 198.51.100.177 連接服務器 |
'fred' |
'198.51.100.%' |
fred 可以從198.51.100.* 網段連接 |
'fred' |
'198.51.100.0/255.255.255.0' |
等同於上一行 |
當客戶端傳入的身份憑據匹配多個條目時服務器必須確定要使用哪個匹配項。它可以按以下方式解決此問題:
- 每當服務器將用戶表讀入內存時,它都會對行進行排序。
- 客戶端嘗試連接時,服務器將按排序順序瀏覽各行。
- 服務器使用與客戶端主機名和用戶名匹配的第一行。
服務器使用排序規則,該規則首先對具有最特定的主機值的行進行排序。主機名和IP地址是最具體的。(原義IP地址的特異性不受其是否具有網絡掩碼的影響,因此198.51.100.13
和198.51.100.0/255.255.255.0
被認爲是同等具體的。)模式'%'
表示“任何主機”,並且最少具體。空字符串""也表示“任何主機”,但在%
之後排序。具有相同“主機”值的行將按照最特定的user
值進行排序(空白的user
值表示“任何用戶”,並且是最不特定的)。對於具有相同特定的user
和host
值的行,順序是不確定的。
要查看其工作原理,假設user
表如下所示:
+---------------+----------+-
| Host | User | ...
+---------------+----------+-
| % | root | ...
| % | jeffrey | ...
| localhost | root | ...
| localhost | | ...
+------------ --+----------+-
當服務器將表讀入內存時,它將使用剛剛描述的規則對行進行排序。排序後的結果如下所示:
當客戶端嘗試連接時,服務器將瀏覽已排序的行並使用找到的第一個匹配項。對於jeffrey
與localhost
的連接,表中的兩行匹配:host
和user
值爲'localhost'
和''
的那一行,以及host
和user
值爲'%'
和'jeffrey'
的那一行。localhost
行首先以排序順序出現,因此服務器會使用這行。
這是另一個例子。假設用戶表如下所示:
排序後的結果如下:
第一行匹配來自h1.example.net
的jeffrey
連接,第二行匹配來自任何主機的jeffrey
連接。
提示
常見的誤解是認爲,當客戶端從某個主機以用戶名密碼登錄時服務器會完全匹配其信息,這是不對的。前面的示例對此進行了說明,其中
jeffrey
與h1.example.net
的連接首先不匹配包含jeffrey
作爲user
字段值的行,而是不匹配用戶名的行。結果,即使jeffrey
在連接時指定了用戶名,他仍被認證爲匿名用戶。
如果您能夠連接到服務器,但是特權不是您所期望的,則您可能已通過其他帳戶的身份驗證。要找出服務器用來驗證您身份的帳戶,請使用CURRENT_USER()
函數。此函數會返回user_name@host_name
格式返回一個值,該值指示匹配的用戶錶行中的user
和host
值。
階段二:請求驗證
建立連接後,服務器進入訪問控制的第二階段。對於通過該連接發出的每個請求,服務器將確定要執行的操作,然後檢查是否具有足夠的特權。這是授權表中的特權列起作用的地方。這些特權可以來自任何用戶,global_grants
,db
,tables_priv
,columns_priv
或procs_priv
表。
user
和global_grants
表授予全局特權。這些表中給定帳戶的行表示無論默認數據庫是什麼,在全局基礎上應用的帳戶特權。例如,如果用戶表授予您DELETE
特權,則可以從服務器主機上任何數據庫中的任何表中刪除行。明智的做法是僅將需要特權的用戶授予用戶表中的特權,例如數據庫管理員。對於其他用戶,請將用戶表中的所有特權都設置爲N
,並僅在更特定的級別(對於特定的數據庫,表,列或例程)授予特權。也可以全局授予數據庫特權,但可以撤銷部分權限來限制它們在特定數據庫上的執行。
db
表授予特定於數據庫的特權。該表的scope
列中的值可以採用以下形式:
- 空的用戶值與匿名用戶匹配。非空值從字面上匹配;用戶名中沒有通配符。
- 通配符
%
和可以在host
和db
列中使用。這些具有與使用LIKE
運算符執行的模式匹配操作相同的含義。如果要在授予特權時按字面使用任何一個字符,則必須使用反斜槓將其轉義。例如,要將下劃線字符(_)包括在數據庫名稱中,請在GRANT
語句中將其指定爲\_
。 %
或空白host
值表示“任何主機”。%
或空白db
值表示“任何數據庫”。
服務器將數據庫表讀入內存,並在讀取user
表的同時對其進行排序。服務器根據host
,db
和user
列對數據庫表進行排序。與用戶表一樣,排序將最具體的值放在最前面,最不具體的值放在最後,當服務器尋找匹配的行時,它將使用找到的第一個匹配項。
服務器使用排序的表來驗證它收到的每個請求。對於需要管理特權(例如SHUTDOWN
或RELOAD
)的請求,服務器僅檢查user
和global_privilege
表,因爲它們是唯一指定管理特權的表。如果這些表中帳戶的行允許請求的操作,則服務器將授予訪問權限,否則拒絕訪問。例如,如果您想執行mysqladmin shutdown
,但是您的用戶錶行未授予您SHUTDOWN
特權,則服務器將拒絕訪問,甚至不檢查db
表。(後一個表不包含Shutdown_priv
列,因此無需檢查它。)
對於與數據庫相關的請求(INSERT
,UPDATE
等),服務器首先在user
錶行中檢查用戶的全局特權(減去部分撤銷所施加的任何特權限制)。如果該行允許請求的操作,則授予訪問權限。如果用戶表中的全局特權不足,則服務器從數據庫表中確定用戶的數據庫特定特權:
服務器在db
表中查找host
,db
和user
列上的匹配項。host
和user
列與連接用戶的主機名和MySQL用戶名匹配。db
列與用戶要訪問的數據庫匹配。如果主機和用戶沒有行,則拒絕訪問。
在確定db
錶行授予的特定於數據庫的特權之後,服務器會將它們添加到user
表授予的全局特權中。如果結果允許請求的操作,則授予訪問權限。否則,服務器會先檢查tables_priv和columns_priv
表中用戶的表特權和列特權,將這些特權添加到用戶特權中,然後根據結果允許或拒絕訪問。對於存儲過程操作,服務器使用procs_priv
表而不是table_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
特權。您的特權可能是這樣的:user
錶行授予一個全局特權,而數據庫錶行授予另一個特權,專門針對相關數據庫。在這種情況下,您具有執行請求所必需的特權,但是服務器無法僅從全局特權或數據庫特權中分辨出這一點。它必須根據組合的權限做出訪問控制決定。
賬戶管理
創建賬戶並授權
CREATE USER 'finley'@'localhost'
IDENTIFIED BY 'password';
GRANT ALL
ON *.*
TO 'finley'@'localhost'
WITH GRANT OPTION;
CREATE USER 'finley'@'%.example.com'
IDENTIFIED BY 'password';
GRANT ALL
ON *.*
TO 'finley'@'%.example.com'
WITH GRANT OPTION;
CREATE USER 'admin'@'localhost'
IDENTIFIED BY 'password';
GRANT RELOAD,PROCESS
ON *.*
TO 'admin'@'localhost';
CREATE USER 'dummy'@'localhost';
以上四組例子創建了四個賬戶,他們分別具有如下屬性:
-
前兩個帳戶的用戶名均爲finley。兩者都是具有執行任何操作的完整全局特權的超級用戶帳戶。僅當從本地主機連接時,才能使用
'finley' @'localhost'
帳戶。'finley'@'%.example.com'
帳戶在主機部分使用%
通配符,因此可用於從example.com
域名下的任何主機進行連接。如果有本地主機的匿名用戶帳戶,則必須使用
'finley'@'localhost'
帳戶。如果沒有'finley'@'localhost'
帳戶,則當finley從本地主機進行連接並且finley
被視爲匿名用戶時,該匿名用戶帳戶將具有優先權。原因是匿名用戶帳戶的主機列值比'finley'@'%'
帳戶更具體,因此在用戶表排序順序中排在更早的位置。 -
'admin' @'localhost'
帳戶只能由admin
用來從本地主機進行連接。它被授予全局RELOAD
和PROCESS
管理特權。這些特權使admin
用戶可以執行mysqladmin reload
,mysqladmin refresh
和mysqladmin flush-xxx
命令以及mysqladmin processlist
。不授予訪問任何數據庫的特權。您可以使用GRANT
語句添加此類特權。 -
'dummy'@'localhost'
帳戶沒有密碼(不安全,不建議使用)。該帳戶只能用於從本地主機連接。這條語句沒有授予任何特權,您假定您將使用GRANT
語句授予該帳戶特定的特權。
以上的授權語句中的權限名可在之前的MYSQL特權找到。
前面的示例在全局級別授予特權。下一個示例創建三個帳戶,並授予他們較低級別的訪問權限;即針對特定數據庫或數據庫中的對象。每個帳戶都有一個自定義用戶名,但是主機名部分有所不同:
CREATE USER 'custom'@'localhost'
IDENTIFIED BY 'password';
GRANT ALL
ON bankaccount.*
TO 'custom'@'localhost';
CREATE USER 'custom'@'host47.example.com'
IDENTIFIED BY 'password';
GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP
ON expenses.*
TO 'custom'@'host47.example.com';
CREATE USER 'custom'@'%.example.com'
IDENTIFIED BY 'password';
GRANT SELECT,INSERT,UPDATE,DELETE,CREATE,DROP
ON customer.addresses
TO 'custom'@'%.example.com';
這三個帳戶適合在以下情況使用:
-
'custom'@'localhost'
帳戶具有訪問bankaccount
數據庫的所有數據庫級特權。該帳戶只能用於從本地主機連接到服務器。 -
'custom'@'host47.example.com'
帳戶具有訪問expenses
數據庫的特定數據庫級別特權。該帳戶只能用於從主機host47.example.com
連接到服務器。 -
'custom'@'%.example.com'
帳戶具有特定的表級特權,可以從example.com
域名下的任何主機訪問客戶數據庫中的地址表。由於在帳戶名稱的主機部分中使用%
通配符,因此該帳戶可用於從此域名中的所有計算機連接到服務器。
檢查帳戶特權和屬性
使用SHOW GRANTS
查看帳戶的特權:
mysql> SHOW GRANTS FOR 'admin'@'localhost';
+----------------------------------------------------------------------------+
| Grants for admin@localhost |
+----------------------------------------------------------------------------+
| GRANT RELOAD, PROCESS ON *.* TO 'admin'@'localhost' |
+----------------------------------------------------------------------------+
使用SHOW CREATE USER
查看帳戶的非特權屬性:
mysql> SET print_identified_with_as_hex = ON; ''' 賬戶信息以十六進制打印,保護隱私
mysql> SHOW CREATE USER 'admin'@'localhost'\G
*************************** 1. row ***************************
CREATE USER for admin@localhost: CREATE USER 'admin'@'localhost'
IDENTIFIED WITH 'caching_sha2_password'
AS 0x24412430303524301D0E17054E2241362B1419313C3E44326F294133734B30792F436E77764270373039612E32445250786D43594F45354532324B6169794F47457852796E32
REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK
PASSWORD HISTORY DEFAULT
PASSWORD REUSE INTERVAL DEFAULT
PASSWORD REQUIRE CURRENT DEFAULT
啓用print_identified_with_as_hex系統變量(自MySQL 8.0.17起可用)會導致SHOW CREATE USER以十六進制字符串而非常規字符串文字形式顯示包含不可打印字符的哈希值。
撤銷帳戶特權
要撤消帳戶特權,請使用REVOKE
語句。可以在不同級別撤銷特權,就像可以在不同級別授予特權一樣。
撤銷全局特權:
REVOKE ALL
ON *.*
FROM 'finley'@'%.example.com';
REVOKE RELOAD
ON *.*
FROM 'admin'@'localhost';
撤消數據庫級特權:
REVOKE CREATE,DROP
ON expenses.*
FROM 'custom'@'host47.example.com';
撤消表級特權:
REVOKE INSERT,UPDATE,DELETE
ON customer.addresses
FROM 'custom'@'%.example.com';
要檢查特權撤銷的效果,請使用SHOW GRANTS
:
mysql> SHOW GRANTS FOR 'admin'@'localhost';
+----------------------------------------------------------------+
| Grants for admin@localhost |
+----------------------------------------------------------------+
| GRANT PROCESS ON *.* TO 'admin'@'localhost' |
+----------------------------------------------------------------+
刪除帳戶
要刪除帳戶,請使用DROP USER
語句。例如,刪除一些先前創建的帳戶:
DROP USER 'finley'@'localhost';
DROP USER 'finley'@'%.example.com';
DROP USER 'admin'@'localhost';
DROP USER 'dummy'@'localhost';
保留賬戶
在數據目錄初始化期間,MySQL創建應被視爲保留的用戶帳戶。
當剛安裝完MYSQL後,會創建以下用戶:
-
'root'@'localhost
:用於管理目的。該帳戶具有所有特權,是系統帳戶,並且可以執行任何操作。嚴格來說,此帳戶名不是保留的,在某種意義上說,某些安裝會將根帳戶重命名爲其他名稱,以避免暴露具有衆所周知名稱的高特權帳戶。
-
'mysql.sys'@'localhost'
:用作sys
模式對象的DEFINER
。使用mysql.sys
帳戶可避免DBA
重命名或刪除根帳戶時發生的問題。此帳戶已鎖定,因此不能用於客戶端連接。 -
'mysql.session'@'localhost'
:插件在內部用於訪問服務器。此帳戶已鎖定,因此不能用於客戶端連接。該帳戶是系統帳戶。 -
'mysql.infoschema'@'localhost'
:用作INFORMATION_SCHEMA
視圖的DEFINER
。mysql.infoschema
帳戶的使用避免了DBA重命名或刪除根帳戶時發生的問題。此帳戶已鎖定,因此不能用於客戶端連接。
分配賬戶密碼
MySQL將憑據存儲在mysql
系統數據庫的user
表中。分配或修改密碼的操作僅允許具有CREATE USER
特權或mysql
數據庫特權(創建新帳戶的INSERT
特權,修改現有帳戶的UPDATE
特權)的用戶使用。如果啓用了read_only
系統變量,則使用帳戶修改語句(例如CREATE USER
或ALTER USER
)還需要CONNECTION_ADMIN
特權(或不建議使用的SUPER
特權)。
此處的討論僅彙總了最常見的密碼分配語句的語法。
創建用戶
要在創建新帳戶時分配密碼,請使用CREATE USER
幷包含IDENTIFIED BY
子句:
CREATE USER 'jeffrey'@'localhost' IDENTIFIED BY 'password';
CREATE USER
還支持用於指定帳戶身份驗證插件的語法。具體參看官方文檔。
修改密碼
要爲現有帳戶分配或更改密碼,請使用帶有IDENTIFIED BY
子句的ALTER USER
語句:
ALTER USER 'jeffrey'@'localhost' IDENTIFIED BY 'password';
如果不是以匿名用戶身份連接的,則可以更改自己的密碼,而無需直接指定自己的帳戶:
ALTER USER USER() IDENTIFIED BY 'password';
要從命令行更改帳戶密碼,請使用mysqladmin
命令:
mysqladmin -u user_name -h host_name password "password"
此命令設置密碼的帳戶是mysql.user
系統表中帶有一行的帳戶,該行與user
字段中的user_name和host
列中連接的客戶端主機相匹配。
警告使用
mysqladmin
設置密碼應該被認爲是不安全的。在某些系統上,密碼對系統狀態程序(例如ps
命令)可見,其他用戶可能會調用該密碼來顯示命令行。MySQL客戶端通常在初始化序列期間用零覆蓋命令行密碼參數。但是,仍然有一個短暫的時間間隔,在該時間間隔內該值是可見的。同樣,在某些系統上,此覆蓋策略無效,並且ps
仍然可以看到密碼。(SystemV Unix
系統以及其他系統可能會遇到此問題。)
密碼管理
MySQL支持以下密碼管理功能:
- 密碼到期,要求定期更改密碼。
- 密碼重用限制,以防止再次選擇舊密碼。
- 密碼驗證,要求更改密碼還指定要替換的當前密碼。
- 雙密碼,使客戶端可以使用主密碼或輔助密碼進行連接。
- 密碼強度評估,要求使用強密碼。
- 隨機密碼生成,作爲要求管理員指定明確的文字密碼的替代方法。
- 密碼失敗跟蹤,用於在連續多次錯誤密碼登錄失敗後啓用臨時帳戶鎖定。
以下各節描述了這些功能,但密碼強度評估功能除外,後者是使用validate_password
組件實現的,在這之前筆者已將這個組件卸載了。
重要MySQL使用mysql系統數據庫中的表實現密碼管理功能。如果從早期版本升級MySQL,則系統表可能不是最新的。在這種情況下,服務器會在啓動過程中將類似於以下消息的消息寫入錯誤日誌(確切的數目可能有所不同):
[ERROR] Column count of mysql.user is wrong. Expected 49, found 47. The table is probably corrupted [Warning] ACL table mysql.password_history missing. Some operations may fail.
要解決此問題,請執行MySQL升級過程。請參見升級MySQL。在此之前,無法更改密碼。
內部與外部憑證存儲
一些身份驗證插件在mysql.user
系統表中將帳戶憑據存儲在MySQL內部:
mysql_native_password
caching_sha2_password
sha256_password
本節中的大多數討論都適用於此類身份驗證插件,因爲此處描述的大多數密碼管理功能都是基於MySQL本身處理的內部憑據存儲。其他身份驗證插件將帳戶憑據存儲在MySQL的外部。對於使用插件針對外部憑據系統執行身份驗證的帳戶,密碼管理也必須在外部針對該系統進行處理。
唯一的例外是登錄失敗跟蹤和臨時帳戶鎖定的選項適用於所有帳戶,而不僅限於使用內部憑據存儲的帳戶,因爲MySQL能夠評估任何帳戶的登錄嘗試狀態,無論它是使用內部帳戶還是內部帳戶。外部憑證存儲。
密碼過期策略
MySQL使數據庫管理員可以手動使帳戶密碼過期,並建立自動密碼過期策略。可以在全局範圍內建立到期策略,並且可以將各個帳戶設置爲遵從全局策略,或者以特定的每個帳戶行爲覆蓋全局策略。
要手動使帳戶密碼失效,請使用ALTER USER
語句:
ALTER USER 'jeffrey'@'localhost' PASSWORD EXPIRE;
該操作在mysql.user
系統表的相應行中標記密碼已過期。
根據策略,密碼有效期是自動的,並且基於密碼有效期,對於給定帳戶,將從最近一次密碼更改的日期和時間開始對其進行評估。mysql.user
系統表爲每個帳戶指示上次更改密碼的時間,如果服務器的使用期限大於允許的使用期限,則服務器會在客戶端連接時自動將其視爲過期密碼。此方法無需明確的手動密碼有效期。
要全局建立自動密碼過期策略,請使用default_password_lifetime
系統變量。其默認值爲0,這將禁用自動密碼過期。如果default_password_lifetime
的值爲正整數N
,則表示允許的密碼生存期,因此必須每N
天更改一次密碼。
示例:
-
要建立密碼有效期約爲六個月的全局策略,請在服務器
my.cnf
文件中的以下行啓動服務器:[mysqld] default_password_lifetime=180
-
要建立密碼永不過期的全局策略,請將default_password_lifetime設置爲0:
[mysqld] default_password_lifetime=0
-
還可以在運行時設置
default_password_lifetime
並將其持久化:SET PERSIST default_password_lifetime = 180; SET PERSIST default_password_lifetime = 0;
SET PERSIST
設置正在運行的MySQL
實例的值。它還保存該值以繼續進行後續服務器重新啓動。要更改正在運行的MySQL實例的值而不使它繼續進行後續的重新啓動,請使用GLOBAL
關鍵字而不是PERSIST
。
全局密碼過期策略適用於尚未設置爲覆蓋該策略的所有帳戶。要爲單個帳戶建立策略,請使用CREATE USER
和ALTER USER
語句的PASSWORD EXPIRE
選項。
一個對賬單示例
-
要求每90天更改一次密碼:
CREATE USER 'jeffrey'@'localhost' PASSWORD EXPIRE INTERVAL 90 DAY; ALTER USER 'jeffrey'@'localhost' PASSWORD EXPIRE INTERVAL 90 DAY;
該到期選項將覆蓋該語句命名的所有帳戶的全局策略。
-
禁用密碼有效期:
CREATE USER 'jeffrey'@'localhost' PASSWORD EXPIRE NEVER; ALTER USER 'jeffrey'@'localhost' PASSWORD EXPIRE NEVER;
該到期選項將覆蓋該語句命名的所有帳戶的全局策略。
-
對語句命名的所有帳戶遵循全局到期策略:
CREATE USER 'jeffrey'@'localhost' PASSWORD EXPIRE DEFAULT; ALTER USER 'jeffrey'@'localhost' PASSWORD EXPIRE DEFAULT;
客戶端成功連接後,服務器將確定帳戶密碼是否已過期:
- 服務器檢查密碼是否已手動過期。
- 否則,服務器會根據自動密碼過期策略檢查密碼使用期限是否大於其允許的使用期限。如果是這樣,則服務器認爲密碼已過期。
如果密碼已過期(無論是手動還是自動),則服務器將斷開客戶端連接或限制其允許的操作。受限制的客戶端執行的操作會導致錯誤,直到用戶建立新的帳戶密碼爲止:
mysql> SELECT 1;
ERROR 1820 (HY000): You must reset your password using ALTER USER
statement before executing this statement.
mysql> ALTER USER USER() IDENTIFIED BY 'password';
Query OK, 0 rows affected (0.01 sec)
mysql> SELECT 1;
+---+
| 1 |
+---+
| 1 |
+---+
1 row in set (0.00 sec)
客戶端重置密碼後,服務器將恢復會話以及使用該帳戶的後續連接的正常訪問。管理員用戶也可以重設帳戶密碼,但是該帳戶的所有現有受限會話仍然受到限制。使用該帳戶的客戶端必須斷開連接並重新連接,然後語句才能成功執行。
提示
注意儘管可以通過將過期的密碼設置爲當前值來“重置”它,但出於良好的政策考慮,最好選擇其他密碼。DBA可以通過建立適當的密碼重用策略來強制不重用。請參閱密碼重用策略。
密碼重用策略
MySQL允許對重用以前的密碼進行限制。可以根據密碼更改的次數,經過的時間或同時基於兩者來建立重用限制。可以在全局範圍內建立重用策略,並且可以將各個帳戶設置爲遵從全局策略,或者使用特定的按帳戶行爲替換全局策略。
帳戶的密碼歷史記錄由過去分配的密碼組成。MySQL可以限制從以下歷史記錄中選擇新密碼:
-
如果根據密碼更改次數限制了帳戶,則無法從指定數量的最新密碼中選擇新密碼。例如,如果密碼更改的最小次數設置爲3,則新密碼不能與最新的3個密碼相同。
-
如果根據時間限制了帳戶,則無法從歷史記錄中指定天數以上的密碼中選擇新密碼。例如,如果密碼重用間隔設置爲60,則新密碼一定不能屬於過去60天內先前選擇的密碼。
提示
空密碼不計入密碼歷史記錄,並且隨時可以重複使用。
要全局建立密碼重用策略,請使用password_history
和password_reuse_interval
系統變量。
示例
-
要禁止重複使用最後6個密碼或比365天新的密碼,請在服務器my.cnf文件中添加以下行:
[mysqld] password_history=6 password_reuse_interval=365
-
要在運行時設置和保留變量,請使用如下語句:
SET PERSIST password_history = 6; SET PERSIST password_reuse_interval = 365;
SET PERSIST
設置正在運行的MySQL實例的值。它還保存該值以繼續進行後續服務器重新啓動。要更改正在運行的MySQL實例的值而不使它繼續進行後續的重新啓動,請使用GLOBAL
關鍵字而不是PERSIST
。
一個對賬單示例
-
在允許重複使用之前,至少需要更改5次密碼:
CREATE USER 'jeffrey'@'localhost' PASSWORD HISTORY 5; ALTER USER 'jeffrey'@'localhost' PASSWORD HISTORY 5;
此歷史記錄長度選項將覆蓋該語句命名的所有帳戶的全局策略。
-
至少需要經過365天,才能允許重複使用:
CREATE USER 'jeffrey'@'localhost' PASSWORD REUSE INTERVAL 365 DAY; ALTER USER 'jeffrey'@'localhost' PASSWORD REUSE INTERVAL 365 DAY;
這個“過去時間”的選項會覆蓋該語句命名的所有帳戶的全局策略。
-
要結合兩種類型的重用限制,請一起使用
PASSWORD HISTORY
和PASSWORD REUSE INTERVAL
:CREATE USER 'jeffrey'@'localhost' PASSWORD HISTORY 5 PASSWORD REUSE INTERVAL 365 DAY; ALTER USER 'jeffrey'@'localhost' PASSWORD HISTORY 5 PASSWORD REUSE INTERVAL 365 DAY;
這些選項覆蓋了該語句命名的所有帳戶的全局策略重用限制。
-
對於這兩種類型的重用限制,請遵循全局策略:
CREATE USER 'jeffrey'@'localhost' PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT; ALTER USER 'jeffrey'@'localhost' PASSWORD HISTORY DEFAULT PASSWORD REUSE INTERVAL DEFAULT;
密碼驗證要求策略
從MySQL 8.0.13開始,可能需要通過指定要替換的當前密碼來驗證更改帳戶密碼的嘗試。這使DBA可以防止用戶在不證明他們知道當前密碼的情況下更改密碼。否則,例如,如果一個用戶暫時退出終端會話而沒有註銷,並且惡意用戶使用該會話來更改原始用戶的MySQL密碼,則可能會發生此類更改。這可能會帶來不幸的後果:
- 在管理員重置帳戶密碼之前,原始用戶將無法訪問MySQL。
- 在密碼重置發生之前,惡意用戶可以使用良性用戶更改的憑據訪問MySQL。
可以在全局範圍內建立密碼驗證策略,並且可以將各個帳戶設置爲遵從全局策略,或者使用特定的按帳戶行爲覆蓋全局策略。
對於每個帳戶,其mysql.user
行指示是否存在特定於帳戶的設置,要求對當前密碼進行驗證以進行密碼更改嘗試。該設置由CREATE USER
和ALTER USER
語句的PASSWORD REQUIRE
選項建立:
-
如果帳戶設置爲
PASSWORD REQUIRE CURRENT
,則密碼更改必須指定當前密碼。 -
如果帳戶設置爲
PASSWORD REQUIRE CURRENT OPTIONAL
,則可以更改密碼,但不必指定當前密碼。 -
如果帳戶設置爲
PASSWORD REQUIRE CURRENT DEFAULT
,則password_require_current
系統變量將確定該帳戶的驗證所需策略:- 如果啓用了
password_require_current
,則密碼更改必須指定當前密碼。 - 如果禁用
password_require_current
,則可以但不必指定當前密碼來更改密碼。
- 如果啓用了
換句話說,如果帳戶設置不是PASSWORD REQUIRE CURRENT DEFAULT
,則帳戶設置優先於password_require_current
系統變量建立的全局策略。否則,該帳戶將遵循password_require_current
設置。
默認情況下,密碼驗證是可選的:password_require_current
被禁用,並且沒有PASSWORD REQUIRE
選項創建的帳戶默認爲PASSWORD REQUIRE CURRENT DEFAULT
。
下表顯示了每個帳戶設置如何與password_require_current
系統變量值進行交互,以確定需要帳戶密碼驗證的策略。
密碼驗證策略
每個賬戶的設置 | password_require_current 系統變量 | 修改密碼前是否需要確認原密碼 |
---|---|---|
PASSWORD REQUIRE CURRENT |
OFF |
Yes |
PASSWORD REQUIRE CURRENT |
ON |
Yes |
PASSWORD REQUIRE CURRENT OPTIONAL |
OFF |
No |
PASSWORD REQUIRE CURRENT OPTIONAL |
ON |
No |
PASSWORD REQUIRE CURRENT DEFAULT |
OFF |
No |
PASSWORD REQUIRE CURRENT DEFAULT |
ON |
Yes |
注意
特權用戶可以在不指定當前密碼的情況下更改任何帳戶密碼,而不管驗證要求的策略如何。特權用戶是具有mysql
系統數據庫的全局CREATE USER
特權或UPDATE
特權的用戶。
要全局建立密碼驗證策略,請使用password_require_current
系統變量。其默認值爲OFF
,因此不需要更改帳戶密碼來指定當前密碼。
示例:
-
要建立密碼更改必須指定當前密碼的全局策略,請在服務器
my.cnf
文件中使用以下幾行啓動服務器:[mysqld] password_require_current=ON
-
要在運行時設置和保留
password_require_current
,請使用以下語句之一:SET PERSIST password_require_current = ON; SET PERSIST password_require_current = OFF;
SET PERSIST設置正在運行的MySQL實例的值。它還保存該值以繼續進行後續服務器重新啓動。要更改正在運行的MySQL實例的值而不使它繼續進行後續的重新啓動,請使用
GLOBAL
關鍵字而不是PERSIST
。
要求全局密碼驗證的策略適用於尚未設置爲覆蓋該策略的所有帳戶。要爲單個帳戶建立策略,請使用CREATE USER
和ALTER USER
語句的PASSWORD REQUIRE
選項。
一個對賬單示例
-
要求密碼更改指定當前密碼:
CREATE USER 'jeffrey'@'localhost' PASSWORD REQUIRE CURRENT; ALTER USER 'jeffrey'@'localhost' PASSWORD REQUIRE CURRENT;
該驗證選項將覆蓋該語句命名的所有帳戶的全局策略。
-
不需要更改密碼指定當前密碼(可以但不必提供當前密碼):
CREATE USER 'jeffrey'@'localhost' PASSWORD REQUIRE CURRENT OPTIONAL; ALTER USER 'jeffrey'@'localhost' PASSWORD REQUIRE CURRENT OPTIONAL;
當用戶使用
ALTER USER
或SET PASSWORD
語句更改密碼時,對當前密碼進行驗證。這些示例使用ALTER USER
,這比SET PASSWORD
更爲可取,但是這裏描述的原理對於兩個語句都是相同的。 -
對語句命名的所有帳戶都遵循全局密碼驗證所需的策略:
CREATE USER 'jeffrey'@'localhost' PASSWORD REQUIRE CURRENT DEFAULT; ALTER USER 'jeffrey'@'localhost' PASSWORD REQUIRE CURRENT DEFAULT;
當用戶使用ALTER USER
或SET PASSWORD
語句更改密碼時,對當前密碼進行驗證。這些示例使用ALTER USER
,這比SET PASSWORD
更爲可取,但是這裏描述的原理對於兩個語句都是相同的。
在更改密碼的語句中,REPLACE
子句指定要替換的當前密碼。
示例:
-
更改當前用戶的密碼:
ALTER USER USER() IDENTIFIED BY 'auth_string' REPLACE 'current_auth_string';
-
更改指定用戶的密碼:
ALTER USER 'jeffrey'@'localhost' IDENTIFIED BY 'auth_string' REPLACE 'current_auth_string';
-
更改命名用戶的身份驗證插件和密碼:
ALTER USER 'jeffrey'@'localhost' IDENTIFIED WITH caching_sha2_password BY 'auth_string' REPLACE 'current_auth_string';
REPLACE
子句的工作方式如下:
-
如果需要更改帳戶密碼以指定當前密碼,則必須給出
REPLACE
,以驗證嘗試進行更改的用戶實際上知道當前密碼。 -
如果可以(但不必指定當前密碼)更改帳戶密碼,則
REPLACE
是可選的。 -
如果指定了
REPLACE
,則必須指定正確的當前密碼,否則會發生錯誤。 -
即使
REPLACE
是可選的,也是如此。 -
僅當更改當前用戶的帳戶密碼時才能指定
REPLACE
。(這意味着在剛剛顯示的示例中,除非當前用戶爲jeffrey
,否則顯式命名jeffrey
帳戶的語句將失敗。)即使特權用戶嘗試對另一個用戶進行更改,也是如此。但是,這樣的用戶可以在不指定REPLACE
的情況下更改任何密碼。 -
二進制日誌中省略了
REPLACE
,以避免向其寫入明文密碼。
雙密碼支持
從MySQL 8.0.14開始,允許用戶帳戶具有雙重密碼,分別指定爲主要和次要密碼。雙密碼功能使在以下情況下無縫執行憑據更改成爲可能:
- 系統具有大量的MySQL服務器,可能涉及複製。
- 多個應用程序連接到不同的MySQL服務器。
- 必須對應用程序用來連接到服務器的一個或多個帳戶進行定期的憑據更改。
考慮僅在一個帳戶只允許使用一個密碼的情況下,在上述類型的方案中必須如何執行憑證更改。在這種情況下,在更改帳戶密碼並將其傳播到所有服務器以及何時更新所有使用該帳戶的應用程序以使用新密碼的時間上,必須緊密合作。此過程可能涉及服務器或應用程序不可用的停機時間。
使用雙重密碼,可以更容易地,分階段地更輕鬆地進行憑據更改,而無需密切合作,也無需停機:
- 對於每個受影響的帳戶,在服務器上建立新的主密碼,並保留當前密碼作爲輔助密碼。這使服務器能夠識別每個帳戶的主密碼或輔助密碼,而應用程序可以繼續使用與以前相同的密碼(現在稱爲輔助密碼)連接到服務器。
- 密碼更改傳播到所有服務器後,修改使用任何受影響帳戶的應用程序以使用帳戶主密碼進行連接。
- 將所有應用程序從輔助密碼遷移到主要密碼後,不再需要輔助密碼,可以將其丟棄。此更改傳播到所有服務器後,只能使用每個帳戶的主密碼進行連接。憑據更改現已完成。
MySQL通過保存和丟棄輔助密碼的語法實現了雙密碼功能:
-
當您分配新的主要密碼時,
ALTER USER
和SET PASSWORD
語句的RETAIN CURRENT PASSWORD
子句會將帳戶當前密碼保存爲其次要密碼。 -
ALTER USER
的DISCARD OLD PASSWORD
子句將丟棄帳戶的輔助密碼,僅保留主密碼。
假設對於先前描述的憑據更改方案,應用程序使用名爲appuser1@ host1.example.com
的帳戶連接到服務器,並且該帳戶的密碼將從password_a
更改爲password_b
。
要執行此憑據更改,請按以下方式使用ALTER USER
:
-
在不是備份從節點的每個服務器上,將
password_b
設置爲新的appuser1
主密碼,並保留當前密碼作爲輔助密碼:ALTER USER 'appuser1'@'host1.example.com' IDENTIFIED BY 'password_b' RETAIN CURRENT PASSWORD;
-
等待密碼更改,以將整個系統複製到所有從屬服務器。
-
修改使用
appuser1
帳戶的每個應用程序,以使其使用password_b
而不是password_a
的密碼連接到服務器。 -
此時,不再需要輔助密碼。在不是複製從服務器的每個服務器上,放棄輔助密碼:
ALTER USER 'appuser1'@'host1.example.com' DISCARD OLD PASSWORD;
-
丟棄密碼更改複製到所有從屬服務器後,憑據更改完成。
RETAIN CURRENT PASSWORD
和DISCARD OLD PASSWORD
子句具有以下效果:
-
RETAIN CURRENT PASSWORD
保留帳戶當前密碼作爲其次要密碼,以替換任何現有的次要密碼。新密碼成爲主密碼,但是客戶端可以使用主密碼或輔助密碼使用該帳戶連接到服務器。(例外:如果ALTER USER
或SET PASSWORD
語句指定的新密碼爲空,則即使給出了RETAIN CURRENT PASSWORD
,二級密碼也將爲空。) -
如果爲具有空主密碼的帳戶指定
RETAIN CURRENT PASSWORD
,則該語句將失敗。 -
如果帳戶具有輔助密碼,而您在未指定
RETAIN CURRENT PASSWORD
的情況下更改了其主密碼,則輔助密碼將保持不變。 -
對於
ALTER USER
,如果您更改分配給該帳戶的身份驗證插件,則輔助密碼將被丟棄。如果您更改身份驗證插件,並且還指定了RETAIN CURRENT PASSWORD
,則該語句將失敗。 -
對於
ALTER USER
,DISCARD OLD PASSWORD
會丟棄輔助密碼(如果存在)。該帳戶僅保留其主密碼,並且客戶端只能使用該主密碼來使用該帳戶連接到服務器。
修改輔助密碼的語句需要以下特權:
-
要對適用於您自己的帳戶的
ALTER USER
和SET PASSWORD
語句使用RETAIN CURRENT PASSWORD
或DISCARD OLD PASSWORD
子句,必須具有APPLICATION_PASSWORD_ADMIN
特權。由於大多數用戶僅需要一個密碼,因此需要特權來操作您自己的輔助密碼。 -
如果允許一個帳戶操作所有帳戶的輔助密碼,則應授予該帳戶
CREATE USER
特權,而不是APPLICATION_PASSWORD_ADMIN
。
隨機密碼生成
從MySQL 8.0.18開始,CREATE USER
,ALTER USER
和SET PASSWORD
語句具有爲用戶帳戶生成隨機密碼的功能,可以替代要求管理員指定的文字密碼的替代方法。本節描述了生成隨機密碼的共同特徵。
默認情況下,生成的隨機密碼的長度爲20個字符。該長度由generate_random_password_length系統變量控制,範圍爲5到255。
對於每個爲其語句生成隨機密碼的帳戶,該語句將密碼存儲在mysql.user
系統表中,該密碼已針對帳戶身份驗證插件進行了適當的哈希處理。該語句還在結果集的一行中返回明文密碼,以使其對執行該語句的用戶或應用程序可用。結果集列被命名爲用戶,主機和生成的密碼,指示標識mysql.user
系統表中受影響行的用戶名和主機名值,以及明文生成的密碼。
mysql> CREATE USER
'u1'@'localhost' IDENTIFIED BY RANDOM PASSWORD,
'u2'@'%.example.com' IDENTIFIED BY RANDOM PASSWORD,
'u3'@'%.org' IDENTIFIED BY RANDOM PASSWORD;
+------+---------------+------------------------+
| user | host | generated password |
+------+----------------------+-----------------+
| u1 | localhost | BA;42VpXqQ@i+y{&TDFF |
| u2 | %.example.com | YX5>XRAJRP@>sn9azmD4 |
| u3 | %.org | ;GfD44l,)C}PI/6)4TwZ |
+------+---------------+------------------------+
mysql> ALTER USER
'u1'@'localhost' IDENTIFIED BY RANDOM PASSWORD,
'u2'@'%.example.com' IDENTIFIED BY RANDOM PASSWORD;
+------+---------------+----------------------+
| user | host | generated password |
+------+---------------------+----------------+
| u1 | localhost | yhXBrBp.;Y6abB)e_UWr |
| u2 | %.example.com | >M-vmjp9DTY6}hkp,RcC |
+------+---------------+----------------------+
mysql> SET PASSWORD FOR 'u3'@'%.org' TO RANDOM;
+------+-------+-----------------------+
| user | host | generated password |
+------+-------+-----------------------+
| u3 | %.org | o(._oNn)d;FC<vJIDg9M |
+------+-------+-----------------------+
將CREATE USER
,ALTER USER
或SET PASSWORD
語句生成一個帳戶的隨機密碼,並以IDENTIFIED WITH auth_plugin AS'auth_string'
子句作爲CREATE USER
或ALTER USER
語句寫入二進制日誌中,其中auth_plugin
是帳戶身份驗證插件,auth_string
是帳戶的哈希密碼值。
如果安裝了validate_password
組件,則它實施的策略對生成的密碼無效。(密碼驗證的目的是幫助人們創建更好的密碼。)
登錄失敗跟蹤和臨時帳戶鎖定
從MySQL 8.0.19開始,管理員可以配置用戶帳戶,以便太多連續登錄失敗會導致臨時帳戶鎖定。
在這種情況下,“登錄失敗”表示客戶端在連接嘗試期間無法提供正確的密碼。它不包括由於未知用戶或網絡問題等原因導致的連接失敗。對於具有雙重密碼的帳戶(請參閱雙重密碼支持),兩個帳戶密碼均視爲正確。
可以使用CREATE USER
和ALTER USER
語句的FAILED_LOGIN_ATTEMPTS
和PASSWORD_LOCK_TIME
選項對每個帳戶配置所需的登錄失敗次數和鎖定時間。
示例:
CREATE USER 'u1'@'localhost' IDENTIFIED BY 'password'
FAILED_LOGIN_ATTEMPTS 3 PASSWORD_LOCK_TIME 3;
ALTER USER 'u2'@'localhost'
FAILED_LOGIN_ATTEMPTS 4 PASSWORD_LOCK_TIME UNBOUNDED;
當發生太多連續登錄失敗時,客戶端會收到如下錯誤:
ERROR 3957 (HY000): Access denied for user user.
Account is blocked for D day(s) (R day(s) remaining)
due to N consecutive failed logins.
使用以下選項:
-
FAILED_LOGIN_ATTEMPTS
N
此選項指示是否跟蹤指定錯誤密碼的帳戶登錄嘗試。數字*
N
*指定有多少連續的錯誤密碼導致臨時帳戶鎖定。 -
PASSWORD_LOCK_TIME {
N
| UNBOUNDED}此選項指示在連續多次登錄嘗試後提供密碼錯誤後鎖定帳戶的時間。該值是數字*
N
*,用於指定帳戶保持鎖定的天數,或者是UNBOUNDED
,用於指定當帳戶進入臨時鎖定狀態時,該狀態的持續時間不受限制,並且直到帳戶解鎖後才結束。解鎖的條件將在後面描述。
每個選項的*N
*允許值範圍是0到32767。值爲0將禁用該選項。
登錄失敗跟蹤和臨時帳戶鎖定具有以下特徵:
-
爲了使帳戶能夠進行失敗登錄跟蹤和臨時鎖定,其
FAILED_LOGIN_ATTEMPTS
和PASSWORD_LOCK_TIME
選項都必須爲非零。 -
對於
CREATE USER
,如果未指定FAILED_LOGIN_ATTEMPTS
或PASSWORD_LOCK_TIME
,則對於該語句命名的所有帳戶,其隱式默認值爲0。這意味着將禁用登錄失敗跟蹤和臨時帳戶鎖定。(這些隱式默認值也適用於引入失敗登錄跟蹤之前創建的帳戶。) -
對於
ALTER USER
,如果未指定FAILED_LOGIN_ATTEMPTS
或PASSWORD_LOCK_TIME
,則對於該語句命名的所有帳戶,其值均保持不變。 -
爲了使臨時帳戶鎖定,密碼失敗必須是連續的。在達到失敗登錄的
FAILED_LOGIN_ATTEMPTS
值之前發生的任何成功登錄都會導致失敗計數重置。例如,如果FAILED_LOGIN_ATTEMPTS
爲4,並且發生了三個連續的密碼失敗,則必須再出現一個失敗才能開始鎖定。但是,如果下一次登錄成功,則將重置該帳戶的失敗登錄計數,以便再次需要四個連續的失敗來鎖定。 -
一旦開始臨時鎖定,即使使用了正確的密碼,也無法成功登錄,直到鎖定持續時間過去或通過以下討論中列出的一種帳戶重置方法將帳戶解鎖。
當服務器讀取授權表時,它將初始化每個帳戶的狀態信息,其中包括是否啓用了失敗登錄跟蹤,該帳戶當前是否被臨時鎖定以及是否已開始鎖定(如果已啓用)以及該帳戶發生臨時鎖定之前的失敗次數未鎖定。
-
在以下任何一種情況下,都會對所有帳戶進行全局重置:
- 服務器重新啓動。
- 執行
FLUSH PRIVILEGES
。(使用--skip-grant-tables
啓動服務器會導致無法讀取授權表,這會禁用失敗登錄跟蹤。在這種情況下,首次執行FLUSH PRIVILEGES
會導致服務器讀取授權表並啓用失敗-登錄跟蹤,以及重置所有帳戶。)
-
在以下任何一種情況下都會發生按帳戶重置的情況:
- 成功登錄該帳戶。
- 鎖定持續時間過去了。在這種情況下,登錄失敗計數將在下次嘗試登錄時重置。
- 爲該帳戶執行將
FAILED_LOGIN_ATTEMPTS
或PASSWORD_LOCK_TIME
(或兩者)設置爲任何值(包括當前選項值)的帳戶,或對該帳戶執行ALTER USER ... UNLOCK
語句。
該帳戶的其他
ALTER USER
語句對其當前的失敗登錄計數或鎖定狀態沒有影響。
登錄失敗跟蹤與用於檢查憑據的登錄帳戶相關聯。如果正在使用用戶代理,則會對代理用戶(而不是被代理用戶)進行跟蹤。也就是說,跟蹤綁定到USER()
指示的帳戶,而不是CURRENT_USER()
指示的帳戶。有關代理和代理用戶之間區別的信息。
賬戶鎖定
MySQL支持對CREATE USER
和ALTER USER
語句使用ACCOUNT LOCK
和ACCOUNT UNLOCK
子句來鎖定和解鎖用戶帳戶:
- 與
CREATE USER
一起使用時,這些子句指定新帳戶的初始鎖定狀態。在沒有任何一個子句的情況下,將以解鎖狀態創建帳戶。 - 如果啓用
validate_password
組件,則即使該帳戶已鎖定,也將不允許創建沒有密碼的帳戶。
從MySQL 8.0.19開始,ALTER USER ... UNLOCK
解鎖由於登錄失敗而被臨時鎖定的語句命名的任何帳戶。
帳戶鎖定狀態記錄在mysql.user
系統表的account_locked
列中。SHOW CREATE USER
的輸出指示帳戶是鎖定還是未鎖定。
如果客戶端嘗試連接到鎖定的帳戶,則嘗試將失敗。服務器遞增Locked_connects
狀態變量,該變量指示嘗試連接到鎖定帳戶的次數,返回ER_ACCOUNT_HAS_BEEN_LOCKED
錯誤,並將消息寫入錯誤日誌:
Access denied for user 'user_name'@'host_name'.
Account is locked.
鎖定帳戶不會影響使用假定鎖定帳戶身份的代理用戶進行連接的能力。它也不會影響執行具有DEFINER
屬性命名鎖定帳戶的存儲程序或視圖的能力。也就是說,鎖定帳戶不會影響使用代理帳戶或存儲的程序或視圖的能力。
帳戶鎖定功能取決於mysql.user
系統表中account_locked
列的存在。對於從5.7.6之前的MySQL版本進行的升級,請執行MySQL升級過程以確保該列存在。對於沒有account_locked列的未升級安裝,服務器會將所有帳戶視爲已解鎖,並且使用ACCOUNT LOCK
或ACCOUNT UNLOCK
子句會產生錯誤。