From 4c967584addfe2e8ffccd8913f83327dcfb3ee32 Mon Sep 17 00:00:00 2001 From: Jerry Yan <792602257@qq.com> Date: Mon, 8 Aug 2022 10:50:49 +0800 Subject: [PATCH] =?UTF-8?q?Webauthn=E6=8A=BD=E8=B1=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/UserWebAuthnController.php | 108 ++++++++---------- app/WebAuthn/WebAuthnService.php | 62 +++++++++- 2 files changed, 106 insertions(+), 64 deletions(-) diff --git a/app/Http/Controllers/UserWebAuthnController.php b/app/Http/Controllers/UserWebAuthnController.php index e17a996..d891051 100644 --- a/app/Http/Controllers/UserWebAuthnController.php +++ b/app/Http/Controllers/UserWebAuthnController.php @@ -32,8 +32,6 @@ use Webauthn\TokenBinding\IgnoreTokenBindingHandler; class UserWebAuthnController extends BaseController { - private $attestationStatementSupportManager = null; - public function webauthn_login(Request $request) { return view("user.webauthn.login"); @@ -68,12 +66,21 @@ class UserWebAuthnController extends BaseController public function register_validate(Request $request) { if (!$request->session()->has("webauthn_register_challenge")) { - /// + return new Response([ + "success" => false, + "code" => 403, + "message" => "重放攻击可能?无对应验证请求" + ], 403); } - $publicKeyCredential = $this->get_public_key_credential_loader()->loadArray($request->json()->all()); + $publicKeyCredential = WebAuthnService::getPublicKeyCredentialLoader()->loadArray($request->json()->all()); $authenticatorAttestationResponse = $publicKeyCredential->getResponse(); if (!$authenticatorAttestationResponse instanceof AuthenticatorAttestationResponse) { //e.g. process here with a redirection to the public key creation page. + return new Response([ + "success" => false, + "code" => 400, + "message" => "接口调用错误?无法验证请求" + ], 400); } $userEntity = new PublicKeyCredentialUserEntity( $request->user("web")->name, @@ -82,31 +89,49 @@ class UserWebAuthnController extends BaseController ); $challenge = $request->session()->remove("webauthn_register_challenge"); $publicKeyCredentialCreationOptions = WebAuthnService::createRequestOptions($userEntity, $challenge); - $publicKeyCredentialSource = $this->get_authn_attestation_response_validator()->check( - $authenticatorAttestationResponse, - $publicKeyCredentialCreationOptions, - ServerRequest::fromGlobals(), - ["localhost"] - ); - $this->get_pub_key_cred_source_repository()->saveCredentialSource($publicKeyCredentialSource); + try { + $publicKeyCredentialSource = WebAuthnService::getAuthenticatorAttestationResponseValidator()->check( + $authenticatorAttestationResponse, + $publicKeyCredentialCreationOptions, + ServerRequest::fromGlobals(), + ["localhost"] + ); + } catch (\Throwable $e) { + return new Response([ + "success" => false, + "code" => 500, + "message" => "服务器异常", + "exception" => $e->getMessage() + ], 500); + } + WebAuthnService::getPublicKeyCredentialSourceRepository()->saveCredentialSource($publicKeyCredentialSource); return $publicKeyCredentialSource; } public function login_validate(Request $request) { if (!$request->session()->has("webauthn_login_challenge")) { - /// + return new Response([ + "success" => false, + "code" => 403, + "message" => "重放攻击可能?无对应验证请求" + ], 403); } $publicKeyCredentialRequestOptions = new PublicKeyCredentialRequestOptions( $request->session()->remove("webauthn_login_challenge") ); - $publicKeyCredential = $this->get_public_key_credential_loader()->loadArray($request->json()->all()); + $publicKeyCredential = WebAuthnService::getPublicKeyCredentialLoader()->loadArray($request->json()->all()); $authenticatorAssertionResponse = $publicKeyCredential->getResponse(); if (!$authenticatorAssertionResponse instanceof AuthenticatorAssertionResponse) { //e.g. process here with a redirection to the public key login/MFA page. + return new Response([ + "success" => false, + "code" => 400, + "message" => "接口调用错误?无法验证请求" + ], 400); } try { - $publicKeyCredentialSource = $this->get_authn_assertion_response_validator()->check( + $publicKeyCredentialSource = WebAuthnService::getAuthenticatorAssertionResponseValidator()->check( $publicKeyCredential->getRawId(), $authenticatorAssertionResponse, $publicKeyCredentialRequestOptions, @@ -115,7 +140,12 @@ class UserWebAuthnController extends BaseController ["localhost"] ); } catch (\Throwable $e) { - return new Response($e->getMessage(), 500); + return new Response([ + "success" => false, + "code" => 500, + "message" => "服务器异常", + "exception" => $e->getMessage() + ], 500); } Auth::loginUsingId($publicKeyCredentialSource->getUserHandle()); return [ @@ -125,52 +155,4 @@ class UserWebAuthnController extends BaseController "userId" => $publicKeyCredentialSource->getUserHandle() ]; } - - private function get_authn_attestation_response_validator(): AuthenticatorAttestationResponseValidator - { - $authenticatorAttestationResponseValidator = new AuthenticatorAttestationResponseValidator( - $this->get_attestation_stmt_sup_mgr(), - WebAuthnService::getPublicKeyCredentialSourceRepository(), - new IgnoreTokenBindingHandler(), - new ExtensionOutputCheckerHandler() - ); - return $authenticatorAttestationResponseValidator; - } - - private function get_authn_assertion_response_validator(): AuthenticatorAssertionResponseValidator - { - $algorithmManager = new Manager(); - $algorithmManager->add(new ES256()); - $algorithmManager->add(new RS256()); - $authenticatorAssertionResponseValidator = new AuthenticatorAssertionResponseValidator( - WebAuthnService::getPublicKeyCredentialSourceRepository(), - new IgnoreTokenBindingHandler(), - new ExtensionOutputCheckerHandler(), - $algorithmManager - ); - return $authenticatorAssertionResponseValidator; - } - - private function get_public_key_credential_loader(): PublicKeyCredentialLoader - { - $publicKeyCredentialLoader = new PublicKeyCredentialLoader( - $this->get_attestation_loader() - ); - return $publicKeyCredentialLoader; - } - - private function get_attestation_loader(): AttestationObjectLoader - { - $attestationObjectLoader = new AttestationObjectLoader($this->get_attestation_stmt_sup_mgr()); - return $attestationObjectLoader; - } - - private function get_attestation_stmt_sup_mgr() - { - if ($this->attestationStatementSupportManager === null) { - $this->attestationStatementSupportManager = new AttestationStatementSupportManager(); - $this->attestationStatementSupportManager->add(new NoneAttestationStatementSupport()); - } - return $this->attestationStatementSupportManager; - } } diff --git a/app/WebAuthn/WebAuthnService.php b/app/WebAuthn/WebAuthnService.php index 2990ad7..33fe10f 100644 --- a/app/WebAuthn/WebAuthnService.php +++ b/app/WebAuthn/WebAuthnService.php @@ -3,20 +3,33 @@ namespace App\WebAuthn; use App\WebAuthn\Repository\PublicKeyCredentialSourceRepositoryImpl; +use Cose\Algorithm\Manager; +use Cose\Algorithm\Signature\ECDSA\ES256; +use Cose\Algorithm\Signature\RSA\RS256; use Cose\Algorithms; +use Webauthn\AttestationStatement\AttestationObjectLoader; +use Webauthn\AttestationStatement\AttestationStatementSupportManager; +use Webauthn\AttestationStatement\NoneAttestationStatementSupport; +use Webauthn\AuthenticationExtensions\ExtensionOutputCheckerHandler; +use Webauthn\AuthenticatorAssertionResponseValidator; +use Webauthn\AuthenticatorAttestationResponseValidator; use Webauthn\AuthenticatorSelectionCriteria; use Webauthn\PublicKeyCredentialCreationOptions; +use Webauthn\PublicKeyCredentialLoader; use Webauthn\PublicKeyCredentialParameters; use Webauthn\PublicKeyCredentialRpEntity; use Webauthn\PublicKeyCredentialSourceRepository; use Webauthn\PublicKeyCredentialUserEntity; +use Webauthn\TokenBinding\IgnoreTokenBindingHandler; class WebAuthnService { private static $rpName = "开心鄢的录播查询小站"; - private static $rpId = "localhost"; + private static $rpId = "comment.sc.jerryyan.top"; private static $timeout = 45000; private static $publicKeyCredentialSourceRepositoryInstance = null; + private static $authenticatorAssertionResponseValidator = null; + private static $attestationStatementSupportManager = null; public static function createRequestOptions(PublicKeyCredentialUserEntity $userEntity, string $challenge): PublicKeyCredentialCreationOptions { $publicKeyCredentialParametersList = [ @@ -40,6 +53,53 @@ class WebAuthnService return static::$publicKeyCredentialSourceRepositoryInstance; } + public static function getAuthenticatorAssertionResponseValidator(): AuthenticatorAssertionResponseValidator + { + if (static::$authenticatorAssertionResponseValidator === null) { + $algorithmManager = new Manager(); + $algorithmManager->add(new ES256()); + $algorithmManager->add(new RS256()); + static::$authenticatorAssertionResponseValidator = new AuthenticatorAssertionResponseValidator( + static::getPublicKeyCredentialSourceRepository(), + new IgnoreTokenBindingHandler(), + new ExtensionOutputCheckerHandler(), + $algorithmManager + ); + } + return static::$authenticatorAssertionResponseValidator; + } + + public static function getAuthenticatorAttestationResponseValidator(): AuthenticatorAttestationResponseValidator + { + return new AuthenticatorAttestationResponseValidator( + static::getAttestationStatementSupportManager(), + static::getPublicKeyCredentialSourceRepository(), + new IgnoreTokenBindingHandler(), + new ExtensionOutputCheckerHandler() + ); + } + + public static function getPublicKeyCredentialLoader(): PublicKeyCredentialLoader + { + return new PublicKeyCredentialLoader( + static::getAttestationObjectLoader() + ); + } + + public static function getAttestationObjectLoader(): AttestationObjectLoader + { + return new AttestationObjectLoader(static::getAttestationStatementSupportManager()); + } + + private static function getAttestationStatementSupportManager(): AttestationStatementSupportManager + { + if (static::$attestationStatementSupportManager === null) { + static::$attestationStatementSupportManager = new AttestationStatementSupportManager(); + static::$attestationStatementSupportManager->add(new NoneAttestationStatementSupport()); + } + return static::$attestationStatementSupportManager; + } + private static function getRpEntity(): PublicKeyCredentialRpEntity { return new PublicKeyCredentialRpEntity(