diff --git a/mysql-schema/mysql-schema-user.md b/mysql-schema/mysql-schema-user.md index 69ef40dce8f45..aa64c42a92875 100644 --- a/mysql-schema/mysql-schema-user.md +++ b/mysql-schema/mysql-schema-user.md @@ -83,7 +83,7 @@ The `mysql.user` table contains several fields that can be categorized into thre * `authentication_string` and `plugin`: `authentication_string` stores the credentials for the user account. The credentials are interpreted based on the authentication plugin specified in the `plugin` field. * `Account_locked`: indicates whether the user account is locked. * `Password_reuse_history` and `Password_reuse_time`: used for [Password reuse policy](/password-management.md#password-reuse-policy). - * `User_attributes`: provides information about user comments and user attributes. + * `User_attributes`: provides information about user comments and user attributes. It also stores the secondary password under `$.additional_password` when [dual passwords](/password-management.md#dual-password-policy) are used. * `Token_issuer`: used for the [`tidb_auth_token`](/security-compatibility-with-mysql.md#tidb_auth_token) authentication plugin. * `Password_expired`, `Password_last_changed`, and `Password_lifetime`: used for [Password expiration policy](/password-management.md#password-expiration-policy). @@ -102,7 +102,7 @@ The `mysql.user` table contains several fields that can be categorized into thre * `authentication_string` and `plugin`: `authentication_string` stores the credentials for the user account. The credentials are interpreted based on the authentication plugin specified in the `plugin` field. * `Account_locked`: indicates whether the user account is locked. * `Password_reuse_history` and `Password_reuse_time`: used for [Password reuse policy](https://docs.pingcap.com/tidb/stable/password-management#password-reuse-policy). - * `User_attributes`: provides information about user comments and user attributes. + * `User_attributes`: provides information about user comments and user attributes. It also stores the secondary password under `$.additional_password` when [dual passwords](https://docs.pingcap.com/tidb/stable/password-management#dual-password-policy) are used. * `Token_issuer`: used for the [`tidb_auth_token`](https://docs.pingcap.com/tidb/stable/security-compatibility-with-mysql#tidb_auth_token) authentication plugin. * `Password_expired`, `Password_last_changed`, and `Password_lifetime`: used for [Password expiration policy](https://docs.pingcap.com/tidb/stable/password-management#password-expiration-policy). diff --git a/password-management.md b/password-management.md index 2d839d8989fd2..bdaaabb2415a5 100644 --- a/password-management.md +++ b/password-management.md @@ -12,6 +12,8 @@ To protect the security of user passwords, TiDB supports the following password - Password reuse policy: prevent users from reusing old passwords. - Failed-login tracking and temporary account locking policy: temporarily lock a user account to prevent the same user from trying to log in after multiple login failures caused by wrong passwords. +Starting from v8.5.7, TiDB also supports the dual password policy, which lets you rotate passwords for applications without downtime. For details, see [Dual password policy](#dual-password-policy). + ## TiDB authentication credential storage To ensure the authenticity of user identity, TiDB uses passwords as credentials to authenticate users when they log in to the TiDB server. @@ -358,6 +360,55 @@ ALTER USER 'test'@'localhost' > - The default value of the `PASSWORD HISTORY` and `PASSWORD REUSE INTERVAL` options is 0, which means that the reuse policy is disabled. > - When you modify a username, TiDB migrates the corresponding password history in the `mysql.password_history` system table from the original username to the new username. +## Dual password policy + +Starting from v8.5.7, TiDB supports the MySQL-compatible [dual password policy](https://dev.mysql.com/doc/refman/8.0/en/password-management.html#dual-passwords), which allows an account to hold two valid passwords at the same time: a primary password and a secondary password. When many applications share one account, dual passwords let you rotate the password in phases without any downtime: establish a new primary password while the old password keeps working, migrate the applications one by one, and then discard the old password. + +A typical rotation works as follows: + +1. Set a new primary password and retain the current password as the secondary password: + + ```sql + ALTER USER 'app_user'@'%' IDENTIFIED BY 'new_password' RETAIN CURRENT PASSWORD; + ``` + + At this point, `app_user` can log in with both `new_password` and the previous password. + +2. Update every application to use the new password. + +3. After all applications are migrated, discard the secondary password: + + ```sql + ALTER USER 'app_user'@'%' DISCARD OLD PASSWORD; + ``` + + At this point, only `new_password` is valid. + +`SET PASSWORD` supports the same clause: + +```sql +SET PASSWORD FOR 'app_user'@'%' = 'new_password' RETAIN CURRENT PASSWORD; +``` + +TiDB stores the secondary password in the `User_attributes` column of the `mysql.user` system table, and [`SHOW CREATE USER`](/sql-statements/sql-statement-show-create-user.md) does not display it in its output. + +### Required privileges + +- Operating on your own account (including the `ALTER USER USER() ...` and `SET PASSWORD ... RETAIN CURRENT PASSWORD` forms) requires the `APPLICATION_PASSWORD_ADMIN` [dynamic privilege](/privilege-management.md#dynamic-privileges). The `CREATE USER` privilege or the `UPDATE` privilege on the `mysql` schema also suffices. +- Operating on another account requires the normal cross-account authority: the `CREATE USER` privilege (or the `UPDATE` privilege on the `mysql` schema) for `ALTER USER`, and the `SUPER` privilege for `SET PASSWORD`. `APPLICATION_PASSWORD_ADMIN` does not grant authority over other accounts. + +### Restrictions + +- TiDB only supports dual passwords for accounts that authenticate with the `mysql_native_password`, `caching_sha2_password`, or `tidb_sm3_password` plugin. +- `RETAIN CURRENT PASSWORD` requires a non-empty new password. Otherwise, TiDB reports the `ER_CURRENT_PASSWORD_CANNOT_BE_RETAINED` error. +- `RETAIN CURRENT PASSWORD` requires a non-empty current password. Otherwise, TiDB reports the `ER_SECOND_PASSWORD_CANNOT_BE_EMPTY` error. +- You cannot combine `RETAIN CURRENT PASSWORD` with a change of the authentication plugin. Otherwise, TiDB reports the `ER_PASSWORD_CANNOT_BE_RETAINED_ON_PLUGIN_CHANGE` error. Changing the authentication plugin without `RETAIN CURRENT PASSWORD` silently removes any existing secondary password. +- Changing the primary password without `RETAIN CURRENT PASSWORD` leaves an existing secondary password unchanged. + +> **Note:** +> +> During a rolling upgrade, a retained secondary password authenticates only on TiDB nodes that are already upgraded to a version that supports dual passwords. The new primary password works on all nodes. Complete the cluster upgrade before relying on dual password rotation. + ## Failed-login tracking and temporary account locking policy TiDB can track the number of failed login attempts for an account. To prevent the password from being cracked by brute force, TiDB can lock the account after a specified number of failed login attempts. diff --git a/privilege-management.md b/privilege-management.md index 8b08960b11c4d..b39355e338d04 100644 --- a/privilege-management.md +++ b/privilege-management.md @@ -274,6 +274,7 @@ Dynamic privileges include: * `SYSTEM_VARIABLES_ADMIN` * `ROLE_ADMIN` * `CONNECTION_ADMIN` +* `APPLICATION_PASSWORD_ADMIN` allows privilege owners to use the `RETAIN CURRENT PASSWORD` and `DISCARD OLD PASSWORD` clauses on their own account. For details, see [Dual password policy](/password-management.md#dual-password-policy). * `PLACEMENT_ADMIN` allows privilege owners to create, modify, and remove placement policies. * `DASHBOARD_CLIENT` allows privilege owners to log in to TiDB Dashboard. * `RESTRICTED_TABLES_ADMIN` allows privilege owners to view system tables when SEM is enabled. diff --git a/security-compatibility-with-mysql.md b/security-compatibility-with-mysql.md index 91dedf934ec2f..3458cfc9fcce3 100644 --- a/security-compatibility-with-mysql.md +++ b/security-compatibility-with-mysql.md @@ -13,7 +13,6 @@ TiDB supports security features similar to MySQL 5.7, and also supports some sec - Column level permissions. - These permission attributes: `max_questions` and `max_updated`. - Password verification policy, which requires you to verify the current password when you change it. -- Dual password policy. - Random password generation. - Multi-factor authentication. diff --git a/sql-statements/sql-statement-alter-user.md b/sql-statements/sql-statement-alter-user.md index f139053caa91b..2b64d1292c849 100644 --- a/sql-statements/sql-statement-alter-user.md +++ b/sql-statements/sql-statement-alter-user.md @@ -12,13 +12,16 @@ This statement changes an existing user inside the TiDB privilege system. In the ```ebnf+diagram AlterUserStmt ::= - 'ALTER' 'USER' IfExists (UserSpecList RequireClauseOpt ConnectionOptions PasswordOption LockOption AttributeOption | 'USER' '(' ')' 'IDENTIFIED' 'BY' AuthString) ResourceGroupNameOption + 'ALTER' 'USER' IfExists (UserSpecList RequireClauseOpt ConnectionOptions PasswordOption LockOption AttributeOption | 'USER' '(' ')' ('IDENTIFIED' 'BY' AuthString DualPasswordOption | 'DISCARD' 'OLD' 'PASSWORD')) ResourceGroupNameOption UserSpecList ::= UserSpec ( ',' UserSpec )* UserSpec ::= - Username AuthOption + Username AuthOption DualPasswordOption + +DualPasswordOption ::= + ( 'RETAIN' 'CURRENT' 'PASSWORD' | 'DISCARD' 'OLD' 'PASSWORD' )? Username ::= StringName ('@' StringName | singleAtIdentifier)? | 'CURRENT_USER' OptionalBraces @@ -74,6 +77,41 @@ mysql> SHOW CREATE USER 'newuser'; 1 row in set (0.00 sec) ``` +Rotate the password for `newuser` without downtime using [dual passwords](/password-management.md#dual-password-policy): retain the current password as the secondary password while setting a new primary password, and discard it after all applications have switched to the new password. Both statements require the `APPLICATION_PASSWORD_ADMIN` dynamic privilege when operating on your own account. + +```sql +ALTER USER 'newuser' IDENTIFIED BY 'newpassword' RETAIN CURRENT PASSWORD; +``` + +``` +Query OK, 0 rows affected (0.02 sec) +``` + +At this point, `newuser` can log in with both the new and the previous password. TiDB stores the secondary password under `$.additional_password` in the `User_attributes` column of the `mysql.user` system table: + +```sql +SELECT JSON_EXTRACT(User_attributes, '$.additional_password') IS NOT NULL AS has_secondary FROM mysql.user WHERE User = 'newuser'; +``` + +``` ++---------------+ +| has_secondary | ++---------------+ +| 1 | ++---------------+ +1 row in set (0.00 sec) +``` + +```sql +ALTER USER 'newuser' DISCARD OLD PASSWORD; +``` + +``` +Query OK, 0 rows affected (0.02 sec) +``` + +After `DISCARD OLD PASSWORD`, only the new password is valid. + Lock the user `newuser`: ```sql @@ -216,6 +254,7 @@ SELECT USER, JSON_EXTRACT(User_attributes, "$.resource_group") FROM mysql.user W * [TiDB User Account Management](/user-account-management.md) +* [TiDB Password Management](/password-management.md) * [Security Compatibility with MySQL](/security-compatibility-with-mysql.md) diff --git a/sql-statements/sql-statement-set-password.md b/sql-statements/sql-statement-set-password.md index 2d3ae1e3b6e2e..392bff4b5c3f6 100644 --- a/sql-statements/sql-statement-set-password.md +++ b/sql-statements/sql-statement-set-password.md @@ -12,7 +12,7 @@ This statement changes the user password for a user account in the TiDB system d ```ebnf+diagram SetPasswordStmt ::= - "SET" "PASSWORD" ( "FOR" Username )? "=" ( stringLit | "PASSWORD" "(" stringLit ")" ) + "SET" "PASSWORD" ( "FOR" Username )? "=" ( stringLit | "PASSWORD" "(" stringLit ")" ) ( "RETAIN" "CURRENT" "PASSWORD" )? ``` ## Examples @@ -55,9 +55,21 @@ mysql> SHOW CREATE USER 'newuser'; 1 row in set (0.00 sec) ``` +Starting from v8.5.7, `SET PASSWORD ... RETAIN CURRENT PASSWORD` retains the current password as the secondary password while setting the new primary password, so both passwords remain valid during a password rotation. For details, see [Dual password policy](/password-management.md#dual-password-policy). + +```sql +SET PASSWORD FOR 'newuser' = 'newpassword' RETAIN CURRENT PASSWORD; +``` + +``` +Query OK, 0 rows affected (0.01 sec) +``` + +Setting your own password with `RETAIN CURRENT PASSWORD` requires the `APPLICATION_PASSWORD_ADMIN` dynamic privilege. Setting the password of another account requires the `SUPER` privilege. + ## MySQL compatibility -The `SET PASSWORD` statement in TiDB is fully compatible with MySQL. If you find any compatibility differences, [report a bug](https://docs.pingcap.com/tidb/stable/support). +The `SET PASSWORD` statement in TiDB is fully compatible with MySQL, except that TiDB does not support the `REPLACE 'current_auth_string'` clause for verifying the current password. If you find any compatibility differences, [report a bug](https://docs.pingcap.com/tidb/stable/support). ## See also