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 +70 -28 5.8.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,10 +66,10 @@
65 66 $this,
66 67 'get_credential_request_option',
67 68 )
68 69 );
69 - add_action( 'wp_ajax_defender_webauthn_verify_response', array( $this, 'verify_response' ) );
70 - 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' ) );
71 72 // Handling requests in the frontend.
72 73 if ( wd_di()->get( Two_Fa_Controller::class )->woo_integration_enabled() ) {
73 74 add_action( 'wp_ajax_nopriv_defender_webauthn_create_challenge', array( $this, 'create_challenge' ) );
74 75 add_action( 'wp_ajax_nopriv_defender_webauthn_verify_challenge', array( $this, 'verify_challenge' ) );
@@ -246,8 +247,9 @@
246 247 $posted_data = defender_get_data_from_request( null, 'p' );
247 248 if ( '' === $posted_data['data'] || '' === $posted_data['client_id'] ) {
248 249 throw new Exception( esc_html__( 'Missing field(s).', 'defender-security' ) );
249 250 }
251 + $client_id = sanitize_text_field( $posted_data['client_id'] );
250 252
251 253 $psr17_factory = new Psr17Factory();
252 254 $creator = new ServerRequestCreator(
253 255 $psr17_factory,
@@ -268,12 +270,11 @@
268 270 $this->service,
269 271 null
270 272 );
271 273
272 - $response_data = base64_decode( sanitize_text_field( $posted_data['data'] ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
273 - $client_id = sanitize_text_field( $posted_data['client_id'] );
274 + $response_data = base64_decode( sanitize_text_field( $posted_data['data'] ), true ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
274 275 $pub_key_cco = $this->get_trans_val( 'pub_key_cco', $client_id );
275 - $pub_key_cco = PublicKeyCredentialCreationOptions::createFromString( base64_decode( $pub_key_cco ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
276 + $pub_key_cco = PublicKeyCredentialCreationOptions::createFromString( base64_decode( $pub_key_cco, true ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
276 277
277 278 $public_key_credential_source = $server->loadAndCheckAttestationResponse(
278 279 $response_data,
279 280 $pub_key_cco,
@@ -304,12 +305,16 @@
304 305 );
305 306 }
306 307 wp_send_json_success( $response );
307 308 } catch ( Error $error ) {
308 - $this->delete_trans( 'pub_key_cco', $client_id );
309 + if ( null !== $client_id ) {
310 + $this->delete_trans( 'pub_key_cco', $client_id );
311 + }
309 312 wp_send_json_error( $error->getMessage() );
310 313 } catch ( Exception $exception ) {
311 - $this->delete_trans( 'pub_key_cco', $client_id );
314 + if ( null !== $client_id ) {
315 + $this->delete_trans( 'pub_key_cco', $client_id );
316 + }
312 317 wp_send_json_error( $exception->getMessage() );
313 318 }
314 319 }
315 320
@@ -485,16 +490,26 @@
485 490 }
486 491 }
487 492
488 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 + /**
489 503 * Verify response.
490 504 *
491 - * @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.
492 507 *
493 - * @return array
508 + * @return array|void
494 509 * @throws Exception If something goes wrong.
495 510 */
496 - public function verify_response( bool $will_return = false ) {
511 + public function verify_response( bool $will_return = false, ?WP_User $expected_user = null ) {
497 512 $client_id = null;
498 513 try {
499 514 if ( ! $this->verify_nonce( 'wpdef_webauthn', 'post' ) ) {
500 515 throw new Exception( esc_html__( 'Bad nonce.', 'defender-security' ) );
@@ -508,23 +523,44 @@
508 523 || '' === $posted_data['client_id']
509 524 ) {
510 525 throw new Exception( esc_html__( 'Missing field(s).', 'defender-security' ) );
511 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 + }
512 534
513 - $user_entity = false;
514 - $response_data = base64_decode( $posted_data['data'] ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
515 - $username = sanitize_text_field( $posted_data['username'] );
516 - $client_id = sanitize_text_field( $posted_data['client_id'] );
517 - $pub_key_cro = PublicKeyCredentialRequestOptions::createFromString( base64_decode( $this->get_trans_val( 'pub_key_cro', $client_id ) ) ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
518 - $user = get_user_by( 'login', $username );
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 );
539 + $response_data = base64_decode( $posted_data['data'], true ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_decode
540 + if ( false === $response_data ) {
541 + throw new Exception( esc_html__( 'Invalid response data.', 'defender-security' ) );
542 + }
519 543
520 - if ( is_object( $user ) ) {
521 - $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' ) );
522 547 }
523 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 );
524 559 if ( false === $user_entity ) {
525 560 throw new Exception( esc_html__( 'User does not exist.', 'defender-security' ) );
526 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
527 563
528 564 $psr17_factory = new Psr17Factory();
529 565 $creator = new ServerRequestCreator(
530 566 $psr17_factory,
@@ -553,14 +589,18 @@
553 589 $server_request
554 590 );
555 591
556 592 $this->delete_trans( 'pub_key_cro', $client_id );
593 + // Reset the area just in case.
594 + $this->service->set_expected_user_id( null );
557 595
558 596 $data = esc_html__( 'Authenticator verified successfully.', 'defender-security' );
559 597
560 598 return defender_maybe_echo_json( $data, true, $will_return );
561 599 } catch ( Error $error ) {
562 - $this->delete_trans( 'pub_key_cro', $client_id );
600 + if ( null !== $client_id ) {
601 + $this->delete_trans( 'pub_key_cro', $client_id );
602 + }
563 603
564 604 return defender_maybe_echo_json( $error->getMessage(), false, $will_return );
565 605 } catch ( Exception $exception ) {
566 606 $data = array( 'message' => $exception->getMessage() );
@@ -574,9 +614,11 @@
574 614
575 615 $this->service->addUserHandleMatchFailed( $user, $decoded_response );
576 616 }
577 617
578 - $this->delete_trans( 'pub_key_cro', $client_id );
618 + if ( null !== $client_id ) {
619 + $this->delete_trans( 'pub_key_cro', $client_id );
620 + }
579 621
580 622 return defender_maybe_echo_json( $data, false, $will_return );
581 623 }
582 624 }