PluginProbe
Defender Security – Malware Scanner, Login Security & Firewall / 6.3.0
Defender Security – Malware Scanner, Login Security & Firewall v6.3.0
6.3.0 6.2.3 6.2.4 6.2.0 6.2.1 6.2.2 6.1.0 5.3.1 5.4.0 5.4.1 5.5.0 5.5.1 5.6.0 5.6.1 5.6.2 5.7.0 5.7.1 5.7.2 5.8.0 5.8.1 5.9.0 6.0.0 6.0.1 3.0.1 3.1.0 All 141 releases
← All changes | src/controller/class-webauthn.php +66 -26 5.9.0 → 6.3.0 View file →
@@ -7,19 +7,20 @@
7 7
8 8 namespace WP_Defender\Controller;
9 9
10 10 use Error;
11 +use WP_User;
11 12 use Exception;
12 -use Webauthn\Server;
13 +use WP_DEFENDER_VENDOR\Webauthn\Server;
13 14 use WP_Defender\Controller;
14 15 use WP_Defender\Behavior\WPMUDEV;
15 -use Nyholm\Psr7\Factory\Psr17Factory;
16 -use Webauthn\PublicKeyCredentialRpEntity;
17 -use Nyholm\Psr7Server\ServerRequestCreator;
18 -use Webauthn\PublicKeyCredentialUserEntity;
19 -use Webauthn\AuthenticatorSelectionCriteria;
20 -use Webauthn\PublicKeyCredentialRequestOptions;
21 -use Webauthn\PublicKeyCredentialCreationOptions;
16 +use WP_DEFENDER_VENDOR\Nyholm\Psr7\Factory\Psr17Factory;
17 +use WP_DEFENDER_VENDOR\Webauthn\PublicKeyCredentialRpEntity;
18 +use WP_DEFENDER_VENDOR\Nyholm\Psr7Server\ServerRequestCreator;
19 +use WP_DEFENDER_VENDOR\Webauthn\PublicKeyCredentialUserEntity;
20 +use WP_DEFENDER_VENDOR\Webauthn\AuthenticatorSelectionCriteria;
21 +use WP_DEFENDER_VENDOR\Webauthn\PublicKeyCredentialRequestOptions;
22 +use WP_DEFENDER_VENDOR\Webauthn\PublicKeyCredentialCreationOptions;
22 23 use WP_Defender\Traits\Webauthn as Webauthn_Trait;
23 24 use WP_Defender\Component\Two_Fa as Two_Fa_Component;
24 25 use WP_Defender\Component\Webauthn as Webauthn_Component;
25 26 use WP_Defender\Controller\Two_Factor as Two_Fa_Controller;
@@ -65,12 +66,10 @@
65 66 $this,
66 67 'get_credential_request_option',
67 68 )
68 69 );
69 - // @phpstan-ignore-next-line
70 - add_action( 'wp_ajax_defender_webauthn_verify_response', array( $this, 'verify_response' ) );
71 - // @phpstan-ignore-next-line
72 - add_action( 'wp_ajax_nopriv_defender_webauthn_verify_response', array( $this, 'verify_response' ) );
70 + add_action( 'wp_ajax_defender_webauthn_verify_response', array( $this, 'handle_verify_response_request' ) );
71 + add_action( 'wp_ajax_nopriv_defender_webauthn_verify_response', array( $this, 'handle_verify_response_request' ) );
73 72 // Handling requests in the frontend.
74 73 if ( wd_di()->get( Two_Fa_Controller::class )->woo_integration_enabled() ) {
75 74 add_action( 'wp_ajax_nopriv_defender_webauthn_create_challenge', array( $this, 'create_challenge' ) );
76 75 add_action( 'wp_ajax_nopriv_defender_webauthn_verify_challenge', array( $this, 'verify_challenge' ) );
@@ -248,8 +247,9 @@
248 247 $posted_data = defender_get_data_from_request( null, 'p' );
249 248 if ( '' === $posted_data['data'] || '' === $posted_data['client_id'] ) {
250 249 throw new Exception( esc_html__( 'Missing field(s).', 'defender-security' ) );
251 250 }
251 + $client_id = sanitize_text_field( $posted_data['client_id'] );
252 252
253 253 $psr17_factory = new Psr17Factory();
254 254 $creator = new ServerRequestCreator(
255 255 $psr17_factory,
@@ -271,9 +271,8 @@
271 271 null
272 272 );
273 273
274 274 $response_data = base64_decode( sanitize_text_field( $posted_data['data'] ), true ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
275 - $client_id = sanitize_text_field( $posted_data['client_id'] );
276 275 $pub_key_cco = $this->get_trans_val( 'pub_key_cco', $client_id );
277 276 $pub_key_cco = PublicKeyCredentialCreationOptions::createFromString( base64_decode( $pub_key_cco, true ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
278 277
279 278 $public_key_credential_source = $server->loadAndCheckAttestationResponse(
@@ -306,12 +305,16 @@
306 305 );
307 306 }
308 307 wp_send_json_success( $response );
309 308 } catch ( Error $error ) {
310 - $this->delete_trans( 'pub_key_cco', $client_id );
309 + if ( null !== $client_id ) {
310 + $this->delete_trans( 'pub_key_cco', $client_id );
311 + }
311 312 wp_send_json_error( $error->getMessage() );
312 313 } catch ( Exception $exception ) {
313 - $this->delete_trans( 'pub_key_cco', $client_id );
314 + if ( null !== $client_id ) {
315 + $this->delete_trans( 'pub_key_cco', $client_id );
316 + }
314 317 wp_send_json_error( $exception->getMessage() );
315 318 }
316 319 }
317 320
@@ -487,16 +490,26 @@
487 490 }
488 491 }
489 492
490 493 /**
494 + * Handle verify response request.
495 + *
496 + * @return void
497 + */
498 + public function handle_verify_response_request(): void {
499 + $this->verify_response();
500 + }
501 +
502 + /**
491 503 * Verify response.
492 504 *
493 - * @param bool $will_return Either return array or echo json.
505 + * @param bool $will_return Either return array or echo json.
506 + * @param WP_User|null $expected_user The expected user.
494 507 *
495 508 * @return array|void
496 509 * @throws Exception If something goes wrong.
497 510 */
498 - public function verify_response( bool $will_return = false ) {
511 + public function verify_response( bool $will_return = false, ?WP_User $expected_user = null ) {
499 512 $client_id = null;
500 513 try {
501 514 if ( ! $this->verify_nonce( 'wpdef_webauthn', 'post' ) ) {
502 515 throw new Exception( esc_html__( 'Bad nonce.', 'defender-security' ) );
@@ -510,23 +523,44 @@
510 523 || '' === $posted_data['client_id']
511 524 ) {
512 525 throw new Exception( esc_html__( 'Missing field(s).', 'defender-security' ) );
513 526 }
527 + $client_id = sanitize_text_field( $posted_data['client_id'] );
528 + // Check expected user.
529 + if ( $expected_user instanceof WP_User ) {
530 + $user = $expected_user;
531 + } else {
532 + $user = wp_get_current_user();
533 + }
514 534
515 - $user_entity = false;
535 + // Establish a trusted scope BEFORE any access to the repository —
536 + // both before our own pre-check and before internal library calls
537 + // inside loadAndCheckAssertionResponse().
538 + $this->service->set_expected_user_id( $user->ID );
516 539 $response_data = base64_decode( $posted_data['data'], true ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
517 - $username = sanitize_text_field( $posted_data['username'] );
518 - $client_id = sanitize_text_field( $posted_data['client_id'] );
519 - $pub_key_cro = PublicKeyCredentialRequestOptions::createFromString( base64_decode( $this->get_trans_val( 'pub_key_cro', $client_id ), true ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
520 - $user = get_user_by( 'login', $username );
540 + if ( false === $response_data ) {
541 + throw new Exception( esc_html__( 'Invalid response data.', 'defender-security' ) );
542 + }
521 543
522 - if ( is_object( $user ) ) {
523 - $user_entity = $this->get_user_entity( $user->ID );
544 + $decoded_response = json_decode( $response_data, true );
545 + if ( ! is_array( $decoded_response ) || ! isset( $decoded_response['rawId'] ) || ! is_string( $decoded_response['rawId'] ) || '' === $decoded_response['rawId'] ) {
546 + throw new Exception( esc_html__( 'Invalid assertion payload.', 'defender-security' ) );
524 547 }
525 548
549 + $credential_id = $this->base64url_decode( $decoded_response['rawId'] );
550 + if ( false === $credential_id || '' === $credential_id ) {
551 + throw new Exception( esc_html__( 'Invalid credential id.', 'defender-security' ) );
552 + }
553 + $credential = $this->service->findOneByCredentialId( $credential_id );
554 + if ( ! $credential ) {
555 + throw new Exception( esc_html__( 'Invalid user.', 'defender-security' ) );
556 + }
557 +
558 + $user_entity = $this->get_user_entity( $user->ID );
526 559 if ( false === $user_entity ) {
527 560 throw new Exception( esc_html__( 'User does not exist.', 'defender-security' ) );
528 561 }
562 + $pub_key_cro = PublicKeyCredentialRequestOptions::createFromString( base64_decode( $this->get_trans_val( 'pub_key_cro', $client_id ), true ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
529 563
530 564 $psr17_factory = new Psr17Factory();
531 565 $creator = new ServerRequestCreator(
532 566 $psr17_factory,
@@ -555,14 +589,18 @@
555 589 $server_request
556 590 );
557 591
558 592 $this->delete_trans( 'pub_key_cro', $client_id );
593 + // Reset the area just in case.
594 + $this->service->set_expected_user_id( null );
559 595
560 596 $data = esc_html__( 'Authenticator verified successfully.', 'defender-security' );
561 597
562 598 return defender_maybe_echo_json( $data, true, $will_return );
563 599 } catch ( Error $error ) {
564 - $this->delete_trans( 'pub_key_cro', $client_id );
600 + if ( null !== $client_id ) {
601 + $this->delete_trans( 'pub_key_cro', $client_id );
602 + }
565 603
566 604 return defender_maybe_echo_json( $error->getMessage(), false, $will_return );
567 605 } catch ( Exception $exception ) {
568 606 $data = array( 'message' => $exception->getMessage() );
@@ -576,9 +614,11 @@
576 614
577 615 $this->service->addUserHandleMatchFailed( $user, $decoded_response );
578 616 }
579 617
580 - $this->delete_trans( 'pub_key_cro', $client_id );
618 + if ( null !== $client_id ) {
619 + $this->delete_trans( 'pub_key_cro', $client_id );
620 + }
581 621
582 622 return defender_maybe_echo_json( $data, false, $will_return );
583 623 }
584 624 }