WL#13515: Add Oracle style per user FAILED_LOGIN_ATTEMPTS/PASSWORD_LOCK_TIME counters
Affects: Server-8.0
—
Status: Complete
Introduce a per-user account temporary account lock-out on failed password attempt, similar to https://docs.oracle.com/cd/B19306_01/server.102/b14200/statements_6010.htm , the FAILED_LOGIN_ATTEMPTS/PASSWORD_LOCK_TIME part. The idea is that if the user failed to provide their password FAILED_LOGIN_ATTEMPTS consecutive times the account gets locked for login attempts for PASSWORD_LOCK_TIME. The default of these options will be off for backward compatibility.
FR1. Add 2 new password_option to CREATE USER and ALTER USER : * FAILED_LOGIN_ATTEMPTS: temporarily lock the user at the specified attempt (e.g. FAILED_LOGIN_ATTEMPTS 2 will block on the 2nd failed login attempt * PASSWORD_LOCK_TIME FR1.1: if any is specified it will override the stored value FR1.2: The values will be checked (and the remaining days updated) at login time FR1.3: a server restart and the FLUSH PRIVILEGES command will unlock all temporarily locked users and reset their failed login attempts counts back to the FAILED_LOGIN_ATTEMPTS value. FR1.4: the failed login attempts for each user will be reset back to failed_login_attempts after password_lock_time days even for unsuccessful login (and login failure counting will start afresh). FR1.5: Any other updates to the user account will not result in any update on the state of its logins failed temp lock. FR1.6: Setting either FAILED_LOGIN_ATTEMPTS or PASSWORD_LOCK_TIME to zero will turn off the tracking of failed login attempts for that user account. FR1.7: setting either FAILED_LOGIN_ATTEMPTS or PASSWORD_LOCK_TIME will reset the runtime lock state, i.e. it will unlock the account if it was locked due to exceeding FAILED_LOGIN_ATTEMPTS. FR1.8: If the account is in locked state (FAILED_LOGIN_ATTEMPTS and before PASSWORD_LOCK_TIME has expired) even a successful login will not be possible. The administrator needs to explicitly reset the lock via ALTER USER or the ways described in FR1.3. FR1.9: A successful login will reset the runtime count of consecutive unsuccessful logins back to FAILED_LOGIN_ATTEMPTS. FR2. Add a new composite key into mysql.user.user_attributes: {"Password_locking": {"failed_login_attempts": , "password_lock_time_days": }} to store the current values for PASSWORD_LOCK_TIME and FAILED_LOGIN_ATTEMPTS for the user account. FR2.1. If any of the JSON key(s) is missing or has wrong or missing value the whole fragment will be ignored and the values will be considered 0. FR3. SHOW CREATE USER will print FAILED_LOGIN_ATTEMPTS and PASSWORD_LOCK_TIME only if they are non-zero. FR4. binlog versions of CREATE USER and ALTER USER will contain FAILED_LOGIN_ATTEMPTS and PASSWORD_LOCK_TIME if they're specified in the original statement. FR5. If the login is to fail due to account being locked a specific error message telling that will be sent to the client and logged to the error log as information. FR6. The results of the login are checked right after checking the authentication method and should not be affected by any other reasons for a login failure (ALTER USER LOCK etc). FR7. The tracking of the login failures will be done for the "login account", i.e. the one that is used to check the credentials. In proxy case this means not the user account a successful login will proxy to (CURRENT_USER) but the USER() account. FR8. These new features will work for all authentication types. And will count failed logins too. In this case a "failed login" means the authentication plugin returning "false". FR9. If you do not specify FAILED_LOGIN_ATTEMPTS it will default to 0 FR10. If you do not specify PASSWORD_LOCK_TIME it will default to 0 FR11. ALTER USER ACCOUNT UNLOCK will also reset the current password locked state introduced by this worklog (similarly to a successful login, see FR1.3). FR12. PASSWORD_LOCK_TIME will take a special option value called UNBOUNDED meaning the account will be in locked state until it's unlocked through one of the accepted ways. FR13. PASSWORD_LOCK_TIME and FAILED_LOGIN_ATTEMPTS can take numeric values between 0 and 32767.
We add a composite key to mysql.user.user_attributes. We add new parser rule to parse FAILED_LOGIN_ATTEMPTS and PASSWORD_LOCK_TIME. We make FAILED_LOGIN_ATTEMPTS and PASSWORD_LOCK_TIME non-reserved mysql specific keywords. We add a new structure to ACL_USER that we fill in with: 1. The current values for FAILED_LOGIN_ATTEMPTS and PASSWORD_LOCK_TIME 2. The trackers of the lock state: the day number when the account was locked (0 if not) and the remaining password attempts (0 if account locked or not to be locked at all). Every time the values for FAILED_LOGIN_ATTEMPTS and PASSWORD_LOCK_TIME we reset the trackers of the lock state according to the new value (and unlock the account). The runtime state. In addition to the values for FAILED_LOGIN_ATTEMPTS and PASSWORD_LOCK_TIME ACL_USER holds 2 more data members: - the remaining failed logins before the account is locked (remaining_login_attempts). 0 means don't check. - the day number when the account is locked (daynr_locked). 0 here means not locked. How is the check at login time done: If the account has no password lock checking returns success regardless of the runtime state. if the login was successful and the account is not locked the runtime state is reset and checking returns success. if there are remaining login attempts they're decreased and if they are still greater then zero checking returns success if the remaining login attempts has just got down to 0 the account is locked: daynr_locked is set to the present day number and processing returns failure (stop the login). if the account is locked (daynr_locked is non-zero) and the current day is PASSWORD_LOCK_TIME days after the day the account was locked (daynr_locked) the runtime state is reset (daynr_locked set to 0 and remaining_login_attempts to FAILED_LOGIN_ATTEMPTS) and success is returned. Notes on upgrade/downgrade: No changes to the USER_ATTRIBUTE columns will be needed at version upgrade. Since the JSON composite will be missing it'll be considered set to 0/0, i.e. backward compatible with what it used to be before this feature is introduced. On downgrade the older server will just ignore the values set for these two options hence everything should work with the old server except that the auto-locking will stop even for accounts that have it configured. Note that once the other values in the JSON column are updated by an old server the locking JSON composite may be removed when storing the column. Thus if you downgrade the server, update one of the JSON values and then upgrade the server again, the password locking settings may be lost (wherever set). Clarification on FR7 Having 2 active passwords doesn't have an effect on the behavior: a successful login (either password) resets the count. A failed login (none of the passwords) counts as a failed login. Example syntax: CREATE USER foo@localhost FAILED_LOGIN_ATTEMPTS 2 PASSWORD_LOCK_TIME 3; ALTER USER foo@localhost PASSWORD_LOCK_TIME UNBOUNDED; Clarification on FR9/FR10 Obviously the default 0 applies only to CREATE USER. For ALTER USER if you do not specify either of the new clauses the current value will be preserved.
Copyright (c) 2000, 2024, Oracle Corporation and/or its affiliates. All rights reserved.