Skip to content

Commit b5e0c16

Browse files
committed
Security: Reduce the length of the hash returned by wp_fast_hash() so it can be used in the user_activation_key field when a legacy database schema is still in use.
This reduces the hash length from 32 bytes to 30 so the overall length of an activation key after encoding, prefixing, and prepending a timestamp fits into 60 bytes. A key is also introduced for domain separation. This doesn't affect the output length. Props dd32, paragoninitiativeenterprises, peterwilsoncc, johnbillion Fixes #21022 git-svn-id: https://develop.svn.wordpress.org/trunk@59904 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 1b236aa commit b5e0c16

File tree

2 files changed

+95
-1
lines changed

2 files changed

+95
-1
lines changed

src/wp-includes/functions.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -9142,7 +9142,8 @@ function wp_fast_hash(
91429142
#[\SensitiveParameter]
91439143
string $message
91449144
): string {
9145-
return '$generic$' . sodium_bin2hex( sodium_crypto_generichash( $message ) );
9145+
$hashed = sodium_crypto_generichash( $message, 'wp_fast_hash_6.8+', 30 );
9146+
return '$generic$' . sodium_bin2base64( $hashed, SODIUM_BASE64_VARIANT_URLSAFE_NO_PADDING );
91469147
}
91479148

91489149
/**

tests/phpunit/tests/auth.php

+93
Original file line numberDiff line numberDiff line change
@@ -473,6 +473,99 @@ public function test_password_is_hashed_with_bcrypt() {
473473
$this->assertSame( self::$user_id, $user->ID );
474474
}
475475

476+
public function data_passwords(): array {
477+
return array(
478+
array( 'a' ),
479+
array( 'password' ),
480+
array( str_repeat( 'a', self::$password_length_limit ) ),
481+
);
482+
}
483+
484+
/**
485+
* Ensure the hash of the user password remains less than 64 characters in length to account for the old users table schema.
486+
*
487+
* @ticket 21022
488+
* @dataProvider data_passwords
489+
*/
490+
public function test_user_password_against_old_users_table_schema( string $password ) {
491+
// Mimic the schema of the users table prior to WordPress 4.4.
492+
add_filter( 'wp_pre_insert_user_data', array( $this, 'mimic_users_schema_prior_to_44' ) );
493+
494+
$username = 'old-schema-user';
495+
496+
// Create a user.
497+
$user_id = $this->factory()->user->create(
498+
array(
499+
'user_login' => $username,
500+
'user_email' => '[email protected]',
501+
'user_pass' => $password,
502+
)
503+
);
504+
505+
// Check the user can authenticate.
506+
$user = wp_authenticate( $username, $password );
507+
508+
$this->assertNotWPError( $user );
509+
$this->assertInstanceOf( 'WP_User', $user );
510+
$this->assertSame( $user_id, $user->ID, 'User should be able to authenticate' );
511+
$this->assertNotSame( self::$user_id, $user->ID, 'A unique user must be created for this test, the shared fixture must not be used' );
512+
}
513+
514+
/**
515+
* Ensure the hash of the user activation key remains less than 60 characters in length to account for the old users table schema.
516+
*
517+
* @ticket 21022
518+
*/
519+
public function test_user_activation_key_against_old_users_table_schema() {
520+
// Mimic the schema of the users table prior to WordPress 4.4.
521+
add_filter( 'wp_pre_insert_user_data', array( $this, 'mimic_users_schema_prior_to_44' ) );
522+
523+
$username = 'old-schema-user';
524+
525+
// Create a user.
526+
$user_id = $this->factory()->user->create(
527+
array(
528+
'user_login' => $username,
529+
'user_email' => '[email protected]',
530+
)
531+
);
532+
533+
$user = get_userdata( $user_id );
534+
$key = get_password_reset_key( $user );
535+
536+
// A correctly saved key should be accepted.
537+
$check = check_password_reset_key( $key, $user->user_login );
538+
539+
$this->assertNotWPError( $check );
540+
$this->assertInstanceOf( 'WP_User', $check );
541+
$this->assertSame( $user->ID, $check->ID );
542+
$this->assertNotSame( self::$user_id, $user->ID, 'A unique user must be created for this test, the shared fixture must not be used' );
543+
}
544+
545+
/*
546+
* Fake the schema of the users table prior to WordPress 4.4 to mimic sites that are using the
547+
* `DO_NOT_UPGRADE_GLOBAL_TABLES` constant and have not updated the users table schema.
548+
*
549+
* The schema of the wp_users table on wordpress.org has not been updated since the schema was changed in [35638]
550+
* for WordPress 4.4, which means the `user_activation_key` field remains at 60 characters length and the `user_pass`
551+
* field remains at 64 characters length instead of the expected 255. Although this is unlikely to affect other
552+
* sites, this can be accommodated for in the codebase.
553+
*
554+
* Actually altering the database schema during tests will commit the transaction and break subsequent tests, hence
555+
* the use of this filter.
556+
*/
557+
public function mimic_users_schema_prior_to_44( array $data ): array {
558+
if ( isset( $data['user_pass'] ) ) {
559+
$this->assertLessThanOrEqual( 64, strlen( $data['user_pass'] ) );
560+
}
561+
562+
if ( isset( $data['user_activation_key'] ) ) {
563+
$this->assertLessThanOrEqual( 60, strlen( $data['user_activation_key'] ) );
564+
}
565+
566+
return $data;
567+
}
568+
476569
/**
477570
* @ticket 21022
478571
*/

0 commit comments

Comments
 (0)