Make WordPress Core

Changeset 60125 for branches/6.8


Ignore:
Timestamp:
04/03/2025 02:36:46 PM (17 months ago)
Author:
johnbillion
Message:

Application Passwords: Correct the fallback behaviour for application passwords that don't use a generic hash.

Application passwords that aren't hashed using BLAKE2b should be checked using wp_check_password() rather than assuming they were hashed with phpass. This provides full back compat support for application passwords that were created via an overridden wp_hash_password() function that uses an alternative hashing algorithm.

Reviewed by audrasjb.
Merges [60123] into the 6.8 branch.

Props snicco, debarghyabanerjee, peterwilsoncc, jorbin, johnbillion.

Fixes #63203

Location:
branches/6.8
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • branches/6.8

  • branches/6.8/src/wp-includes/class-wp-application-passwords.php

    r59828 r60125  
    503503                string $hash
    504504        ): bool {
     505                if ( ! str_starts_with( $hash, '$generic$' ) ) {
     506                        /*
     507                         * If the hash doesn't start with `$generic$`, it is a hash created with `wp_hash_password()`.
     508                         * This is the case for application passwords created before 6.8.0.
     509                         */
     510                        return wp_check_password( $password, $hash );
     511                }
     512
    505513                return wp_verify_fast_hash( $password, $hash );
    506514        }
  • branches/6.8/src/wp-includes/functions.php

    r60085 r60125  
    91519151 *
    91529152 * The function uses Sodium to hash the message and compare it to the hashed value. If the hash is not a generic hash,
    9153  * the hash is treated as a phpass portable hash in order to provide backward compatibility for application passwords
    9154  * which were hashed using phpass prior to WordPress 6.8.0.
     9153 * the hash is treated as a phpass portable hash in order to provide backward compatibility for passwords and security
     9154 * keys which were hashed using phpass prior to WordPress 6.8.0.
    91559155 *
    91569156 * @since 6.8.0
  • branches/6.8/tests/phpunit/tests/auth.php

    r59904 r60125  
    11361136
    11371137        /**
     1138         * @ticket 21022
     1139         * @ticket 63203
     1140         */
     1141        public function test_plain_bcrypt_application_password_is_accepted() {
     1142                add_filter( 'application_password_is_api_request', '__return_true' );
     1143                add_filter( 'wp_is_application_passwords_available', '__return_true' );
     1144
     1145                $password = 'password';
     1146
     1147                // Set an application password with plain bcrypt, which mimics a password that was hashed with
     1148                // a custom `wp_hash_password()` in use.
     1149                $uuid = self::set_application_password_with_plain_bcrypt( $password, self::$user_id );
     1150
     1151                // Authenticate.
     1152                $user = wp_authenticate_application_password( null, self::USER_LOGIN, $password );
     1153
     1154                // Verify that the plain bcrypt hash for the application password was valid.
     1155                $this->assertNotWPError( $user );
     1156                $this->assertInstanceOf( 'WP_User', $user );
     1157                $this->assertSame( self::$user_id, $user->ID );
     1158        }
     1159
     1160        /**
    11381161         * @dataProvider data_usernames
    11391162         *
     
    15901613                );
    15911614                $this->assertSame( $item['uuid'], rest_get_authenticated_app_password() );
     1615        }
     1616
     1617        /**
     1618         * @ticket 21022
     1619         * @ticket 63203
     1620         *
     1621         * @covers WP_Application_Passwords::create_new_application_password
     1622         */
     1623        public function test_application_password_is_hashed_with_fast_hash() {
     1624                // Create a new app-only password.
     1625                list( , $item ) = WP_Application_Passwords::create_new_application_password( self::$user_id, array( 'name' => 'phpunit' ) );
     1626
     1627                $this->assertStringStartsWith( '$generic$', $item['password'] );
    15921628        }
    15931629
     
    19672003         * Test the tests
    19682004         *
     2005         * @covers Tests_Auth::set_application_password_with_plain_bcrypt
     2006         *
     2007         * @ticket 21022
     2008         * @ticket 63203
     2009         */
     2010        public function test_set_application_password_with_plain_bcrypt() {
     2011                // Set an application password with the plain_bcrypt algorithm.
     2012                $uuid = self::set_application_password_with_plain_bcrypt( 'password', self::$user_id );
     2013
     2014                // Ensure the password is hashed with plain_bcrypt.
     2015                $hash = WP_Application_Passwords::get_user_application_password( self::$user_id, $uuid )['password'];
     2016                $this->assertStringStartsWith( '$2y$', $hash );
     2017        }
     2018
     2019        /**
     2020         * Creates an application password that is hashed using bcrypt instead of the generic algorithm.
     2021         *
     2022         * This is ultimately used to mimic a plugged version of `wp_hash_password()` that uses bcrypt and
     2023         * facilitate backwards compatibility testing.
     2024         *
     2025         * @param string $password The password to hash.
     2026         * @param int    $user_id  The user ID to associate the password with.
     2027         * @return string The UUID of the application password.
     2028         */
     2029        private static function set_application_password_with_plain_bcrypt( string $password, int $user_id ) {
     2030                return self::set_application_password( password_hash( $password, PASSWORD_BCRYPT ), $user_id );
     2031        }
     2032
     2033        /**
     2034         * Test the tests
     2035         *
    19692036         * @covers Tests_Auth::set_application_password_with_phpass
    19702037         *
     
    19802047        }
    19812048
     2049        /**
     2050         * Creates an application password that is hashed using a phpass portable hash instead of the generic algorithm.
     2051         *
     2052         * This facilitate backwards compatibility testing.
     2053         *
     2054         * @param string $password The password to hash.
     2055         * @param int    $user_id  The user ID to associate the password with.
     2056         * @return string The UUID of the application password.
     2057         */
    19822058        private static function set_application_password_with_phpass( string $password, int $user_id ) {
     2059                return self::set_application_password( self::$wp_hasher->HashPassword( $password ), $user_id );
     2060        }
     2061
     2062        /**
     2063         * Creates an application password using the given password hash.
     2064         *
     2065         * @param string $hash    The password hash.
     2066         * @param int    $user_id The user ID to associate the password with.
     2067         * @return string The UUID of the application password.
     2068         */
     2069        private static function set_application_password( string $hash, int $user_id ) {
    19832070                $uuid = wp_generate_uuid4();
    19842071                $item = array(
     
    19862073                        'app_id'    => '',
    19872074                        'name'      => 'Test',
    1988                         'password'  => self::$wp_hasher->HashPassword( $password ),
     2075                        'password'  => $hash,
    19892076                        'created'   => time(),
    19902077                        'last_used' => null,
Note: See TracChangeset for help on using the changeset viewer.