背景:
繼上次介紹 初識 MySQL 5.6 新功能、參數完之后,剛好MySQL 5.7又GA了,在官方測試里看到,MySQL5.7在功能、性能、可用性、安全和監控上又提升了很高。現在看看和MySQL5.6對比,新增參數可以看上一篇文章,本文來說明MySQL5.7關於賬號安全方面的新特性:(后續持續更新)
1,賬號安全相關的特性
1.1:創建用戶
5.7版本的用戶表mysql.user要求plugin字段非空,且默認值是mysql_native_password認證插件,並且不再支持mysql_old_password認證插件。5.7用戶長度最大為32字節,之前最大長度為16字節,並且CREATE USER 和 DROP USER 命令里實現了 IF [NOT] EXISTS 條件判斷。5.7之后用戶通過grant創建用戶報warning。如:
grant all on *.* to dxy@localhost identified by 'dxy'; Query OK, 0 rows affected, 1 warnings (0.00 sec) show warnings;
+---------+------+---------------------------------------------------------------+ | Level | Code | Message | +---------+------+---------------------------------------------------------------+ | Warning | 1287 | Using GRANT for creating new user is deprecated and will be removed in future release. Create new user with CREATE USER statement. | +---------+------+---------------------------------------------------------------+ 2 rows in set (0.01 sec)
提示grant創建賬戶的語法將會被刪除,用cerate user代替,創建用戶分2步:創建和授權。
先通過create user 創建用戶:
#明文密碼創建 CREATE USER 'dxy'@'localhost' IDENTIFIED BY '123456';
等同 CREATE USER 'dxy'@'localhost' IDENTIFIED WITH 'mysql_native_password' BY '123456'; #加密密碼創建 CREATE USER 'dxy'@'localhost' IDENTIFIED BY PASSWORD '*6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9'; --will be removed in a future release
等同 CREATE USER 'dxy'@'localhost' IDENTIFIED WITH 'mysql_native_password' AS '*6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9';
再通過grant來授權:
grant select,insert,update,delete on dba_test.* to dxy@localhost;
注意:授權管理用戶的時候,不止只有all的權限,還要包括with grant option和proxy的權限。proxy權限需要在代理用戶的時候用到。
查看默認管理用戶權限: show grants for root@localhost; ----2條記錄 +---------------------------------------------------------------------+ | +---------------------------------------------------------------------+ | GRANT ALL PRIVILEGES ON *.* TO 'root'@'localhost' WITH GRANT OPTION | | GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION | +---------------------------------------------------------------------+ 新建管理賬號: create user dba@127.0.0.1 identified by '123456'; 授權: GRANT ALL PRIVILEGES ON *.* TO 'root'@'127.0.0.1' WITH GRANT OPTION; 授proxy權:創建代理用戶的時候需要 GRANT PROXY ON ''@'' TO 'dba'@'127.0.0.1' WITH GRANT OPTION; 查看: show grants for 'dba'@'127.0.0.1'; +--------------------------------------------------------------------+ | GRANT ALL PRIVILEGES ON *.* TO 'dba'@'127.0.0.1' WITH GRANT OPTION | | GRANT PROXY ON ''@'' TO 'dba'@'127.0.0.1' WITH GRANT OPTION | +--------------------------------------------------------------------+
查看用戶權限:
show grants for dxy@localhost; +---------------------------------------------------------------------------+ | Grants for dxy@localhost | +---------------------------------------------------------------------------+ | GRANT USAGE ON *.* TO 'dxy'@'localhost' | | GRANT SELECT, INSERT, UPDATE, DELETE ON `dba_test`.* TO 'dxy'@'localhost' | +---------------------------------------------------------------------------+
查看用戶密碼:
show create user dxy@localhost; +----------------------------------------------------------------------------------+ | CREATE USER 'dxy'@'localhost' IDENTIFIED WITH 'mysql_native_password' AS '*6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9' REQUIRE NONE PASSWORD EXPIRE DEFAULT ACCOUNT UNLOCK | +----------------------------------------------------------------------------------+
1.2:密碼過期策略
為用戶設置密碼過期時間,一定時間以后,強制用戶修改密碼。可以直接在create user的時候設置,也可以alter user設置:
PASSWORD EXPIRE DEFAULT | 默認,過期時間受全局變量default_password_lifetime控制 |
PASSWORD EXPIRE NEVER | 永不過期 |
PASSWORD EXPIRE INTERVAL N DAY | N天后過期 |
PASSWORD EXPIRE | 過期 |
直接創建用戶的時候設置: create user dxy@localhost identified by '123456' password expire interval 10 day; ---- 10天后過期 對已有用戶設置 alter user zjy@localhost password expire never; ----永不過期
注意:設置一個用戶過期后,登陸會有提示修改密碼,不能進行任何操作:適用讓程序不能訪問數據庫。
設置用戶密碼過期: alter user dxy@localhost password expire; 執行任何命令報錯: ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement. 解決辦法:重置密碼
alter user dxy@localhost identified by '123456';
1.2.1:密碼安全策略
為防止用戶設置過簡單的密碼,mysql在5.6開始就已經支持了密碼安全策略的插件:
沒有開啟安全策略的時候,能成功為用戶設置一個簡單的密碼:
mysql> grant all on *.* to dxy@localhost identified by '123'; Query OK, 0 rows affected(0.00 sec)
開啟密碼安全策略,2種方法:
① 在配置文件里修改:
[mysqld] plugin-load=validate_password.so validate-password=FORCE_PLUS_PERMANENT
② 在線修改
mysql> INSTALL PLUGIN validate_password SONAME 'validate_password.so'; Query OK, 0 rows affected (0.01 sec)
開啟安全策略的時候,不能為用戶設置一個簡單的密碼:
mysql> grant all on *.* to dxy@localhost identified by '123'; ERROR 1819 (HY000): Your password does not satisfy the current policy requirements
相關參數:
表示密碼策略,可設置的值有:
1:0 or LOW 僅需需符合密碼長度(由參數validate_password_length指定)
2:1 or MEDIUM 滿足LOW策略,同時還需滿足至少有1個數字,小寫字母,大寫字母和特殊字符,默認。
3:2 or STRONG 滿足MEDIUM策略,同時密碼不能存在字典文件(dictionary file)中
validate_password_dictionary_file:
在STRONG模式下還能設置字典文件,字典中存在的密碼不得使用。可以通過參數validate_password_dictionary_file來設置字典文件
設置密碼的最小長度,默認值為8
validate_password_mixed_case_count:
小寫和大寫字符的最小數目,如果密碼策略是MEDIUM或STRONG的,有validate_password插件檢查,默認1。
validate_password_number_count:
數字字符的最小數目,如果密碼策略是MEDIUM或STRONG的,有validate_password插件檢查,默認1。
validate_password_special_char_count:
非字母字符(特殊字符)的最小數目,如果密碼策略是MEDIUM或STRONG的,有validate_password插件檢查,默認1。
根據上面的參數,按照自己想要的密碼復雜度,進行設置。更多信息可以看官方文檔和MYSQL密碼審計插件。
1.3:鎖定禁用用戶 alter user
當某些場景需要"鎖"住用戶,暫時禁用某個用戶:適用讓程序不能訪問數據庫。
設置鎖定用戶: alter user dxy@localhost account lock; 登陸報錯: ERROR 3118 (HY000): Access denied for user 'dxy'@'localhost'. Account is locked. 解決辦法:解鎖用戶 alter user dxy@localhost account unlock;
1.4 代理用戶
基於mysql_native_password的認證插件自帶了代理用戶的功能。代理用戶相當於“代理”其他用戶的權限,這樣很方便的把一個賬號的權限授予其他賬號,而不需要每個賬號都需要執行授權操作。開啟代理用戶的功能需要開啟參數:check_proxy_users 和 mysql_native_password_proxy_users
創建原始賬號: create user dxy@127.0.0.1 identified by '123456'; 授權: grant all on test.* to dxy@127.0.0.1; 創建代理賬號: create user dxy_proxy@127.0.0.1 identified by '123456'; 授權代理權限: grant proxy on dxy@127.0.0.1 to dxy_proxy@127.0.0.1; 查看: show grants for dxy_proxy@127.0.0.1; +-------------------------------------------------------------+ | GRANT USAGE ON *.* TO 'dxy_proxy'@'127.0.0.1' | | GRANT PROXY ON 'dxy'@'127.0.0.1' TO 'dxy_proxy'@'127.0.0.1' | +-------------------------------------------------------------+
用代理賬號登陸測試:
查看登陸賬號:代理賬號current_user(),原始賬號user() select user(),current_user(); +---------------------+----------------+ | user() | current_user() | +---------------------+----------------+ | dxy_proxy@127.0.0.1 | dxy@127.0.0.1 | +---------------------+----------------+ 查看權限:發現代理賬號的權限顯示的是原始賬號的權限 show grants;
+-------------------------------------------------------+ +-------------------------------------------------------+ | GRANT USAGE ON *.* TO 'dxy'@'127.0.0.1' | | GRANT ALL PRIVILEGES ON `test`.* TO 'dxy'@'127.0.0.1' | +-------------------------------------------------------+ 驗證代理賬號是否有test庫的權限: mysql> show databases; +--------------------+ | Database | +--------------------+ | information_schema | | test | +--------------------+ mysql> use test mysql> show tables; +----------------+ | Tables_in_test | +----------------+ | tttt | +----------------+ mysql> select * from tttt; +------+ | id | +------+ | 1 | | 100 | +------+ mysql> insert into tttt values(2),(200); mysql> select * from tttt; +------+ | id | +------+ | 1 | | 100 | | 2 | | 200 | +------+ 驗證得出代理賬號(dxy_proxy)代理了原始賬號(dxy)的權限。
1.5:其他選項:SSL、MAX_QUERIES_PER_HOUR、MAX_UPDATES_PER_HOUR、MAX_CONNECTIONS_PER_HOUR、MAX_USER_CONNECTIONS。當需要限制賬號通過ssl登陸,需要添加require,當需要限制資源,需要添加with:
create user dxy@localhost identified by '123456' require SSL with MAX_QUERIES_PER_HOUR 100 MAX_USER_CONNECTIONS 100 password expire never account unlock;
2,外部相關的安全
2.1:MySQL5.7已經刪除了test數據庫,默認安裝完后是沒有test數據庫,原先任何用戶都可以訪問test數據庫,增加安全隱患。
2.2:MySQL5.7提供了更為簡單SSL安全訪問配置,並且默認連接就采用SSL的加密方式。在5.7之前,生成SSL相關文件需要自己手動創建,可以查看這篇文章,5.7之后MySQL通過mysql_ssl_rsa_setup可以直接生成了:
root@t20:~# mysql_ssl_rsa_setup Generating a 2048 bit RSA private key .................................+++ ....................+++ writing new private key to 'ca-key.pem' ----- Generating a 2048 bit RSA private key ......+++ ..............................+++ writing new private key to 'server-key.pem' ----- Generating a 2048 bit RSA private key .........................................................................................+++ ..+++ writing new private key to 'client-key.pem' -----
可以在數據目錄下面看到一些以pem結尾的文件,而這些文件就是開啟SSL連接所需要的文件(注意文件權限),之后用賬號
默認登陸:
root@t20:/var/lib/mysql# mysql -udba -p -h127.0.0.1 Enter password: mysql> \s -------------- mysql Ver 14.14 Distrib 5.7.12, for Linux (x86_64) using EditLine wrapper Connection id: 4 Current database: Current user: dba@localhost SSL: Cipher in use is DHE-RSA-AES256-SHA ... ...
強制ssl登陸:
root@t20:~# mysql -udba -p -h127.0.0.1 --ssl=1 WARNING: --ssl is deprecated and will be removed in a future version. Use --ssl-mode instead. Enter password: mysql> \s -------------- mysql Ver 14.14 Distrib 5.7.12, for Linux (x86_64) using EditLine wrapper Connection id: 10 Current database: Current user: dba@localhost SSL: Cipher in use is DHE-RSA-AES256-SHA ... ...
從上面看到均已ssl登陸,若在創建用戶時,希望該用戶每次必須通過SSL方式,則需在創建用戶通過REQUIRE SSL來進行設置,上面已經介紹。姜承堯文章中的測試案例顯示開啟SSL性能開銷在25%左右:MySQL的SSL加密連接與性能開銷
2.3:MySQL5.7開始建議用戶使用 mysqld --initialize
來初始化數據庫,放棄之前的mysql_install_db的方式,新的方式只創建了一個root@localhost的用戶,隨機密碼保存在~/.mysql_secret文件中,第一次使用必須reset password。
初始化數據庫:新建實例。 mysqld --initialize --datadir=/var/lib/mysql3309/
2.4:MySQL5.7 sql_mode的變更,
5.7默認的sql_mode
select @@sql_mode; ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
5.7之前默認的sql_mode
select @@sql_mode; NO_ENGINE_SUBSTITUTION
看到在5.7中sql_mode更加嚴格。解釋下各個mode的含義:
ONLY_FULL_GROUP_BY |
不要讓GROUP BY部分中的查詢指向未選擇的列 |
STRICT_TRANS_TABLES |
為事務存儲引擎啟用嚴格模式,也可能為非事務存儲引擎啟用嚴格模式 |
NO_ZERO_IN_DATE | 在嚴格模式,不接受月或日部分為0的日期 |
NO_ZERO_DATE | 在嚴格模式,不將 '0000-00-00'做為合法日期 |
ERROR_FOR_DIVISION_BY_ZERO | 在嚴格模式,在INSERT或UPDATE過程中,如果被零除(或MOD(X,0)),則產生錯誤 |
NO_AUTO_CREATE_USER | 防止GRANT自動創建新用戶,除非還指定了密碼 |
NO_ENGINE_SUBSTITUTION | 如果需要的存儲引擎被禁用或未編譯,可以防止自動替換存儲引擎 |
在默認情況下5.7的情況:
----對於datetime類型<NO_ZERO_DATE>: 插入"0000-00-00 00:00:00"值,會報錯:Incorrect datetime value ----對於varchar/char類型<STRICT_TRANS_TABLES>: 插入字符串超出長度,會報錯: Data too long for column... ----對於not null的列<STRICT_TRANS_TABLES>: 插入不指定not null的列會報錯:Field 'xxx' doesn't have a default value ' ----對於grant<NO_AUTO_CREATE_USER>: 授權一個用戶,不指定密碼會報錯:Can't find any matching row in the user table ' ----對於engine存儲引擎<NO_ENGINE_SUBSTITUTION>: 創建一個不支持的存儲引擎,不會轉換為默認的存儲引擎,直接報錯:Unknown storage engine ... Using storage engine InnoDB for table '...'
注意:在一個主從環境下,為保證數據的一致性,一定要設置主從的sql_mode一樣,在數據遷移的時候也要保證sql_mode的一致,不然復制和遷移遇到上面的限制均會失敗,所以盡可能使用標准SQL語法。
MySQL錯誤信息總結:

1005:創建表失敗 1006:創建數據庫失敗 1007:數據庫已存在,創建數據庫失敗 1008:數據庫不存在,刪除數據庫失敗 1009:不能刪除數據庫文件導致刪除數據庫失敗 1010:不能刪除數據目錄導致刪除數據庫失敗 1011:刪除數據庫文件失敗 1012:不能讀取系統表中的記錄 1020:記錄已被其他用戶修改 1021:硬盤剩余空間不足,請加大硬盤可用空間 1022:關鍵字重復,更改記錄失敗 1023:關閉時發生錯誤 1024:讀文件錯誤 1025:更改名字時發生錯誤 1026:寫文件錯誤 1032:記錄不存在 1036:數據表是只讀的,不能對它進行修改 1037:系統內存不足,請重啟數據庫或重啟服務器 1038:用於排序的內存不足,請增大排序緩沖區 1040:已到達數據庫的最大連接數,請加大數據庫可用連接數 1041:系統內存不足 1042:無效的主機名 1043:無效連接 1044:當前用戶沒有訪問數據庫的權限 1045:不能連接數據庫,用戶名或密碼錯誤 1048:字段不能為空 1049:數據庫不存在 1050:數據表已存在 1051:數據表不存在 1054:字段不存在 1065:無效的SQL語句,SQL語句為空 1081:不能建立Socket連接 1114:數據表已滿,不能容納任何記錄 1116:打開的數據表太多 1129:數據庫出現異常,請重啟數據庫 1130:連接數據庫失敗,沒有連接數據庫的權限 1133:數據庫用戶不存在 1141:當前用戶無權訪問數據庫 1142:當前用戶無權訪問數據表 1143:當前用戶無權訪問數據表中的字段 1146:數據表不存在 1147:未定義用戶對數據表的訪問權限 1149:SQL語句語法錯誤 1158:網絡錯誤,出現讀錯誤,請檢查網絡連接狀況 1159:網絡錯誤,讀超時,請檢查網絡連接狀況 1160:網絡錯誤,出現寫錯誤,請檢查網絡連接狀況 1161:網絡錯誤,寫超時,請檢查網絡連接狀況 1062:字段值重復,入庫失敗 1169:字段值重復,更新記錄失敗 1177:打開數據表失敗 1180:提交事務失敗 1181:回滾事務失敗 1203:當前用戶和數據庫建立的連接已到達數據庫的最大連接數,請增大可用的數據庫連接數或重啟數據庫 1205:加鎖超時 1211:當前用戶沒有創建用戶的權限 1216:外鍵約束檢查失敗,更新子表記錄失敗 1217:外鍵約束檢查失敗,刪除或修改主表記錄失敗 1226:當前用戶使用的資源已超過所允許的資源,請重啟數據庫或重啟服務器 1227:權限不足,您無權進行此操作 1235:MySQL版本過低,不具有本功能
3,總結:
在MySQL 5.7中,有不少安全性相關的改進:創建賬號分2步:用create user來建立賬號(賬號長度加大),用grant 來授權;初始數據庫的時候密碼不為空;賬號可以鎖和可以設置密碼過期;test庫被刪除;默認提供ssl連接;sql_mode增強等。文章從這些方面進行了介紹和測試,進一步加深對MySQL5.7的認識,本文只是說了一些自己比較關注的,比較詳細的請見 What’s New in MySQL 5.7,后續會持續更新說明。