Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 23 additions & 17 deletions class-two-factor-core.php
Original file line number Diff line number Diff line change
Expand Up @@ -529,7 +529,7 @@ public static function get_user_two_factor_revalidate_url( $interim = false ) {
* @return boolean
*/
public static function is_valid_user_action( $user_id, $action ) {
$request_nonce = isset( $_REQUEST[ self::USER_SETTINGS_ACTION_NONCE_QUERY_ARG ] ) ? wp_unslash( $_REQUEST[ self::USER_SETTINGS_ACTION_NONCE_QUERY_ARG ] ) : '';
$request_nonce = isset( $_REQUEST[ self::USER_SETTINGS_ACTION_NONCE_QUERY_ARG ] ) ? wp_unslash( $_REQUEST[ self::USER_SETTINGS_ACTION_NONCE_QUERY_ARG ] ) : ''; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Value only passed to wp_verify_nonce().

if ( ! $user_id || ! $action || ! $request_nonce ) {
return false;
Expand All @@ -550,8 +550,8 @@ public static function is_valid_user_action( $user_id, $action ) {
*/
public static function current_user_being_edited() {
// Try to resolve the user ID from the request first.
if ( ! empty( $_REQUEST['user_id'] ) ) {
$user_id = intval( $_REQUEST['user_id'] );
if ( ! empty( $_REQUEST['user_id'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified in trigger_user_settings_action() via is_valid_user_action() before any state change.
$user_id = intval( $_REQUEST['user_id'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified in trigger_user_settings_action() via is_valid_user_action() before any state change.

if ( current_user_can( 'edit_user', $user_id ) ) {
return $user_id;
Expand All @@ -570,7 +570,7 @@ public static function current_user_being_edited() {
* @return void
*/
public static function trigger_user_settings_action() {
$action = isset( $_REQUEST[ self::USER_SETTINGS_ACTION_QUERY_VAR ] ) ? wp_unslash( $_REQUEST[ self::USER_SETTINGS_ACTION_QUERY_VAR ] ) : '';
$action = isset( $_REQUEST[ self::USER_SETTINGS_ACTION_QUERY_VAR ] ) ? wp_unslash( $_REQUEST[ self::USER_SETTINGS_ACTION_QUERY_VAR ] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce verified in is_valid_user_action() before do_action.
$user_id = self::current_user_being_edited();

if ( self::is_valid_user_action( $user_id, $action ) ) {
Expand Down Expand Up @@ -984,7 +984,7 @@ public static function show_two_factor_login( $user ) {
wp_die( esc_html__( 'Failed to create a login nonce.', 'two-factor' ) );
}

$redirect_to = isset( $_REQUEST['redirect_to'] ) ? $_REQUEST['redirect_to'] : admin_url();
$redirect_to = isset( $_REQUEST['redirect_to'] ) ? esc_url_raw( wp_unslash( $_REQUEST['redirect_to'] ) ) : admin_url(); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Value only used for redirect; auth protected by 2FA login nonce later.

self::login_html( $user, $login_nonce['key'], $redirect_to );
}
Expand Down Expand Up @@ -1038,6 +1038,12 @@ public static function maybe_show_reset_password_notice( $errors ) {
return $errors;
}

// Verify login form nonce when present (e.g. wp-login.php); skip only when nonce is not sent (custom login forms).
if ( isset( $_POST['_wpnonce'] ) && ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['_wpnonce'] ) ), 'log-in' ) ) {
return $errors;
}

// phpcs:ignore WordPress.Security.NonceVerification.Missing -- Nonce verified above when _wpnonce present; absent for custom login forms.
$user_name = sanitize_user( wp_unslash( $_POST['log'] ) );
$attempted_user = get_user_by( 'login', $user_name );
if ( ! $attempted_user && str_contains( $user_name, '@' ) ) {
Expand Down Expand Up @@ -1558,11 +1564,11 @@ public static function rest_api_can_edit_user_and_update_two_factor_options( $us
* @since 0.2.0
*/
public static function login_form_validate_2fa() {
$wp_auth_id = ! empty( $_REQUEST['wp-auth-id'] ) ? absint( $_REQUEST['wp-auth-id'] ) : 0;
$nonce = ! empty( $_REQUEST['wp-auth-nonce'] ) ? wp_unslash( $_REQUEST['wp-auth-nonce'] ) : '';
$provider = ! empty( $_REQUEST['provider'] ) ? wp_unslash( $_REQUEST['provider'] ) : '';
$redirect_to = ! empty( $_REQUEST['redirect_to'] ) ? wp_unslash( $_REQUEST['redirect_to'] ) : '';
$is_post_request = ( 'POST' === strtoupper( $_SERVER['REQUEST_METHOD'] ) );
$wp_auth_id = ! empty( $_REQUEST['wp-auth-id'] ) ? absint( $_REQUEST['wp-auth-id'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified in _login_form_validate_2fa() via verify_login_nonce() before any use.
$nonce = ! empty( $_REQUEST['wp-auth-nonce'] ) ? wp_unslash( $_REQUEST['wp-auth-nonce'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce verified in _login_form_validate_2fa() before any use.
$provider = ! empty( $_REQUEST['provider'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['provider'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified in _login_form_validate_2fa() before any use.
$redirect_to = ! empty( $_REQUEST['redirect_to'] ) ? esc_url_raw( wp_unslash( $_REQUEST['redirect_to'] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified in _login_form_validate_2fa() before any use.
$is_post_request = isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' === strtoupper( $_SERVER['REQUEST_METHOD'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- REQUEST_METHOD is not user input.
$user = get_user_by( 'id', $wp_auth_id );

if ( ! $wp_auth_id || ! $nonce || ! $user ) {
Expand Down Expand Up @@ -1624,7 +1630,7 @@ public static function _login_form_validate_2fa( $user, $nonce = '', $provider =
delete_user_meta( $user->ID, self::USER_FAILED_LOGIN_ATTEMPTS_KEY );

$rememberme = false;
if ( isset( $_REQUEST['rememberme'] ) && $_REQUEST['rememberme'] ) {
if ( isset( $_REQUEST['rememberme'] ) && $_REQUEST['rememberme'] ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Request read only after successful verify_login_nonce() in this request.
$rememberme = true;
}

Expand Down Expand Up @@ -1665,7 +1671,7 @@ public static function _login_form_validate_2fa( $user, $nonce = '', $provider =
$interim_login = isset( $_REQUEST['interim-login'] ); // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited,WordPress.Security.NonceVerification.Recommended

if ( $interim_login ) {
$customize_login = isset( $_REQUEST['customize-login'] );
$customize_login = isset( $_REQUEST['customize-login'] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Request read only after successful verify_login_nonce() in this request.
if ( $customize_login ) {
wp_enqueue_script( 'customize-base' );
wp_add_inline_script(
Expand Down Expand Up @@ -1699,10 +1705,10 @@ public static function _login_form_validate_2fa( $user, $nonce = '', $provider =
* @since 0.9.0
*/
public static function login_form_revalidate_2fa() {
$nonce = ! empty( $_REQUEST['wp-auth-nonce'] ) ? wp_unslash( $_REQUEST['wp-auth-nonce'] ) : '';
$provider = ! empty( $_REQUEST['provider'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['provider'] ) ) : false;
$redirect_to = ! empty( $_REQUEST['redirect_to'] ) ? wp_unslash( $_REQUEST['redirect_to'] ) : admin_url();
$is_post_request = ( 'POST' === strtoupper( $_SERVER['REQUEST_METHOD'] ) );
$nonce = ! empty( $_REQUEST['wp-auth-nonce'] ) ? wp_unslash( $_REQUEST['wp-auth-nonce'] ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Nonce verified in _login_form_revalidate_2fa() for POST before processing.
$provider = ! empty( $_REQUEST['provider'] ) ? sanitize_text_field( wp_unslash( $_REQUEST['provider'] ) ) : false; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified in _login_form_revalidate_2fa() for POST before processing.
$redirect_to = ! empty( $_REQUEST['redirect_to'] ) ? esc_url_raw( wp_unslash( $_REQUEST['redirect_to'] ) ) : admin_url(); // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Nonce verified in _login_form_revalidate_2fa() for POST before processing.
$is_post_request = isset( $_SERVER['REQUEST_METHOD'] ) && 'POST' === strtoupper( $_SERVER['REQUEST_METHOD'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- REQUEST_METHOD is not user input.

self::_login_form_revalidate_2fa( $nonce, $provider, $redirect_to, $is_post_request );
exit;
Expand Down Expand Up @@ -2553,7 +2559,7 @@ public static function get_current_user_session() {
public static function rememberme() {
$rememberme = false;

if ( ! empty( $_REQUEST['rememberme'] ) ) {
if ( ! empty( $_REQUEST['rememberme'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Only used after 2FA login nonce verified by caller.
$rememberme = true;
}

Expand Down
4 changes: 2 additions & 2 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@
},
"require-dev": {
"automattic/vipwpcs": "^3.0",
"dealerdirect/phpcodesniffer-composer-installer": "^1.0",
"dealerdirect/phpcodesniffer-composer-installer": "^1.2",
"phpcompatibility/phpcompatibility-wp": "3.0.0-alpha2",
"phpunit/phpunit": "^8.5|^9.6",
"phpunit/phpunit": "^8.5",
"spatie/phpunit-watcher": "^1.23",
"szepeviktor/phpstan-wordpress": "^1.3",
"wp-coding-standards/wpcs": "^3.3",
Expand Down
12 changes: 6 additions & 6 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions providers/class-two-factor-provider.php
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,11 @@ public static function get_code( $length = 8, $chars = '1234567890' ) {
* @return false|string Auth code on success, false if the field is not set or not expected length.
*/
public static function sanitize_code_from_request( $field, $length = 0 ) {
if ( empty( $_REQUEST[ $field ] ) ) {
if ( empty( $_REQUEST[ $field ] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Caller (core) verifies nonce before provider processing.
return false;
}

$code = wp_unslash( $_REQUEST[ $field ] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, handled by the core method already.
$code = wp_unslash( $_REQUEST[ $field ] ); // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- Caller (core) verifies nonce; value sanitized below.
$code = preg_replace( '/\s+/', '', $code );

// Maybe validate the length.
Expand Down
Loading