Make WordPress Core


Ignore:
Timestamp:
02/17/2025 11:22:33 AM (20 months ago)
Author:
johnbillion
Message:

Security: Switch to using bcrypt for hashing user passwords and BLAKE2b for hashing application passwords and security keys.

Passwords and security keys that were saved in prior versions of WordPress will continue to work. Each user's password will be opportunistically rehashed and resaved when they next subsequently log in using a valid password.

The following new functions have been introduced:

  • wp_password_needs_rehash()
  • wp_fast_hash()
  • wp_verify_fast_hash()

The following new filters have been introduced:

  • password_needs_rehash
  • wp_hash_password_algorithm
  • wp_hash_password_options

Props ayeshrajans, bgermann, dd32, deadduck169, desrosj, haozi, harrym, iandunn, jammycakes, joehoyle, johnbillion, mbijon, mojorob, mslavco, my1xt, nacin, otto42, paragoninitiativeenterprises, paulkevan, rmccue, ryanhellyer, scribu, swalkinshaw, synchro, th23, timothyblynjacobs, tomdxw, westi, xknown.

Additional thanks go to the Roots team, Soatok, Calvin Alkan, and Raphael Ahrens.

Fixes #21022, #44628

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/user.php

    r59817 r59828  
    206206        }
    207207
    208         if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) {
     208        $valid = wp_check_password( $password, $user->user_pass, $user->ID );
     209
     210        if ( ! $valid ) {
    209211                return new WP_Error(
    210212                        'incorrect_password',
    … …  
    220222        }
    221223
     224        if ( wp_password_needs_rehash( $user->user_pass, $user->ID ) ) {
     225                wp_set_password( $password, $user->ID );
     226        }
     227
    222228        return $user;
    223229}
    … …  
    283289        }
    284290
    285         if ( ! wp_check_password( $password, $user->user_pass, $user->ID ) ) {
     291        $valid = wp_check_password( $password, $user->user_pass, $user->ID );
     292
     293        if ( ! $valid ) {
    286294                return new WP_Error(
    287295                        'incorrect_password',
    … …  
    295303                        '</a>'
    296304                );
     305        }
     306
     307        if ( wp_password_needs_rehash( $user->user_pass, $user->ID ) ) {
     308                wp_set_password( $password, $user->ID );
    297309        }
    298310
    … …  
    446458
    447459        foreach ( $hashed_passwords as $key => $item ) {
    448                 if ( ! wp_check_password( $password, $item['password'], $user->ID ) ) {
     460                if ( ! WP_Application_Passwords::check_password( $password, $item['password'] ) ) {
    449461                        continue;
    450462                }
    … …  
    24322444         * @since 4.9.0
    24332445         * @since 5.8.0 The `$userdata` parameter was added.
     2446         * @since 6.8.0 The user's password is now hashed using bcrypt instead of phpass.
    24342447         *
    24352448         * @param array    $data {
    … …  
    29792992 * @since 4.4.0
    29802993 *
    2981  * @global PasswordHash $wp_hasher Portable PHP password hashing framework instance.
    2982  *
    29832994 * @param WP_User $user User to retrieve password reset key for.
    29842995 * @return string|WP_Error Password reset key on success. WP_Error on error.
    29852996 */
    29862997function get_password_reset_key( $user ) {
    2987         global $wp_hasher;
    2988 
    29892998        if ( ! ( $user instanceof WP_User ) ) {
    29902999                return new WP_Error( 'invalidcombo', __( '<strong>Error:</strong> There is no account with that username or email address.' ) );
    … …  
    30323041        do_action( 'retrieve_password_key', $user->user_login, $key );
    30333042
    3034         // Now insert the key, hashed, into the DB.
    3035         if ( empty( $wp_hasher ) ) {
    3036                 require_once ABSPATH . WPINC . '/class-phpass.php';
    3037                 $wp_hasher = new PasswordHash( 8, true );
    3038         }
    3039 
    3040         $hashed = time() . ':' . $wp_hasher->HashPassword( $key );
     3043        $hashed = time() . ':' . wp_fast_hash( $key );
    30413044
    30423045        $key_saved = wp_update_user(
    … …  
    30643067 * @since 3.1.0
    30653068 *
    3066  * @global PasswordHash $wp_hasher Portable PHP password hashing framework instance.
    3067  *
    3068  * @param string $key       Hash to validate sending user's password.
     3069 * @param string $key       The password reset key.
    30693070 * @param string $login     The user login.
    30703071 * @return WP_User|WP_Error WP_User object on success, WP_Error object for invalid or expired keys.
    … …  
    30753076        $login
    30763077) {
    3077         global $wp_hasher;
    3078 
    30793078        $key = preg_replace( '/[^a-z0-9]/i', '', $key );
    30803079
    … …  
    30913090        if ( ! $user ) {
    30923091                return new WP_Error( 'invalid_key', __( 'Invalid key.' ) );
    3093         }
    3094 
    3095         if ( empty( $wp_hasher ) ) {
    3096                 require_once ABSPATH . WPINC . '/class-phpass.php';
    3097                 $wp_hasher = new PasswordHash( 8, true );
    30983092        }
    30993093
    … …  
    31193113        }
    31203114
    3121         $hash_is_correct = $wp_hasher->CheckPassword( $key, $pass_key );
     3115        $hash_is_correct = wp_verify_fast_hash( $key, $pass_key );
    31223116
    31233117        if ( $hash_is_correct && $expiration_time && time() < $expiration_time ) {
    … …  
    31343128                /**
    31353129                 * Filters the return value of check_password_reset_key() when an
    3136                  * old-style key is used.
     3130                 * old-style key or an expired key is used.
    31373131                 *
    31383132                 * @since 3.7.0 Previously plain-text keys were stored in the database.
    … …  
    31553149 * @since 5.7.0 Added `$user_login` parameter.
    31563150 *
    3157  * @global wpdb         $wpdb      WordPress database abstraction object.
    3158  * @global PasswordHash $wp_hasher Portable PHP password hashing framework instance.
     3151 * @global wpdb $wpdb WordPress database abstraction object.
    31593152 *
    31603153 * @param string $user_login Optional. Username to send a password retrieval email for.
    … …  
    49374930 * @since 4.9.6
    49384931 *
    4939  * @global PasswordHash $wp_hasher Portable PHP password hashing framework instance.
    4940  *
    49414932 * @param int $request_id Request ID.
    49424933 * @return string Confirmation key.
    49434934 */
    49444935function wp_generate_user_request_key( $request_id ) {
    4945         global $wp_hasher;
    4946 
    49474936        // Generate something random for a confirmation key.
    49484937        $key = wp_generate_password( 20, false );
    49494938
    4950         // Return the key, hashed.
    4951         if ( empty( $wp_hasher ) ) {
    4952                 require_once ABSPATH . WPINC . '/class-phpass.php';
    4953                 $wp_hasher = new PasswordHash( 8, true );
    4954         }
    4955 
     4939        // Save the key, hashed.
    49564940        wp_update_post(
    49574941                array(
    49584942                        'ID'            => $request_id,
    49594943                        'post_status'   => 'request-pending',
    4960                         'post_password' => $wp_hasher->HashPassword( $key ),
     4944                        'post_password' => wp_fast_hash( $key ),
    49614945                )
    49624946        );
    … …  
    49694953 *
    49704954 * @since 4.9.6
    4971  *
    4972  * @global PasswordHash $wp_hasher Portable PHP password hashing framework instance.
    49734955 *
    49744956 * @param string $request_id ID of the request being confirmed.
    … …  
    49814963        $key
    49824964) {
    4983         global $wp_hasher;
    4984 
    49854965        $request_id       = absint( $request_id );
    49864966        $request          = wp_get_user_request( $request_id );
    … …  
    50004980        }
    50014981
    5002         if ( empty( $wp_hasher ) ) {
    5003                 require_once ABSPATH . WPINC . '/class-phpass.php';
    5004                 $wp_hasher = new PasswordHash( 8, true );
    5005         }
    5006 
    50074982        /**
    50084983         * Filters the expiration time of confirm keys.
    … …  
    50154990        $expiration_time     = $key_request_time + $expiration_duration;
    50164991
    5017         if ( ! $wp_hasher->CheckPassword( $key, $saved_key ) ) {
     4992        if ( ! wp_verify_fast_hash( $key, $saved_key ) ) {
    50184993                return new WP_Error( 'invalid_key', __( 'The confirmation key is invalid for this personal data request.' ) );
    50194994        }
Note: See TracChangeset for help on using the changeset viewer.