MySQL8.0 - 新特性 - 安全及權限相關改進

MySQL8.0裏引入了很多關於權限的改動,從這些改動能夠看出來,權限管理更加的規範和遍歷了,這和咱們以前爲rds mysql增長了大量權限管理很相似,想來Oracle也是經過這些改動爲其雲業務服務的吧。php

本文主要簡述下部分相關的權限改動,不會涉及代碼實現部分。當前版本爲8.0.16html

Atomic ACL Statement

因爲實現了新的數據詞典表,全部的權限相關的信息都存儲在innodb mysql tablespace裏。而innodb是事務性引擎,具備ACID特性,因此對應的ACL操做也具備原子特性。mysql

例如以前若是一個語句對多個user操做的時候,有些成功,有些會失敗。而如今則是要麼所有成功,要麼所有失敗。binlog也會在事務提交時記錄到redo log裏。git

這裏有個問題是當咱們經過搭建備庫的方式從5.7升級到8.0時,那些在5.7部分紅功的acl操做,到了以8.0做爲備庫的實例上會所有失敗.github

關於atomic ddl 見官方文檔sql

Role

Role是一個期待已久的功能,能夠認爲是一組權限的集合, 你能夠爲多個帳戶賦予相同的role權限,這也使得權限的管理更加規範,大大方便了運維和管理。你能夠經過 create role 'role_name' 建立一個role名,而後再經過grant語句爲role賦予權限。以後就能夠grant 'role_name' to 一個指定的帳戶了。數據庫

關於role,以前寫了一篇文章介紹了,這裏再也不贅述,感興趣的點連接json

參考:
官方文檔安全

connection control plugin

引入了一個新的插件,代碼在plugin/connection_control/下,該插件使用的是audit plugin接口,其功能是在數次登錄失敗後,會延遲下次登錄的時間,這也有點相似於屢次密碼輸入錯誤,會被凍結一會的意思。session

在lib/plugin目錄下,咱們已經編譯好了插件connection_control.so,安裝也比較簡單:

mysql> INSTALL PLUGIN CONNECTION_CONTROL SONAME 'connection_control.so';
Query OK, 0 rows affected (0.01 sec)

mysql> INSTALL PLUGIN CONNECTION_CONTROL_FAILED_LOGIN_ATTEMPTS SONAME 'connection_control.so';
Query OK, 0 rows affected (0.03 sec)

mysql> SELECT PLUGIN_NAME, PLUGIN_STATUS FROM INFORMATION_SCHEMA.PLUGINS WHERE PLUGIN_NAME LIKE 'connection%'\G
*************************** 1. row ***************************
  PLUGIN_NAME: CONNECTION_CONTROL
PLUGIN_STATUS: ACTIVE
*************************** 2. row ***************************
  PLUGIN_NAME: CONNECTION_CONTROL_FAILED_LOGIN_ATTEMPTS
PLUGIN_STATUS: ACTIVE
2 rows in set (0.00 sec)

mysql> SHOW VARIABLES LIKE '%connection%control%';
+-------------------------------------------------+------------+
| Variable_name                                   | Value      |
+-------------------------------------------------+------------+
| connection_control_failed_connections_threshold | 3          |
| connection_control_max_connection_delay         | 2147483647 |
| connection_control_min_connection_delay         | 1000       |
+-------------------------------------------------+------------+
3 rows in set (0.00 sec)

如何使用:
connection_control_failed_connections_threshold: 容許失敗的次數,在這麼屢次失敗後,會去增長delay的時間(設置爲0則表示關閉該特性,不會去增長延遲)
當超出失敗上限後,就根據以後失敗的測試乘以connection_control_min_connection_delay做爲delay時間,但最大不超過connection_control_max_connection_delay, 以默認配置爲例子,當第四次失敗時是1000毫秒,當第五次失敗時就加倍到2000毫秒

官方文檔

支持雙重密碼

這也是個有趣的特性,意思是支持一個帳戶兩個密碼,這一般發生在你修改了密碼,但又不想致使正在運行的業務中斷時。如worklog所述,當你有大規模的複製集羣時,又想修改複製密碼,固然不但願正在進行的複製中斷拉。那怎麼辦,能夠在保持兩個密碼在一段時間內都是有效的。用法也比較簡單,咱們舉個簡單的例子:

root@test 10:07:00>CREATE USER arthurdent@localhost IDENTIFIED WITH 'mysql_native_password' BY 'abcd';
Query OK, 0 rows affected (0.00 sec)

# 再建立一個密碼,同時保持當前密碼
root@test 10:07:02>ALTER USER arthurdent@localhost IDENTIFIED BY 'efgh' RETAIN CURRENT PASSWORD;
Query OK, 0 rows affected (0.01 sec)

#再建立一個密碼,同時保持當前密碼,可是第一個建立的密碼abcd就失效了
root@test 10:07:18>ALTER USER arthurdent@localhost IDENTIFIED BY 'efghh' RETAIN CURRENT PASSWORD;
Query OK, 0 rows affected (0.01 sec)

若是要拋棄舊密碼,能夠執行以下語句
root@test 10:11:36>ALTER USER arthurdent@localhost DISCARD OLD PASSWORD;
Query OK, 0 rows affected (0.00 sec)

此時你再經過舊密碼efgh就沒法成功登陸了。

mysql.user表被擴展了來存儲兩個密碼,主密碼存儲在mysql.user.authentication_string中,次要密碼存儲在mysql.user.user_attributes中

root@test 10:31:36>select user, authentication_string, user_attributes from mysql.user where user = 'arthurdent'\G
*************************** 1. row ***************************
                 user: arthurdent
authentication_string: *7538919BBFC125D3F772537519E66F8242CD2E6B
      user_attributes: {"additional_password": "*1ACFAF7821CBE8E2D6B7C3FA1A539F53CB41BB9D"}
1 row in set (0.00 sec)

除了ALTER USER外,SET PASSWORD也支持相似的語法:

SET PASSWORD [FOR user] = 'auth_string'
    [REPLACE 'current_auth_string']
    [RETAIN CURRENT PASSWORD]

參考文檔:
WL#11540: Support 2 active passwords per user account

Partial Revoker

在以前若是你有create user權限,相應的也有了drop/create/modify任何帳戶的權限,包括root帳戶。 若是用戶有delete/update權限的話,甚至還能夠修改grant系統表, 由於有的時候咱們須要把部分權限revoke掉

worklog舉了個例子,這裏直接列出來啦:

mysql@root> CREATE USER foo;
mysql@root> GRANT CREATE USER,UPDATE,DELETE ON *.* TO foo WITH GRANT OPTION;
mysql@root> GRANT SELECT ON mysql.* TO foo with grant option;

Now, foo has the ability to do the following:

mysql@foo>CREATE USER bar;
mysql@foo>ALTER USER root@localhost IDENTIFIED BY 'gibberish';
mysql@foo>DROP USER root@localhost;
mysql@foo>DELETE FROM mysql.user WHERE user = 'root';
mysql@foo>UPDATE mysql.user SET authentication_string = 'gibberish'
          WHERE user='root';

如上例,當foo用戶有了由root帳戶賦予的grant權限,他甚至能夠去操做root帳戶。這個worklog的目的,就確保foo用戶沒法對root帳戶進行操做。

這個worklog把權限定義爲三類:

- Global Privileges: DDL/DML privileges that allow object manipulation on all
                     databases. This includes administrative privileges,
                     dynamic privileges.

- Database Privileges: Restricted to a one (or more) databases.
                       They provide ability to manipulate objects and
                       data within database.

- Restrictions_list: List of tuples - (user, database, privileges).
                   Each entry in the list represents operations prohibited on
                   a given database for given user. Restrictions list implies
                   that even if user is granted GLOBAL privileges, if
                   revocation list prevents the operation, user can not perform
                   it for given database.

其中restrictions_list存儲在mysql.user表中,主要是引入Partial revoke, 能夠revoke部分庫上的權限,例如mysql庫,這實際上對於雲業務而言是很是重要的功能:用戶一般但願擁有超級權限,但云平臺自己也有保留的帳號作維護用,這些咱們是不但願被修改的,舉個簡單的例子:

root@(none) 09:26:43>CREATE USER foo;
Query OK, 0 rows affected (0.00 sec)

root@(none) 09:26:49>GRANT ALL ON *.* TO foo;
Query OK, 0 rows affected (0.00 sec)

root@(none) 09:27:00>SET GLOBAL partial_revokes = 0;
Query OK, 0 rows affected (0.00 sec)

root@(none) 09:27:05>REVOKE INSERT ON mysql.* FROM foo;
ERROR 1141 (42000): There is no such grant defined for user 'foo' on host '%'
root@(none) 09:27:12>SET GLOBAL partial_revokes = 1;
Query OK, 0 rows affected (0.00 sec)

root@(none) 09:27:14>REVOKE INSERT ON mysql.* FROM foo;
Query OK, 0 rows affected (0.00 sec)

root@(none) 09:27:24>REVOKE DELETE ON mysql.* FROM foo;
Query OK, 0 rows affected (0.00 sec)

這裏引入了一個全局參數partial_revokes, 只有打開了,你才能對帳戶作partial revoke操做,這裏會產生一個對該帳戶的限制列表,存儲在mysql庫中:

root@(none) 09:29:08>select user, authentication_string, user_attributes from mysql.user where user = 'foo'\G
*************************** 1. row ***************************
                 user: foo
authentication_string:
      user_attributes: {"Restrictions": [{"Database": "mysql", "Privileges": ["INSERT", "DELETE"]}]}
1 row in set (0.00 sec)

能夠看到針對該帳戶產生了一個限制列表Restrictions, 以json的形式存儲。Partial Revoke的限制(摘自文檔):

  • Partial revokes must name the schema literally. Schema names that contain the % or _ SQL wildcard characters (for example, myschema%) are not permitted.
  • It is possible to use partial revokes to place restrictions on nonexistent schemas, but only if the revoked privilege is granted globally. If a privilege is not granted globally, revoking it for a nonexistent schema produces an error.
  • Partial revokes apply at the schema level only. You cannot use partial revokes for privileges that apply only globally (such as FILE or BINLOG_ADMIN), or for table, column, or routine privileges.

當一個有restrictions list的帳戶再去建立別的帳戶時,他受限的列表也會傳遞出去

在wl#12098中還引入了system user這樣的權限類型,只有相同權限的帳戶才能修改這種類型的帳戶,普通帳戶無權對其進行修改。在以後又在wl#12364中,避免擁有CONNECTION_ADMIN權限的普通用戶可以去kill超級用戶的session或者query:

root@(none) 08:20:40>GRANT SYSTEM_USER ON *.* TO foo;
Query OK, 0 rows affected (0.00 sec)

root@(none) 08:20:54>GRANT SYSTEM_USER ON *.* TO bar;
Query OK, 0 rows affected (0.01 sec)

baz@(none) 08:27:38>GRANT CONNECTION_ADMIN ON *.* to baz;
Query OK, 0 rows affected (0.00 sec)

#login foo

foo@(none) 08:27:10>show grants;
+---------------------------------------+
| Grants for foo@%                      |
+---------------------------------------+
| GRANT USAGE ON *.* TO `foo`@`%`       |
| GRANT SYSTEM_USER ON *.* TO `foo`@`%` |
+---------------------------------------+
2 rows in set (0.00 sec)

foo@(none) 08:28:04>show processlist;
+-----+------+-----------+------+---------+------+----------+------------------+
| Id  | User | Host      | db   | Command | Time | State    | Info             |
+-----+------+-----------+------+---------+------+----------+------------------+
| 348 | foo  | localhost | NULL | Query   |    0 | starting | show processlist |
+-----+------+-----------+------+---------+------+----------+------------------+
1 row in set (0.00 sec)


#login baz
baz@(none) 08:29:03>show grants;
+--------------------------------------------+
| Grants for baz@%                           |
+--------------------------------------------+
| GRANT USAGE ON *.* TO `baz`@`%`            |
| GRANT CONNECTION_ADMIN ON *.* TO `baz`@`%` |
+--------------------------------------------+
2 rows in set (0.00 sec)

baz@(none) 08:29:05>show processlist;
+-----+------+-----------+------+---------+------+----------+------------------+
| Id  | User | Host      | db   | Command | Time | State    | Info             |
+-----+------+-----------+------+---------+------+----------+------------------+
| 349 | baz  | localhost | NULL | Query   |    0 | starting | show processlist |
+-----+------+-----------+------+---------+------+----------+------------------+
1 row in set (0.00 sec)

#baz帳戶只能看到本身的線程,若是強制去kill foo呢 ?
baz@(none) 08:30:30>kill 348;
ERROR 1095 (HY000): You are not owner of thread 348

能夠看到有connection_admin權限的帳戶被限制了,不只沒法看到system_user的連接,也沒法去kill session.

簡單來講,有system_user權限的帳戶能夠修改system user和regular user的帳戶;而regular user則沒法修改system user的帳戶

關於這塊官方文檔有很是詳細的內容,筆者對這塊也不太熟悉,就很少說了,感興趣的直接翻閱以下文檔吧:
WL#12098: MySQL system users
WL#12364: Kill administration for system users
WL#12820: Extend GRANT syntax to cover partial revokes information
Privilege Restriction Using Partial Revokes
Account Categories

Password Expiration

能夠設置密碼過時時間,提供了三種操做:

  1. 經過參數default_password_lifetime來控制 , 單位爲天
root@(none) 09:21:31>SET PERSIST default_password_lifetime = 180;
Query OK, 0 rows affected (0.00 sec)

該選項的值會被alter user覆蓋

  1. 經過ALTER USER來控制

指定過時時間

CREATE USER 'jeffrey'@'localhost' PASSWORD EXPIRE INTERVAL 90 DAY;
ALTER USER 'jeffrey'@'localhost' PASSWORD EXPIRE INTERVAL 90 DAY;

過時時間存儲在mysql.user表中

root@(none) 09:35:46>select user,password_lifetime from mysql.user where user = 'jeffrey'\G
*************************** 1. row ***************************
             user: jeffrey
password_lifetime: 90
1 row in set (0.00 sec)

禁止密碼過時

CREATE USER 'jeffrey'@'localhost' PASSWORD EXPIRE NEVER;
ALTER USER 'jeffrey'@'localhost' PASSWORD EXPIRE NEVER;

默認過時時間爲default_password_lifetime:

CREATE USER 'jeffrey'@'localhost' PASSWORD EXPIRE DEFAULT;
ALTER USER 'jeffrey'@'localhost' PASSWORD EXPIRE DEFAULT;
  1. 直接手動過時
ALTER USER 'jeffrey'@'localhost' PASSWORD EXPIRE;

參考:
官方文檔
WL#6587 : Protocol support for password expiration

密碼複用

如今不少系統在忘記密碼重設時,都會要求最近幾回使用付的密碼不容許再次使用,這也是爲了安全考慮,MySQL也增長了這樣的功能,和密碼過時相似,也能夠經過全局變量,ALTER USER來控制:

例如以下配置:

password_history=6
password_reuse_interval=365

表示不要服用最近6次用到的密碼或者365天內用過的密碼。

也能夠經過create/alter user來設置:

CREATE USER 'jeffrey'@'localhost' PASSWORD HISTORY 5;
ALTER USER 'jeffrey'@'localhost' PASSWORD HISTORY 5;
CREATE USER 'jeffrey'@'localhost' PASSWORD REUSE INTERVAL 365 DAY;
ALTER USER 'jeffrey'@'localhost' PASSWORD REUSE INTERVAL 365 DAY;

一樣的也能夠把上例中的history 5 和 interval 365 day指定爲default

參考:
官方文檔
WL#6595: Password rotation policy

修改帳戶要求驗證

一樣是安全相關的,當修改一個帳戶時,須要去驗證密碼,可使用參數password_require_current來控制。默認關閉,當打開該選項時,若是要修改帳戶密碼,必需要提供當前的密碼才容許修改,以下摘錄的官方示例:

要求在修改時輸入當前密碼:

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;

根據參數配置來決定:

CREATE USER 'jeffrey'@'localhost' PASSWORD REQUIRE CURRENT DEFAULT;
ALTER USER 'jeffrey'@'localhost' PASSWORD REQUIRE CURRENT DEFAULT;

那麼修改密碼時就須要顯示當前密碼:

CREATE USER 'jeffrey'@'localhost' PASSWORD REQUIRE CURRENT DEFAULT;
ALTER USER 'jeffrey'@'localhost' PASSWORD REQUIRE CURRENT DEFAULT;

SET PASSWORD也同樣.

SET PASSWORD [FOR user] = password_option

        password_option : {
            'auth_string' [REPLACE 'auth_string']
        }

參考:

官方文檔
WL#11544 Current password required for SET PASSWORD

限制SET PERSIST

MySQL提供了在線持久化參數修改的功能,經過接口SET PERSIST 和SET PERSIST ONLY來實現,但有些涉及敏感信息的變量則不該該被persist, 所以不該該經過遠程終端來管理,而是要管理員登陸機器,手動的修改my.cnf

新增參數persist_only_admin_x509_subject , 當打開這個參數時,只有經過SSL認證的用戶才能Persist一些受限的系統參數。官方文檔列舉了些可持久化的參數和不可持久化的參數

參考:
參數:persist_only_admin_x509_subject
Nonpersistible and Persist-Restricted System Variables

skip-grant-tables

用過的人的都知道,當以skip-grant-tables啓動時候,系統將不檢查任何權限,這是是很危險的,但有時候若是application和數據庫實例部署在同一臺機器時,咱們又能夠經過該選項來得到更好的性能,但帶來的風險是其餘人只要知道host和端口號,也能夠遠程鏈接過來,這就有數據安全問題

所以MySQL加入了新選項skip_networking,再也不監聽tcp/ip鏈接請求。

另外最近也修復了一個有趣的bug#94394,當mysql.user表損壞時,實例啓動時僅僅打印了一條錯誤信息,並以skip-grant-tables的方式啓動了。這實際上市不安全的,人們可能在install初始化階段不當心忽略這個錯誤,然後數據庫的正常運行,也會形成實例正確安裝的錯覺。

所以在8.0.16版本中,官方修復了這個問題,除非用戶指定skip-grant-tables,實例將打印信息以後直接啓動失敗。

fk error不顯示父表信息

這個修復很簡單,就是說對父表沒權限的用戶,若是在子表上由於foreign key約束,致使錯誤的話,不該該將父表的信息暴露出來,這可能致使安全問題,而是返回統一的錯誤:

ERROR 23000: Cannot add or update a child row: a foreign key constraint fails

參考:
WL#8910: Ensure foreign key error does not reveal information about parent table for which user has no access privileges.

SESSION_VARIABLES_ADMIN

一般任何帳戶都容許設置session級別的變量,但某些session級別的變量只能特定權限的用戶設置,例如binlog_format, sql_log_bin,火鶴sql_log_off等,須要須要SYSTEM_VARIABLES_ADMIN或者SUPER權限來設置。

從MySQL8.0.14開始了增長了一個新的權限位session_variables_admin, wl#12217列出了一些須要該權限位的變量:

The following vairables need to enforce SESSION_VARIABLES_ADMIN:

auto_increment_increment
auto_increment_offset
binlog_direct_non_transactional_updates
bulk_insert_buffer_size
character_set_database
character-set-filesystem
collation_database
pseudo_slave_mode
pseudo_thread_id
transaction_write_set_extraction
rbr_exec_mode

The following variables will not be protected:

default_storage_engine
default_tmp_storage_engine
max_allowed_packet
rand_seed1
rand_seed2

These variables should transition from checking SYSTEM_VARIABLES_ADMIN to 
SESSION_VARIABLES_ADMIN:

histogram_generation_max_mem_size
sql_log_off
debug_sync
original_commit_timestamp
The not documented gtid_next
The disabled and not documented gtid_next_list
default_collation_for_utf8mb4
explicit_defaults_for_timestamp
sql_log_bin
explicit_defaults_for_timestamp The variable is mis-documented as not requiring SYSTEM_VARIABLES_ADMIN for SET SESSION. But in reality it does require it. Since the variable is deprecated we'll keep the current behavior.
binlog_format
binlog_row_image
binlog_row_value_options
binlog_rows_query_log_events


原文連接 本文爲雲棲社區原創內容,未經容許不得轉載。

相關文章
相關標籤/搜索