Plugin Directory

Changeset 3063866


Ignore:
Timestamp:
04/03/2024 03:18:19 PM (21 months ago)
Author:
wfryan
Message:

1.1.11 - April 3, 2024

  • Fix: Revised the behavior of the reCAPTCHA verification to use the documented expiration period of the token and response to avoid sending verification requests too frequently, which could artificially lower scores in some circumstances
Location:
wordfence-login-security
Files:
42 added
42 deleted
12 edited
1 copied

Legend:

Unmodified
Added
Removed
  • wordfence-login-security/tags/1.1.11/classes/controller/captcha.php

    r3049221 r3063866  
    9999                $jsonResponse = wp_remote_retrieve_body($response);
    100100                $decoded = @json_decode($jsonResponse, true);
    101                 if (is_array($decoded) && isset($decoded['success']) && isset($decoded['score']) && isset($decoded['action'])) {
    102                     if ($decoded['success'] && $decoded['action'] == $action) {
    103                         return (float) $decoded['score'];
     101                if (is_array($decoded) && isset($decoded['success'])) {
     102                    if ($decoded['success']) {
     103                        if (isset($decoded['score']) && isset($decoded['action']) && $decoded['action'] == $action) {
     104                            return (float) $decoded['score'];
     105                        }
    104106                    }
    105107                    return false;
  • wordfence-login-security/tags/1.1.11/classes/controller/users.php

    r3049221 r3063866  
    1515    const META_KEY_ALLOW_GRACE_PERIOD = 'wfls-allow-grace-period';
    1616    const META_KEY_VERIFICATION_TOKENS = 'wfls-verification-tokens';
     17    const META_KEY_CAPTCHA_SCORES = 'wfls-captcha-scores';
    1718    const VERIFICATION_TOKEN_BYTES = 64;
    1819    const VERIFICATION_TOKEN_LIMIT = 5; //Max number of concurrent tokens
    1920    const VERIFICATION_TOKEN_TRANSIENT_PREFIX = 'wfls_verify_';
     21    const CAPTCHA_SCORE_LIMIT = 2; //Max number of captcha scores cached
     22    const CAPTCHA_SCORE_TRANSIENT_PREFIX = 'wfls_captcha_';
     23    const CAPTCHA_SCORE_CACHE_DURATION = 60; //seconds
    2024    const LARGE_USER_BASE_THRESHOLD = 1000;
    2125    const TRUNCATED_ROLE_KEY = 1;
     
    949953        return $userId !== null && ($user === null || $userId === $user->ID);
    950954    }
     955   
     956    /**
     957     * Returns the key used to store a captcha score transient.
     958     *
     959     * @param string $hash
     960     * @return string
     961     */
     962    private function get_captcha_score_transient_key($hash) {
     963        return self::CAPTCHA_SCORE_TRANSIENT_PREFIX . $hash;
     964    }
     965   
     966    /**
     967     * Attempts to look up a stored captcha score for the given hash and user. If found, returns the score. If not,
     968     * returns null.
     969     *
     970     * @param string $hash
     971     * @param \WP_User $user
     972     * @return float|false
     973     */
     974    private function load_captcha_score($hash, $user) {
     975        $key = $this->get_captcha_score_transient_key($hash);
     976        $data = get_transient($key);
     977        if ($data === false) {
     978            return false;
     979        }
     980       
     981        if (!$user->exists() || $data['user'] !== $user->ID) {
     982            return false;
     983        }
     984       
     985        return floatval($data['score']);
     986    }
     987   
     988    /**
     989     * Deletes the stored captcha score if present for the given hash.
     990     *
     991     * @param string $hash
     992     */
     993    private function clear_captcha_score($token, $user) {
     994        $hash = $this->hash_captcha_token($token);
     995        $key = $this->get_captcha_score_transient_key($hash);
     996        delete_transient($key);
     997       
     998        $storedHashes = get_user_meta($user->ID, self::META_KEY_CAPTCHA_SCORES, true);
     999        $validHashes = array();
     1000        if (is_array($storedHashes)) {
     1001            foreach ($storedHashes as $hash) {
     1002                $storedScore = $this->load_captcha_score($hash, $user);
     1003                if ($storedScore !== false) {
     1004                    $validHashes[] = $hash;
     1005                }
     1006            }
     1007        }
     1008        $validHashes = array_slice($validHashes, 0, self::CAPTCHA_SCORE_LIMIT);
     1009        update_user_meta($user->ID, self::META_KEY_CAPTCHA_SCORES, $validHashes);
     1010    }
     1011   
     1012    /**
     1013     * Hashes the captcha token for storage.
     1014     *
     1015     * @param string $token
     1016     * @return string
     1017     */
     1018    private function hash_captcha_token($token) {
     1019        return wp_hash($token);
     1020    }
     1021   
     1022    /**
     1023     * Returns the cached score for the given captcha score and user if available. This action removes it from the cache
     1024     * since the intent is for it only to be used for the initial login request to validate credentials + the follow-up
     1025     * request either finalizing the login (no 2FA set) or with the 2FA token.
     1026     *
     1027     * $expired will be set to `true` if the reason for returning `false` is because the $token is recently expired. It
     1028     * will be false when the $token is either uncached or has been expired long enough to be removed from the internal
     1029     * list.
     1030     *
     1031     * @param string $token
     1032     * @param \WP_User $user
     1033     * @param bool $expired
     1034     * @return float|false
     1035     */
     1036    public function cached_captcha_score($token, $user, &$expired = false) {
     1037        $hash = $this->hash_captcha_token($token);
     1038        $score = $this->load_captcha_score($hash, $user);
     1039        if ($score === false) {
     1040            $storedHashes = get_user_meta($user->ID, self::META_KEY_CAPTCHA_SCORES, true);
     1041            if (is_array($storedHashes)) {
     1042                $expired = in_array($hash, $storedHashes);
     1043            }
     1044        }
     1045       
     1046        $this->clear_captcha_score($token, $user);
     1047        return $score;
     1048    }
     1049   
     1050    /**
     1051     * Caches the $token/$score pair for $user, automatically pruning its cached list to the maximum allowable count
     1052     *
     1053     * @param string $token
     1054     * @param float|false $score
     1055     * @param \WP_User $user
     1056     */
     1057    public function cache_captcha_score($token, $score, $user) {
     1058        if ($score === false) {
     1059            return;
     1060        }
     1061       
     1062        $storedHashes = get_user_meta($user->ID, self::META_KEY_CAPTCHA_SCORES, true);
     1063        $validHashes = array();
     1064        if (is_array($storedHashes)) {
     1065            foreach ($storedHashes as $hash) {
     1066                $storedScore = $this->load_captcha_score($hash, $user);
     1067                if ($storedScore !== false) {
     1068                    $validHashes[] = $hash;
     1069                }
     1070            }
     1071        }
     1072       
     1073        $hash = $this->hash_verification_token($token);
     1074        array_unshift($validHashes, $hash);
     1075        while (count($validHashes) > self::CAPTCHA_SCORE_LIMIT) {
     1076            $excessHash = array_pop($validHashes);
     1077            delete_transient($this->get_captcha_score_transient_key($excessHash));
     1078        }
     1079       
     1080        $key = $this->get_captcha_score_transient_key($hash);
     1081        set_transient($key, array('user' => $user->ID, 'score' => $score), self::CAPTCHA_SCORE_CACHE_DURATION);
     1082        update_user_meta($user->ID, self::META_KEY_CAPTCHA_SCORES, $validHashes);
     1083    }
    9511084
    9521085    public function get_user_count() {
  • wordfence-login-security/tags/1.1.11/classes/controller/wordfencels.php

    r3049221 r3063866  
    636636            $score = false;
    637637            if ($requireCAPTCHA && !$performVerification) {
    638                 $score = Controller_CAPTCHA::shared()->score($token);
     638                $expired = false;
     639                if (is_object($user) && $user instanceof \WP_User) {
     640                    $score = Controller_Users::shared()->cached_captcha_score($token, $user, $expired);
     641                }
     642               
     643                if ($score === false) {
     644                    if ($expired) {
     645                        return new \WP_Error('wfls_captcha_expired', wp_kses(__('<strong>CAPTCHA EXPIRED</strong>: The CAPTCHA verification for this login attempt has expired. Please try again.', 'wordfence-login-security'), array('strong'=>array())));
     646                    }
     647                   
     648                    $score = Controller_CAPTCHA::shared()->score($token);
     649                   
     650                    if ($score !== false && is_object($user) && $user instanceof \WP_User) {
     651                        Controller_Users::shared()->cache_captcha_score($token, $score, $user);
     652                        Controller_Users::shared()->record_captcha_score($user, $score);
     653                    }
     654                }
     655               
    639656                if ($score === false && !Controller_CAPTCHA::shared()->test_mode()) { //An invalid token will require additional verification (if test mode is not active)
    640657                    $performVerification = true;
    641                 }
    642                 else if (is_object($user) && $user instanceof \WP_User) {
    643                     Controller_Users::shared()->record_captcha_score($user, $score);
    644658                }
    645659            }
  • wordfence-login-security/tags/1.1.11/languages/wordfence-login-security.pot

    r3049221 r3063866  
    33msgid ""
    44msgstr ""
    5 "Project-Id-Version: Wordfence Login Security 1.1.10\n"
    6 "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/wordfence-login-security-zip-Cjy0sfiYR\n"
     5"Project-Id-Version: Wordfence Login Security 1.1.11\n"
     6"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/wordfence-login-security-zip-eZqOVC21Q\n"
    77"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
    88"Language-Team: LANGUAGE <[email protected]>\n"
     
    1010"Content-Type: text/plain; charset=UTF-8\n"
    1111"Content-Transfer-Encoding: 8bit\n"
    12 "POT-Creation-Date: 2024-03-11T15:20:44+00:00\n"
     12"POT-Creation-Date: 2024-04-03T15:14:29+00:00\n"
    1313"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
    1414"X-Generator: WP-CLI 2.7.1\n"
     
    265265msgstr ""
    266266
    267 #: classes/controller/users.php:517
     267#: classes/controller/users.php:521
    268268#: classes/controller/wordfencels.php:486
    269269msgid "2FA Status"
    270270msgstr ""
    271271
    272 #: classes/controller/users.php:521
     272#: classes/controller/users.php:525
    273273msgid "Last Login"
    274274msgstr ""
    275275
    276 #: classes/controller/users.php:523
     276#: classes/controller/users.php:527
    277277msgid "Last CAPTCHA"
    278278msgstr ""
    279279
    280 #: classes/controller/users.php:533
     280#: classes/controller/users.php:537
    281281msgid "Not Allowed"
    282282msgstr ""
    283283
    284 #: classes/controller/users.php:538
     284#: classes/controller/users.php:542
    285285#: classes/controller/wordfencels.php:490
    286286msgid "Active"
    287287msgstr ""
    288288
    289 #: classes/controller/users.php:541
     289#: classes/controller/users.php:545
    290290msgid "Inactive<small class=\"wfls-sub-status\">(Grace Period)</small>"
    291291msgstr ""
    292292
    293 #: classes/controller/users.php:544
     293#: classes/controller/users.php:548
    294294msgid "Locked Out<small class=\"wfls-sub-status\">(Grace Period Disabled)</small>"
    295295msgstr ""
    296296
    297 #: classes/controller/users.php:544
     297#: classes/controller/users.php:548
    298298msgid "Locked Out<small class=\"wfls-sub-status\">(Grace Period Exceeded)</small>"
    299299msgstr ""
    300300
    301 #: classes/controller/users.php:547
     301#: classes/controller/users.php:551
    302302#: classes/controller/wordfencels.php:490
    303303msgid "Inactive"
    304304msgstr ""
    305305
    306 #: classes/controller/users.php:560
     306#: classes/controller/users.php:564
    307307msgid "(not required)"
    308308msgstr ""
    309309
    310 #: classes/controller/users.php:654
     310#: classes/controller/users.php:658
    311311msgid "Edit two-factor authentication for %s"
    312312msgstr ""
    313313
    314 #: classes/controller/users.php:654
     314#: classes/controller/users.php:658
    315315#: views/settings/options.php:9
    316316msgid "2FA"
    317317msgstr ""
    318318
    319 #: classes/controller/users.php:665
     319#: classes/controller/users.php:669
    320320#: views/settings/user-stats.php:25
    321321msgid "2FA Active"
    322322msgstr ""
    323323
    324 #: classes/controller/users.php:666
     324#: classes/controller/users.php:670
    325325#: views/settings/user-stats.php:26
    326326msgid "2FA Inactive"
     
    442442
    443443#: classes/controller/wordfencels.php:490
    444 #: classes/controller/wordfencels.php:855
     444#: classes/controller/wordfencels.php:869
    445445#: views/manage/grace-period.php:22
    446446msgid "Locked Out"
     
    500500msgstr ""
    501501
    502 #: classes/controller/wordfencels.php:671
     502#: classes/controller/wordfencels.php:645
     503msgid "<strong>CAPTCHA EXPIRED</strong>: The CAPTCHA verification for this login attempt has expired. Please try again."
     504msgstr ""
     505
     506#: classes/controller/wordfencels.php:685
    503507msgid "Login Verification Required"
    504508msgstr ""
    505509
    506 #: classes/controller/wordfencels.php:676
     510#: classes/controller/wordfencels.php:690
    507511msgid "<strong>VERIFICATION REQUIRED</strong>: Additional verification is required for login. If there is a valid account for the provided login credentials, please check the email address associated with it for a verification link to continue logging in."
    508512msgstr ""
    509513
    510 #: classes/controller/wordfencels.php:692
     514#: classes/controller/wordfencels.php:706
    511515msgid "<strong>CODE INVALID</strong>: The 2FA code provided is either expired or invalid. Please try again."
    512516msgstr ""
    513517
    514 #: classes/controller/wordfencels.php:701
     518#: classes/controller/wordfencels.php:715
    515519msgid "<strong>CODE REQUIRED</strong>: Please enter your 2FA code immediately after your password in the same field."
    516520msgstr ""
    517521
    518 #: classes/controller/wordfencels.php:703
     522#: classes/controller/wordfencels.php:717
    519523msgid "<strong>CODE REQUIRED</strong>: Please provide your 2FA code when prompted."
    520524msgstr ""
    521525
    522 #: classes/controller/wordfencels.php:706
     526#: classes/controller/wordfencels.php:720
    523527msgid "<strong>LOGIN BLOCKED</strong>: 2FA is required to be active on your account. Please contact the site administrator."
    524528msgstr ""
    525529
    526 #: classes/controller/wordfencels.php:709
     530#: classes/controller/wordfencels.php:723
    527531msgid "You do not currently have two-factor authentication active on your account, which will be required beginning %s. <a href=\"%s\">Configure 2FA</a>"
    528532msgstr ""
    529533
    530 #: classes/controller/wordfencels.php:759
     534#: classes/controller/wordfencels.php:773
    531535msgid "Email verification succeeded. Please continue logging in."
    532536msgstr ""
    533537
    534 #: classes/controller/wordfencels.php:762
     538#: classes/controller/wordfencels.php:776
    535539msgid "Email verification invalid or expired. Please try again."
    536540msgstr ""
    537541
    538 #: classes/controller/wordfencels.php:816
    539 #: classes/controller/wordfencels.php:819
     542#: classes/controller/wordfencels.php:830
     543#: classes/controller/wordfencels.php:833
    540544msgid "Login Security"
    541545msgstr ""
    542546
    543 #: classes/controller/wordfencels.php:847
     547#: classes/controller/wordfencels.php:861
    544548#: views/settings/options.php:23
    545549#: views/settings/user-stats.php:33
     
    547551msgstr ""
    548552
    549 #: classes/controller/wordfencels.php:851
     553#: classes/controller/wordfencels.php:865
    550554#: views/manage/grace-period.php:22
    551555#: views/options/option-roles.php:57
     
    553557msgstr ""
    554558
    555 #: classes/controller/wordfencels.php:870
     559#: classes/controller/wordfencels.php:884
    556560msgid "Users without 2FA active (%s)"
    557561msgstr ""
    558562
    559 #: classes/controller/wordfencels.php:888
    560 #: classes/controller/wordfencels.php:889
     563#: classes/controller/wordfencels.php:902
     564#: classes/controller/wordfencels.php:903
    561565msgid "Two-Factor Authentication"
    562566msgstr ""
    563567
    564 #: classes/controller/wordfencels.php:889
     568#: classes/controller/wordfencels.php:903
    565569msgid "Learn more<span class=\"wfls-hidden-xs\"> about Two-Factor Authentication</span>"
    566570msgstr ""
    567571
    568 #: classes/controller/wordfencels.php:898
     572#: classes/controller/wordfencels.php:912
    569573msgid "Settings"
    570574msgstr ""
    571575
    572 #: classes/controller/wordfencels.php:899
     576#: classes/controller/wordfencels.php:913
    573577msgid "Login Security Settings"
    574578msgstr ""
    575579
    576 #: classes/controller/wordfencels.php:899
     580#: classes/controller/wordfencels.php:913
    577581msgid "Learn more<span class=\"wfls-hidden-xs\"> about Login Security</span>"
    578582msgstr ""
    579583
    580 #: classes/controller/wordfencels.php:925
     584#: classes/controller/wordfencels.php:939
    581585msgid "<strong>REGISTRATION ATTEMPT BLOCKED</strong>: This site requires a security token created when the page loads for all registration attempts. Please ensure JavaScript is enabled and try again."
    582586msgstr ""
    583587
    584 #: classes/controller/wordfencels.php:932
     588#: classes/controller/wordfencels.php:946
    585589msgid "<strong>REGISTRATION ATTEMPT BLOCKED</strong>: The security token for the login attempt was invalid or expired. Please reload the page and try again."
    586590msgstr ""
    587591
    588 #: classes/controller/wordfencels.php:945
     592#: classes/controller/wordfencels.php:959
    589593msgid "<strong>REGISTRATION BLOCKED</strong>: The registration request was blocked because it was flagged as spam. Please try again or <a href=\"#\" class=\"wfls-registration-captcha-contact\" data-token=\"%s\">contact the site owner</a> for help."
    590594msgstr ""
    591595
    592 #: classes/controller/wordfencels.php:948
     596#: classes/controller/wordfencels.php:962
    593597msgid "<strong>REGISTRATION BLOCKED</strong>: The registration request was blocked because it was flagged as spam. Please try again or contact the site owner for help."
    594598msgstr ""
    595599
    596 #: classes/controller/wordfencels.php:1018
     600#: classes/controller/wordfencels.php:1032
    597601msgid "Wordfence 2FA"
    598602msgstr ""
  • wordfence-login-security/tags/1.1.11/readme.txt

    r3056304 r3063866  
    55Requires PHP: 5.5
    66Tested up to: 6.5
    7 Stable tag: 1.1.10
     7Stable tag: 1.1.11
    88
    99Secure your website with Wordfence Login Security, providing two-factor authentication, login and registration CAPTCHA, and XML-RPC protection.
     
    5858
    5959== Changelog ==
     60
     61= 1.1.11 - April 3, 2024 =
     62* Fix: Revised the behavior of the reCAPTCHA verification to use the documented expiration period of the token and response to avoid sending verification requests too frequently, which could artificially lower scores in some circumstances
    6063
    6164= 1.1.10 - March 11, 2024 =
  • wordfence-login-security/tags/1.1.11/wordfence-login-security.php

    r3049221 r3063866  
    55Author: Wordfence
    66Author URI: https://www.wordfence.com/
    7 Version: 1.1.10
     7Version: 1.1.11
    88Network: true
    99Requires at least: 4.5
     
    3939    define('WORDFENCE_LS_FROM_CORE', ($wfCoreActive && isset($wfCoreLoading) && $wfCoreLoading));
    4040   
    41     define('WORDFENCE_LS_VERSION', '1.1.10');
    42     define('WORDFENCE_LS_BUILD_NUMBER', '1710170444');
     41    define('WORDFENCE_LS_VERSION', '1.1.11');
     42    define('WORDFENCE_LS_BUILD_NUMBER', '1712157269');
    4343
    4444    define('WORDFENCE_LS_PLUGIN_BASENAME', plugin_basename(__FILE__));
  • wordfence-login-security/trunk/classes/controller/captcha.php

    r3049221 r3063866  
    9999                $jsonResponse = wp_remote_retrieve_body($response);
    100100                $decoded = @json_decode($jsonResponse, true);
    101                 if (is_array($decoded) && isset($decoded['success']) && isset($decoded['score']) && isset($decoded['action'])) {
    102                     if ($decoded['success'] && $decoded['action'] == $action) {
    103                         return (float) $decoded['score'];
     101                if (is_array($decoded) && isset($decoded['success'])) {
     102                    if ($decoded['success']) {
     103                        if (isset($decoded['score']) && isset($decoded['action']) && $decoded['action'] == $action) {
     104                            return (float) $decoded['score'];
     105                        }
    104106                    }
    105107                    return false;
  • wordfence-login-security/trunk/classes/controller/users.php

    r3049221 r3063866  
    1515    const META_KEY_ALLOW_GRACE_PERIOD = 'wfls-allow-grace-period';
    1616    const META_KEY_VERIFICATION_TOKENS = 'wfls-verification-tokens';
     17    const META_KEY_CAPTCHA_SCORES = 'wfls-captcha-scores';
    1718    const VERIFICATION_TOKEN_BYTES = 64;
    1819    const VERIFICATION_TOKEN_LIMIT = 5; //Max number of concurrent tokens
    1920    const VERIFICATION_TOKEN_TRANSIENT_PREFIX = 'wfls_verify_';
     21    const CAPTCHA_SCORE_LIMIT = 2; //Max number of captcha scores cached
     22    const CAPTCHA_SCORE_TRANSIENT_PREFIX = 'wfls_captcha_';
     23    const CAPTCHA_SCORE_CACHE_DURATION = 60; //seconds
    2024    const LARGE_USER_BASE_THRESHOLD = 1000;
    2125    const TRUNCATED_ROLE_KEY = 1;
     
    949953        return $userId !== null && ($user === null || $userId === $user->ID);
    950954    }
     955   
     956    /**
     957     * Returns the key used to store a captcha score transient.
     958     *
     959     * @param string $hash
     960     * @return string
     961     */
     962    private function get_captcha_score_transient_key($hash) {
     963        return self::CAPTCHA_SCORE_TRANSIENT_PREFIX . $hash;
     964    }
     965   
     966    /**
     967     * Attempts to look up a stored captcha score for the given hash and user. If found, returns the score. If not,
     968     * returns null.
     969     *
     970     * @param string $hash
     971     * @param \WP_User $user
     972     * @return float|false
     973     */
     974    private function load_captcha_score($hash, $user) {
     975        $key = $this->get_captcha_score_transient_key($hash);
     976        $data = get_transient($key);
     977        if ($data === false) {
     978            return false;
     979        }
     980       
     981        if (!$user->exists() || $data['user'] !== $user->ID) {
     982            return false;
     983        }
     984       
     985        return floatval($data['score']);
     986    }
     987   
     988    /**
     989     * Deletes the stored captcha score if present for the given hash.
     990     *
     991     * @param string $hash
     992     */
     993    private function clear_captcha_score($token, $user) {
     994        $hash = $this->hash_captcha_token($token);
     995        $key = $this->get_captcha_score_transient_key($hash);
     996        delete_transient($key);
     997       
     998        $storedHashes = get_user_meta($user->ID, self::META_KEY_CAPTCHA_SCORES, true);
     999        $validHashes = array();
     1000        if (is_array($storedHashes)) {
     1001            foreach ($storedHashes as $hash) {
     1002                $storedScore = $this->load_captcha_score($hash, $user);
     1003                if ($storedScore !== false) {
     1004                    $validHashes[] = $hash;
     1005                }
     1006            }
     1007        }
     1008        $validHashes = array_slice($validHashes, 0, self::CAPTCHA_SCORE_LIMIT);
     1009        update_user_meta($user->ID, self::META_KEY_CAPTCHA_SCORES, $validHashes);
     1010    }
     1011   
     1012    /**
     1013     * Hashes the captcha token for storage.
     1014     *
     1015     * @param string $token
     1016     * @return string
     1017     */
     1018    private function hash_captcha_token($token) {
     1019        return wp_hash($token);
     1020    }
     1021   
     1022    /**
     1023     * Returns the cached score for the given captcha score and user if available. This action removes it from the cache
     1024     * since the intent is for it only to be used for the initial login request to validate credentials + the follow-up
     1025     * request either finalizing the login (no 2FA set) or with the 2FA token.
     1026     *
     1027     * $expired will be set to `true` if the reason for returning `false` is because the $token is recently expired. It
     1028     * will be false when the $token is either uncached or has been expired long enough to be removed from the internal
     1029     * list.
     1030     *
     1031     * @param string $token
     1032     * @param \WP_User $user
     1033     * @param bool $expired
     1034     * @return float|false
     1035     */
     1036    public function cached_captcha_score($token, $user, &$expired = false) {
     1037        $hash = $this->hash_captcha_token($token);
     1038        $score = $this->load_captcha_score($hash, $user);
     1039        if ($score === false) {
     1040            $storedHashes = get_user_meta($user->ID, self::META_KEY_CAPTCHA_SCORES, true);
     1041            if (is_array($storedHashes)) {
     1042                $expired = in_array($hash, $storedHashes);
     1043            }
     1044        }
     1045       
     1046        $this->clear_captcha_score($token, $user);
     1047        return $score;
     1048    }
     1049   
     1050    /**
     1051     * Caches the $token/$score pair for $user, automatically pruning its cached list to the maximum allowable count
     1052     *
     1053     * @param string $token
     1054     * @param float|false $score
     1055     * @param \WP_User $user
     1056     */
     1057    public function cache_captcha_score($token, $score, $user) {
     1058        if ($score === false) {
     1059            return;
     1060        }
     1061       
     1062        $storedHashes = get_user_meta($user->ID, self::META_KEY_CAPTCHA_SCORES, true);
     1063        $validHashes = array();
     1064        if (is_array($storedHashes)) {
     1065            foreach ($storedHashes as $hash) {
     1066                $storedScore = $this->load_captcha_score($hash, $user);
     1067                if ($storedScore !== false) {
     1068                    $validHashes[] = $hash;
     1069                }
     1070            }
     1071        }
     1072       
     1073        $hash = $this->hash_verification_token($token);
     1074        array_unshift($validHashes, $hash);
     1075        while (count($validHashes) > self::CAPTCHA_SCORE_LIMIT) {
     1076            $excessHash = array_pop($validHashes);
     1077            delete_transient($this->get_captcha_score_transient_key($excessHash));
     1078        }
     1079       
     1080        $key = $this->get_captcha_score_transient_key($hash);
     1081        set_transient($key, array('user' => $user->ID, 'score' => $score), self::CAPTCHA_SCORE_CACHE_DURATION);
     1082        update_user_meta($user->ID, self::META_KEY_CAPTCHA_SCORES, $validHashes);
     1083    }
    9511084
    9521085    public function get_user_count() {
  • wordfence-login-security/trunk/classes/controller/wordfencels.php

    r3049221 r3063866  
    636636            $score = false;
    637637            if ($requireCAPTCHA && !$performVerification) {
    638                 $score = Controller_CAPTCHA::shared()->score($token);
     638                $expired = false;
     639                if (is_object($user) && $user instanceof \WP_User) {
     640                    $score = Controller_Users::shared()->cached_captcha_score($token, $user, $expired);
     641                }
     642               
     643                if ($score === false) {
     644                    if ($expired) {
     645                        return new \WP_Error('wfls_captcha_expired', wp_kses(__('<strong>CAPTCHA EXPIRED</strong>: The CAPTCHA verification for this login attempt has expired. Please try again.', 'wordfence-login-security'), array('strong'=>array())));
     646                    }
     647                   
     648                    $score = Controller_CAPTCHA::shared()->score($token);
     649                   
     650                    if ($score !== false && is_object($user) && $user instanceof \WP_User) {
     651                        Controller_Users::shared()->cache_captcha_score($token, $score, $user);
     652                        Controller_Users::shared()->record_captcha_score($user, $score);
     653                    }
     654                }
     655               
    639656                if ($score === false && !Controller_CAPTCHA::shared()->test_mode()) { //An invalid token will require additional verification (if test mode is not active)
    640657                    $performVerification = true;
    641                 }
    642                 else if (is_object($user) && $user instanceof \WP_User) {
    643                     Controller_Users::shared()->record_captcha_score($user, $score);
    644658                }
    645659            }
  • wordfence-login-security/trunk/languages/wordfence-login-security.pot

    r3049221 r3063866  
    33msgid ""
    44msgstr ""
    5 "Project-Id-Version: Wordfence Login Security 1.1.10\n"
    6 "Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/wordfence-login-security-zip-Cjy0sfiYR\n"
     5"Project-Id-Version: Wordfence Login Security 1.1.11\n"
     6"Report-Msgid-Bugs-To: https://wordpress.org/support/plugin/wordfence-login-security-zip-eZqOVC21Q\n"
    77"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
    88"Language-Team: LANGUAGE <[email protected]>\n"
     
    1010"Content-Type: text/plain; charset=UTF-8\n"
    1111"Content-Transfer-Encoding: 8bit\n"
    12 "POT-Creation-Date: 2024-03-11T15:20:44+00:00\n"
     12"POT-Creation-Date: 2024-04-03T15:14:29+00:00\n"
    1313"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
    1414"X-Generator: WP-CLI 2.7.1\n"
     
    265265msgstr ""
    266266
    267 #: classes/controller/users.php:517
     267#: classes/controller/users.php:521
    268268#: classes/controller/wordfencels.php:486
    269269msgid "2FA Status"
    270270msgstr ""
    271271
    272 #: classes/controller/users.php:521
     272#: classes/controller/users.php:525
    273273msgid "Last Login"
    274274msgstr ""
    275275
    276 #: classes/controller/users.php:523
     276#: classes/controller/users.php:527
    277277msgid "Last CAPTCHA"
    278278msgstr ""
    279279
    280 #: classes/controller/users.php:533
     280#: classes/controller/users.php:537
    281281msgid "Not Allowed"
    282282msgstr ""
    283283
    284 #: classes/controller/users.php:538
     284#: classes/controller/users.php:542
    285285#: classes/controller/wordfencels.php:490
    286286msgid "Active"
    287287msgstr ""
    288288
    289 #: classes/controller/users.php:541
     289#: classes/controller/users.php:545
    290290msgid "Inactive<small class=\"wfls-sub-status\">(Grace Period)</small>"
    291291msgstr ""
    292292
    293 #: classes/controller/users.php:544
     293#: classes/controller/users.php:548
    294294msgid "Locked Out<small class=\"wfls-sub-status\">(Grace Period Disabled)</small>"
    295295msgstr ""
    296296
    297 #: classes/controller/users.php:544
     297#: classes/controller/users.php:548
    298298msgid "Locked Out<small class=\"wfls-sub-status\">(Grace Period Exceeded)</small>"
    299299msgstr ""
    300300
    301 #: classes/controller/users.php:547
     301#: classes/controller/users.php:551
    302302#: classes/controller/wordfencels.php:490
    303303msgid "Inactive"
    304304msgstr ""
    305305
    306 #: classes/controller/users.php:560
     306#: classes/controller/users.php:564
    307307msgid "(not required)"
    308308msgstr ""
    309309
    310 #: classes/controller/users.php:654
     310#: classes/controller/users.php:658
    311311msgid "Edit two-factor authentication for %s"
    312312msgstr ""
    313313
    314 #: classes/controller/users.php:654
     314#: classes/controller/users.php:658
    315315#: views/settings/options.php:9
    316316msgid "2FA"
    317317msgstr ""
    318318
    319 #: classes/controller/users.php:665
     319#: classes/controller/users.php:669
    320320#: views/settings/user-stats.php:25
    321321msgid "2FA Active"
    322322msgstr ""
    323323
    324 #: classes/controller/users.php:666
     324#: classes/controller/users.php:670
    325325#: views/settings/user-stats.php:26
    326326msgid "2FA Inactive"
     
    442442
    443443#: classes/controller/wordfencels.php:490
    444 #: classes/controller/wordfencels.php:855
     444#: classes/controller/wordfencels.php:869
    445445#: views/manage/grace-period.php:22
    446446msgid "Locked Out"
     
    500500msgstr ""
    501501
    502 #: classes/controller/wordfencels.php:671
     502#: classes/controller/wordfencels.php:645
     503msgid "<strong>CAPTCHA EXPIRED</strong>: The CAPTCHA verification for this login attempt has expired. Please try again."
     504msgstr ""
     505
     506#: classes/controller/wordfencels.php:685
    503507msgid "Login Verification Required"
    504508msgstr ""
    505509
    506 #: classes/controller/wordfencels.php:676
     510#: classes/controller/wordfencels.php:690
    507511msgid "<strong>VERIFICATION REQUIRED</strong>: Additional verification is required for login. If there is a valid account for the provided login credentials, please check the email address associated with it for a verification link to continue logging in."
    508512msgstr ""
    509513
    510 #: classes/controller/wordfencels.php:692
     514#: classes/controller/wordfencels.php:706
    511515msgid "<strong>CODE INVALID</strong>: The 2FA code provided is either expired or invalid. Please try again."
    512516msgstr ""
    513517
    514 #: classes/controller/wordfencels.php:701
     518#: classes/controller/wordfencels.php:715
    515519msgid "<strong>CODE REQUIRED</strong>: Please enter your 2FA code immediately after your password in the same field."
    516520msgstr ""
    517521
    518 #: classes/controller/wordfencels.php:703
     522#: classes/controller/wordfencels.php:717
    519523msgid "<strong>CODE REQUIRED</strong>: Please provide your 2FA code when prompted."
    520524msgstr ""
    521525
    522 #: classes/controller/wordfencels.php:706
     526#: classes/controller/wordfencels.php:720
    523527msgid "<strong>LOGIN BLOCKED</strong>: 2FA is required to be active on your account. Please contact the site administrator."
    524528msgstr ""
    525529
    526 #: classes/controller/wordfencels.php:709
     530#: classes/controller/wordfencels.php:723
    527531msgid "You do not currently have two-factor authentication active on your account, which will be required beginning %s. <a href=\"%s\">Configure 2FA</a>"
    528532msgstr ""
    529533
    530 #: classes/controller/wordfencels.php:759
     534#: classes/controller/wordfencels.php:773
    531535msgid "Email verification succeeded. Please continue logging in."
    532536msgstr ""
    533537
    534 #: classes/controller/wordfencels.php:762
     538#: classes/controller/wordfencels.php:776
    535539msgid "Email verification invalid or expired. Please try again."
    536540msgstr ""
    537541
    538 #: classes/controller/wordfencels.php:816
    539 #: classes/controller/wordfencels.php:819
     542#: classes/controller/wordfencels.php:830
     543#: classes/controller/wordfencels.php:833
    540544msgid "Login Security"
    541545msgstr ""
    542546
    543 #: classes/controller/wordfencels.php:847
     547#: classes/controller/wordfencels.php:861
    544548#: views/settings/options.php:23
    545549#: views/settings/user-stats.php:33
     
    547551msgstr ""
    548552
    549 #: classes/controller/wordfencels.php:851
     553#: classes/controller/wordfencels.php:865
    550554#: views/manage/grace-period.php:22
    551555#: views/options/option-roles.php:57
     
    553557msgstr ""
    554558
    555 #: classes/controller/wordfencels.php:870
     559#: classes/controller/wordfencels.php:884
    556560msgid "Users without 2FA active (%s)"
    557561msgstr ""
    558562
    559 #: classes/controller/wordfencels.php:888
    560 #: classes/controller/wordfencels.php:889
     563#: classes/controller/wordfencels.php:902
     564#: classes/controller/wordfencels.php:903
    561565msgid "Two-Factor Authentication"
    562566msgstr ""
    563567
    564 #: classes/controller/wordfencels.php:889
     568#: classes/controller/wordfencels.php:903
    565569msgid "Learn more<span class=\"wfls-hidden-xs\"> about Two-Factor Authentication</span>"
    566570msgstr ""
    567571
    568 #: classes/controller/wordfencels.php:898
     572#: classes/controller/wordfencels.php:912
    569573msgid "Settings"
    570574msgstr ""
    571575
    572 #: classes/controller/wordfencels.php:899
     576#: classes/controller/wordfencels.php:913
    573577msgid "Login Security Settings"
    574578msgstr ""
    575579
    576 #: classes/controller/wordfencels.php:899
     580#: classes/controller/wordfencels.php:913
    577581msgid "Learn more<span class=\"wfls-hidden-xs\"> about Login Security</span>"
    578582msgstr ""
    579583
    580 #: classes/controller/wordfencels.php:925
     584#: classes/controller/wordfencels.php:939
    581585msgid "<strong>REGISTRATION ATTEMPT BLOCKED</strong>: This site requires a security token created when the page loads for all registration attempts. Please ensure JavaScript is enabled and try again."
    582586msgstr ""
    583587
    584 #: classes/controller/wordfencels.php:932
     588#: classes/controller/wordfencels.php:946
    585589msgid "<strong>REGISTRATION ATTEMPT BLOCKED</strong>: The security token for the login attempt was invalid or expired. Please reload the page and try again."
    586590msgstr ""
    587591
    588 #: classes/controller/wordfencels.php:945
     592#: classes/controller/wordfencels.php:959
    589593msgid "<strong>REGISTRATION BLOCKED</strong>: The registration request was blocked because it was flagged as spam. Please try again or <a href=\"#\" class=\"wfls-registration-captcha-contact\" data-token=\"%s\">contact the site owner</a> for help."
    590594msgstr ""
    591595
    592 #: classes/controller/wordfencels.php:948
     596#: classes/controller/wordfencels.php:962
    593597msgid "<strong>REGISTRATION BLOCKED</strong>: The registration request was blocked because it was flagged as spam. Please try again or contact the site owner for help."
    594598msgstr ""
    595599
    596 #: classes/controller/wordfencels.php:1018
     600#: classes/controller/wordfencels.php:1032
    597601msgid "Wordfence 2FA"
    598602msgstr ""
  • wordfence-login-security/trunk/readme.txt

    r3056304 r3063866  
    5858
    5959== Changelog ==
     60
     61= 1.1.11 - April 3, 2024 =
     62* Fix: Revised the behavior of the reCAPTCHA verification to use the documented expiration period of the token and response to avoid sending verification requests too frequently, which could artificially lower scores in some circumstances
    6063
    6164= 1.1.10 - March 11, 2024 =
  • wordfence-login-security/trunk/wordfence-login-security.php

    r3049221 r3063866  
    55Author: Wordfence
    66Author URI: https://www.wordfence.com/
    7 Version: 1.1.10
     7Version: 1.1.11
    88Network: true
    99Requires at least: 4.5
     
    3939    define('WORDFENCE_LS_FROM_CORE', ($wfCoreActive && isset($wfCoreLoading) && $wfCoreLoading));
    4040   
    41     define('WORDFENCE_LS_VERSION', '1.1.10');
    42     define('WORDFENCE_LS_BUILD_NUMBER', '1710170444');
     41    define('WORDFENCE_LS_VERSION', '1.1.11');
     42    define('WORDFENCE_LS_BUILD_NUMBER', '1712157269');
    4343
    4444    define('WORDFENCE_LS_PLUGIN_BASENAME', plugin_basename(__FILE__));
Note: See TracChangeset for help on using the changeset viewer.