From 519ff1a402992b6254c6788de83066fc067dabf3 Mon Sep 17 00:00:00 2001 From: Reedy Date: Mon, 26 Feb 2018 00:56:52 +0000 Subject: [PATCH] Add PasswordPolicy to check the password isn't in the large blacklist Add wikimedia/password-blacklist 0.1.3, which contains 100,000 common passwords Bug: T151425 Change-Id: I80572fcee6d23ea04ad9ee683157bab9378b660e Depends-On: I8aea5a44248da9bb9ff7b328679bff6fcf41750d --- RELEASE-NOTES-1.33 | 2 ++ composer.json | 1 + includes/DefaultSettings.php | 10 ++++++++- includes/password/PasswordPolicyChecks.php | 22 +++++++++++++++++++ languages/i18n/en.json | 2 ++ languages/i18n/qqq.json | 2 ++ .../password/PasswordPolicyChecksTest.php | 20 +++++++++++++++++ 7 files changed, 58 insertions(+), 1 deletion(-) diff --git a/RELEASE-NOTES-1.33 b/RELEASE-NOTES-1.33 index 90a8c7f56d..1021cb382f 100644 --- a/RELEASE-NOTES-1.33 +++ b/RELEASE-NOTES-1.33 @@ -32,11 +32,13 @@ production. * The 'GetPreferences' hook now receives an additional $context parameter. * (T96041) __EXPECTUNUSEDCATEGORY__ on a category page causes the category to be hidden on Special:UnusedCategories. +* Add PasswordPolicy to check the password isn't in the large blacklist. * … === External library changes in 1.33 === ==== New external libraries ==== +* Added wikimedia/password-blacklist 0.1.3 * … ==== Changed external libraries ==== diff --git a/composer.json b/composer.json index f05134bf9c..09c62150e9 100644 --- a/composer.json +++ b/composer.json @@ -42,6 +42,7 @@ "wikimedia/html-formatter": "1.0.2", "wikimedia/ip-set": "1.2.0", "wikimedia/object-factory": "1.0.0", + "wikimedia/password-blacklist": "0.1.2", "wikimedia/php-session-serializer": "1.0.6", "wikimedia/purtle": "1.0.7", "wikimedia/relpath": "2.1.1", diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php index da016d47be..5175543e22 100644 --- a/includes/DefaultSettings.php +++ b/includes/DefaultSettings.php @@ -4520,6 +4520,8 @@ $wgCentralIdLookupProvider = 'local'; * commonly chosen. Set to integer n to ban the top n passwords. * If you want to ban all common passwords on file, use the * PHP_INT_MAX constant. + * - PasswordNotInLargeBlacklist - Password not in best practices list of + * 100,000 commonly used passwords. * @since 1.26 */ $wgPasswordPolicy = [ @@ -4529,29 +4531,34 @@ $wgPasswordPolicy = [ 'MinimumPasswordLengthToLogin' => 1, 'PasswordCannotMatchUsername' => true, 'PasswordCannotBePopular' => 25, + 'PasswordNotInLargeBlacklist' => true, ], 'sysop' => [ 'MinimalPasswordLength' => 8, 'MinimumPasswordLengthToLogin' => 1, 'PasswordCannotMatchUsername' => true, 'PasswordCannotBePopular' => 25, + 'PasswordNotInLargeBlacklist' => true, ], 'interface-admin' => [ 'MinimalPasswordLength' => 8, 'MinimumPasswordLengthToLogin' => 1, 'PasswordCannotMatchUsername' => true, 'PasswordCannotBePopular' => 25, + 'PasswordNotInLargeBlacklist' => true, ], 'bot' => [ 'MinimalPasswordLength' => 8, 'MinimumPasswordLengthToLogin' => 1, 'PasswordCannotMatchUsername' => true, + 'PasswordNotInLargeBlacklist' => true, ], 'default' => [ 'MinimalPasswordLength' => 1, 'PasswordCannotMatchUsername' => true, 'PasswordCannotMatchBlacklist' => true, 'MaximalPasswordLength' => 4096, + 'PasswordNotInLargeBlacklist' => false, ], ], 'checks' => [ @@ -4560,7 +4567,8 @@ $wgPasswordPolicy = [ 'PasswordCannotMatchUsername' => 'PasswordPolicyChecks::checkPasswordCannotMatchUsername', 'PasswordCannotMatchBlacklist' => 'PasswordPolicyChecks::checkPasswordCannotMatchBlacklist', 'MaximalPasswordLength' => 'PasswordPolicyChecks::checkMaximalPasswordLength', - 'PasswordCannotBePopular' => 'PasswordPolicyChecks::checkPopularPasswordBlacklist' + 'PasswordCannotBePopular' => 'PasswordPolicyChecks::checkPopularPasswordBlacklist', + 'PasswordNotInLargeBlacklist' => 'PasswordPolicyChecks::checkPasswordNotInLargeBlacklist', ], ]; diff --git a/includes/password/PasswordPolicyChecks.php b/includes/password/PasswordPolicyChecks.php index 837e959780..04ee6e9bc9 100644 --- a/includes/password/PasswordPolicyChecks.php +++ b/includes/password/PasswordPolicyChecks.php @@ -22,6 +22,7 @@ use Cdb\Reader as CdbReader; use MediaWiki\MediaWikiServices; +use Wikimedia\PasswordBlacklist; /** * Functions to check passwords against a policy requirement @@ -167,4 +168,25 @@ class PasswordPolicyChecks { return $status; } + /** + * Ensure the password isn't in the list of passwords blacklisted by the + * wikimedia/password-blacklist library + * + * @param bool $policyVal Whether to apply this policy + * @param User $user + * @param string $password + * + * @since 1.33 + * + * @return Status + */ + public static function checkPasswordNotInLargeBlacklist( $policyVal, User $user, $password ) { + $status = Status::newGood(); + if ( $policyVal && PasswordBlacklist\PasswordBlacklist::isBlacklisted( $password ) ) { + $status->error( 'passwordinlargeblacklist' ); + } + + return $status; + } + } diff --git a/languages/i18n/en.json b/languages/i18n/en.json index 9ff77dcb6d..72e67163e6 100644 --- a/languages/i18n/en.json +++ b/languages/i18n/en.json @@ -478,6 +478,7 @@ "passwordtooshort": "Passwords must be at least {{PLURAL:$1|1 character|$1 characters}}.", "passwordtoolong": "Passwords cannot be longer than {{PLURAL:$1|1 character|$1 characters}}.", "passwordtoopopular": "Commonly chosen passwords cannot be used. Please choose a password that is more difficult to guess.", + "passwordinlargeblacklist": "The password entered is in a list of very commonly used passwords. Please choose a more unique password.", "password-name-match": "Your password must be different from your username.", "password-login-forbidden": "The use of this username and password has been forbidden.", "mailmypassword": "Reset password", @@ -4538,6 +4539,7 @@ "passwordpolicies-policy-passwordcannotmatchblacklist": "Password cannot match specifically blacklisted passwords", "passwordpolicies-policy-maximalpasswordlength": "Password must be less than $1 {{PLURAL:$1|character|characters}} long", "passwordpolicies-policy-passwordcannotbepopular": "Password cannot be {{PLURAL:$1|the popular password|in the list of $1 popular passwords}}", + "passwordpolicies-policy-passwordnotinlargeblacklist": "Password cannot be in the list of 100,000 most commonly used passwords.", "easydeflate-invaliddeflate": "Content provided is not properly deflated", "unprotected-js": "For security reasons JavaScript cannot be loaded from unprotected pages. Please only create javascript in the MediaWiki: namespace or as a User subpage" } diff --git a/languages/i18n/qqq.json b/languages/i18n/qqq.json index ba2b6c2c34..182dc42fee 100644 --- a/languages/i18n/qqq.json +++ b/languages/i18n/qqq.json @@ -681,6 +681,7 @@ "passwordtooshort": "This message is shown in [[Special:Preferences]] and [[Special:CreateAccount]].\n\nParameters:\n* $1 - the minimum number of characters in the password", "passwordtoolong": "This message is shown in [[Special:Preferences]], [[Special:CreateAccount]], and [[Special:Userlogin]].\n\nParameters:\n* $1 - the maximum number of characters in the password", "passwordtoopopular": "Shown if the user chooses a really popular password.", + "passwordinlargeblacklist": "Shown if the user chooses a very common password.", "password-name-match": "Used as error message when password validity check failed.", "password-login-forbidden": "Error message shown when the user has tried to log in using one of the special username/password combinations used for MediaWiki testing. (See [[mwr:75589]], [[mwr:75605]].)", "mailmypassword": "Used as label for Submit button in [[Special:PasswordReset]].\n{{Identical|Reset password}}", @@ -4741,6 +4742,7 @@ "passwordpolicies-policy-passwordcannotmatchblacklist": "Password policy that enforces that passwords are not on a list of blacklisted passwords (often previously used during MediaWiki automated testing)", "passwordpolicies-policy-maximalpasswordlength": "Password policy that enforces a maximum number of characters a password must be. $1 - maximum number of characters that a password can be", "passwordpolicies-policy-passwordcannotbepopular": "Password policy that enforces that a password is not in a list of $1 number of \"popular\" passwords. $1 - number of popular passwords the password will be checked against", + "passwordpolicies-policy-passwordnotinlargeblacklist": "Password policy that enforces that a password is not in a list of 100,000 number of \"popular\" passwords.", "easydeflate-invaliddeflate": "Error message if the content passed to easydeflate was not deflated (compressed) properly", "unprotected-js": "Error message shown when trying to load javascript via action=raw that is not protected" } diff --git a/tests/phpunit/includes/password/PasswordPolicyChecksTest.php b/tests/phpunit/includes/password/PasswordPolicyChecksTest.php index 2d20f2c0ce..215201e9df 100644 --- a/tests/phpunit/includes/password/PasswordPolicyChecksTest.php +++ b/tests/phpunit/includes/password/PasswordPolicyChecksTest.php @@ -157,6 +157,26 @@ class PasswordPolicyChecksTest extends MediaWikiTestCase { $this->assertSame( $expected, $status->isGood() ); } + public static function provideLargeBlacklist() { + return [ + [ false, 'testpass' ], + [ false, 'password' ], + [ false, '12345' ], + [ true, 'DKn17egcA4' ], + [ true, 'testwikijenkinspass' ], + ]; + } + + /** + * @covers PasswordPolicyChecks::checkPasswordNotInLargeBlacklist + * @dataProvider provideLargeBlacklist + */ + public function testCheckNotInLargeBlacklist( $expected, $password ) { + $user = User::newFromName( 'username' ); + $status = PasswordPolicyChecks::checkPasswordNotInLargeBlacklist( true, $user, $password ); + $this->assertSame( $expected, $status->isGood() ); + } + /** * Verify that all password policy description messages actually exist. * Messages used on Special:PasswordPolicies -- 2.20.1