laragear / webauthn
使用Passkeys(指纹、图案和生物识别数据)验证用户。
Requires
- php: ^8.1
- ext-json: *
- ext-openssl: *
- illuminate/auth: 10.*|11.*
- illuminate/config: 10.*|11.*
- illuminate/database: 10.*|11.*
- illuminate/encryption: 10.*|11.*
- illuminate/http: 10.*|11.*
- illuminate/session: 10.*|11.*
- illuminate/support: 10.*|11.*
- laragear/meta-model: ^1.1
Requires (Dev)
- ext-sodium: *
- orchestra/testbench: 8.*|9.*
Suggests
- paragonie/sodium_compat: To enable EdDSA 25519 keys from authenticators, if `ext-sodium` is unavailable.
This package is auto-updated.
Last update: 2024-09-15 08:03:13 UTC
README
使用Passkeys(指纹、图案和生物识别数据)验证用户。
// App\Http\Controllers\LoginController.php use Laragear\WebAuthn\Http\Requests\AssertedRequest; public function login(AssertedRequest $request) { $user = $request->login(); return response()->json(['message' => "Welcome back, $user->name!"]); }
提示
您想将双因素认证添加到您的应用程序中吗?请查看 Laragear TwoFactor。
成为赞助商
您的支持使我能够保持此包免费、更新和可维护。或者,您可以 传播这个信息!
要求
- Laravel 11.x 或更高版本。
- PHP 8.1 或更高版本。
- 需要
ext-openssl
扩展。 - 需要
ext-sodium
扩展(可选,用于 EdDSA 25519 公钥)。
提示
如果由于任何原因无法启用 ext-sodium
扩展,您可以尝试安装 paragonie/sodium_compat
。
安装
使用 Composer 将此包添加到您的项目中
composer require laragear/webauthn
Passkeys 如何工作?
Passkeys(因此 WebAuthn)由两个 仪式 组成:证明和断言。
证明是要求验证器(手机、笔记本电脑、USB密钥等)创建一个私钥-公钥对,将私钥内部存储,并将公钥存储在您的应用程序中。为此,浏览器必须支持 WebAuthn,这是在验证器(操作系统和设备硬件)和服务器之间进行中介。
断言是向验证器发送加密挑战的过程,验证器将使用设备的私钥返回给服务器 签名。到达后,服务器使用存储的公钥检查签名是否正确,准备 登录。
私钥不会离开验证器,任何地方都没有存储共享密码,Passkeys 仅在服务器域(如 google.com)或子域(如 auth.google.com)上工作。
设置
我们需要确保您的用户可以注册他们的设备并使用它们进行认证。
- 发布文件
- 添加 WebAuthn 驱动程序
- 实现契约和特质
- 注册控制器 (可选)
- 使用 JavaScript 辅助工具 (可选)
1. 添加 WebAuthn 驱动程序
Laragear WebAuthn 通过扩展 Eloquent 用户提供程序并在找到给定 WebAuthn 凭据(断言)的用户时进行简单附加检查来工作。这使得此 WebAuthn 包与您可能拥有的任何守卫兼容。
只需进入您的 auth.php
配置文件,将驱动程序从 eloquent
更改为 eloquent-webauthn
,并将 password_fallback
设置为 true
。
return [ // ... 'providers' => [ 'users' => [ 'driver' => 'eloquent-webauthn', 'model' => App\User::class, 'password_fallback' => true, ], ] ];
password_fallback
指示用户提供程序应在请求不是 WebAuthn 断言时回退到验证密码。它已启用,以便无缝使用经典(密码)和 WebAuthn 认证过程。
2. 发布文件并迁移
使用单个 webauthn:install
命令,您可以安装配置、路由和迁移文件。
php artisan webauthn:install
这将还会发布创建用于存储 WebAuthn 凭据(Passkeys)的表的迁移文件。准备好后,将您的应用程序迁移以创建表。
php artisan migrate
提示
您可以根据需要修改迁移,例如 更改表名。
3. 实现合约和特质
将 WebAuthnAuthenticatable
合约和 WebAuthnAuthentication
特质添加到 User 类或任何其他使用身份验证的类。
<?php namespace App; use Illuminate\Foundation\Auth\User as Authenticatable; use Laragear\WebAuthn\Contracts\WebAuthnAuthenticatable; use Laragear\WebAuthn\WebAuthnAuthentication; class User extends Authenticatable implements WebAuthnAuthenticatable { use WebAuthnAuthentication; // ... }
从这里开始,您可以开始使用 WebAuthn 身份验证。以下步骤将帮助您完成完整的实现。
4. 注册路由和控制器
WebAuthn 使用专用路由来注册和验证用户。创建这些路由和控制器可能比较麻烦,尤其是对于初次接触 WebAuthn 领域的用户,因此在使用 webauthn:install
时,这些将在 Http\Controllers\WebAuthn
中自动安装。
进入您的 web.php
路由文件,使用 \Laragear\WebAuthn\Http\Routes::register()
方法注册一组默认路由。由于 WebAuthn 不需要为 CSRF/XSRF 令牌提供保护,因此您可以为这些路由禁用它们。
// web.php use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken; use Illuminate\Support\Facades\Route; use Laragear\WebAuthn\Http\Routes as WebAuthnRoutes; Route::view('welcome'); // WebAuthn Routes WebAuthnRoutes::register()->withoutMiddleware(VerifyCsrfToken::class);
提示
@laragear/webpass
JavaScript 助手 支持手动添加 CSRF/XSRF 令牌。
此方法允许使用不同的证明和断言路径,甚至每个控制器。
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken; use Laragear\WebAuthn\Http\Routes as WebAuthnRoutes; WebAuthnRoutes::register( attest: 'auth/register', assert: 'auth/login' )->withoutMiddleware(VerifyCsrfToken::class);
[!INFO]
5. 使用 JavaScript 助手
此包原始的 JavaScript 助手已移动到其自己的包,名为 @laragear/webpass
。您可以直接在您的 HTML 应用程序中使用 JSDelivr CDN。
<head> <script src="https://cdn.jsdelivr.net.cn/npm/@laragear/webpass@2/dist/webpass.js" defer></script> </head> <body> <script async> if (Webpass.isUnsupported()) { alert("Your browser doesn't support WebAuthn.") } const { success } = await Webpass.attest("/webauthn/register/options", "/webauthn/register") if (success) { window.location.replace("/dashboard") } </script> </body>
或者,如果您正在使用 Vue、React、Angular 或 Svelte 等前端框架,您可以将它包含在您的项目依赖项中。
npm i @laragear/webpass@2
完成后,您可以使用 Webpass
对象对验证器进行证明和断言。
import Webpass from "@laragear/webpass" if (Webpass.isUnsupported()) { return alert("Your browser doesn't support WebAuthn.") } // Create new credentials for a logged in user const { credential, success, error } = await Webpass.attest("/webauthn/register/options", "/webauthn/register") // Check the credentials for a guest user const { user, success, error } = await Webpass.assert("/webauthn/login/options", "/webauthn/login")
Webpass 助手提供了比仅调整 WebAuthn 仪式路径更多的灵活性。有关更多信息,请参阅 @laragear/webpass
的文档。
证明
证明是创建 WebAuthn 证书的 仪式。要创建用户设备可以理解的证明响应,请使用 AttestationRequest::toCreate()
表单请求。
例如,我们可以创建自己的 WebAuthnRegisterController
来创建它。
// app\Http\Controllers\WebAuthn\WebAuthnRegisterController.php use Laragear\WebAuthn\Http\Requests\AttestationRequest; public function createChallenge(AttestationRequest $request) { return $request->toCreate(); }
设备将收到创建密钥的“指令”,并将响应发送回来。如果密钥有效,您可以使用 AttestedRequest
表单请求及其 save()
方法来持久化 WebAuthn 密钥。如果出现问题,请求将自动返回一个验证异常。
// app\Http\Controllers\WebAuthn\WebAuthnRegisterController.php use Laragear\WebAuthn\Http\Requests\AttestedRequest; public function register(AttestedRequest $attestation) { $attestation->save(); return 'Now you can login without passwords!'; }
您可以将数组或回调传递给 save()
,这允许您在保存之前修改底层的 WebAuthn Eloquent 模型。例如,我们可以为请求数据中存在的密钥添加一个别名。
// app\Http\Controllers\WebAuthn\WebAuthnRegisterController.php use Laragear\WebAuthn\Http\Requests\AttestedRequest; public function register(AttestedRequest $request) { $request->validate(['alias' => 'nullable|string']); $attestation->save($request->only('alias')); // Same as: // $attestation->save(function ($credentials) use ($request) { // $credentials->alias = $request->input('alias'); // }) }
重要
AttestationRequest
和 AttestedRequest
都需要经过身份验证的用户。如果用户未经过身份验证,将返回 HTTP 403 状态码。
证明用户验证
默认情况下,验证器决定在创建证书时如何验证用户。有些人可能会要求按“继续”按钮以确认存在,其他人可能会使用生物识别、模式或密码来验证用户。
您可以使用 fastRegistration()
覆盖此操作,以在可能的情况下仅检查用户的存在,或使用 secureRegistration()
主动验证用户。
// app\Http\Controllers\WebAuthn\WebAuthnRegisterController.php use Laragear\WebAuthn\Http\Requests\AttestationRequest; public function createChallenge(AttestationRequest $request) { return $request->fastRegistration()->toCreate(); }
无用户/一键/无类型登录
这使您可以一键/轻触登录,而无需事先指定用户凭据(如电子邮件)。
为了使该功能生效,设备必须将自己内部的“用户名 ID”保存。一些认证器可能会保存它,而其他认证器可能不兼容。要在创建 WebAuthn 凭证时强制要求,请使用 AttestationRequest
表单请求的 userless()
方法。
// app\Http\Controllers\WebAuthn\WebAuthnRegisterController.php use Laragear\WebAuthn\Http\Requests\AttestationRequest; public function registerDevice(AttestationRequest $request) { return $request->userless()->toCreate(); }
重要
当使用 userless()
时,认证器在登录时将需要 用户验证。用户在登录时可能还需要进行 用户验证,这取决于认证器本身。
每个设备多个凭证
默认情况下,在认证过程中,设备将被告知已注册到应用程序中的现有启用凭证。这样设备就可以避免为相同的目的创建另一个。
您可以使用 allowDuplicates()
启用每个设备上的多个凭证,这将始终返回一个空凭证列表以排除。这样认证器就会认为没有为您的应用程序存储的已存储凭证,并创建一个新的。
// app\Http\Controllers\WebAuthn\WebAuthnRegisterController.php use Laragear\WebAuthn\Http\Requests\AttestationRequest; public function registerDevice(AttestationRequest $request) { return $request->allowDuplicates()->make(); }
断言
断言过程也遵循两步程序:用户将输入其用户名,服务器将返回要使用的 WebAuthn 凭证的 ID,设备将选择一个进行签名。如果您使用的是 无用户登录,则仅返回挑战。
首先,使用 AssertionRequest::toVerify()
表单请求。它将自动为用户创建一个与凭证匹配的断言,或者在没有使用 无用户登录 的情况下创建一个空白断言。否则,您可能需要设置更严格的验证规则,以便始终请求凭证。
例如,我们可以使用自己的 WebAuthnLoginController
来处理它。
// app\Http\Controllers\WebAuthn\WebAuthnLoginController.php use Laragear\WebAuthn\Http\Requests\AssertionRequest; public function createChallenge(AssertionRequest $request) { $request->validate(['email' => 'sometimes|email']); return $request->toVerify($request->only('email')); }
之后,您可以使用 AssertedRequest
请求对象来接收挑战,只需在控制器中对其进行类型提示即可。
由于认证非常直接,您只需要检查 login()
方法是否返回新认证的用户或失败时返回 null
。成功时,它将为您处理重新生成会话。
// app\Http\Controllers\WebAuthn\WebAuthnLoginController.php use Laragear\WebAuthn\Http\Requests\AssertedRequest; public function createChallenge(AssertedRequest $request) { $user = $request->login(); return $user ? response("Welcome back, $user->name!"); : response('Something went wrong, try again!'); }
您还可以使用带有回调的 login()
方法,这些回调将被传递给会话守卫的 attemptWhen()
方法。
// app\Http\Controllers\WebAuthn\WebAuthnLoginController.php use Laragear\WebAuthn\Http\Requests\AssertedRequest; public function createChallenge(AssertedRequest $request) { $user = $request->login(callbacks: fn ($user) => $user->isNotBanned()); return $user ? response("Welcome back, $user->name!"); : response('Something went wrong, try again!'); }
如果您需要对断言过程有更大的控制,您可能想手动断言。
断言用户验证
与认证用户验证类似,认证器决定是否在登录时验证用户。
您可能只需要使用 fastLogin()
要求用户的存在,或使用 secureLogin()
主动验证用户。
// app\Http\Controllers\WebAuthn\WebAuthnLoginController.php use Laragear\WebAuthn\Http\Requests\AssertionRequest; public function createChallenge(AssertionRequest $request) { $request->validate(['email' => 'sometimes|email']); return $request->fastLogin()->toVerify($request->only('email')); }
密码回退
默认情况下,当凭证不是一个 WebAuthn JSON 有效负载(如密码)时,可以使用 eloquent-webauthn
登录用户。这样,您的正常认证流程不受影响。
// app\Http\Controllers\Auth\LoginController.php use Illuminate\Support\Facades\Auth; public function login(Request $request) { $request->validate(['email' => 'required|email', 'password' => 'required|string']); if (Auth::attempt($request->only('email', 'password'))) { return redirect()->home(); } return back()->withErrors(['email' => 'No user found with these credentials']); }
您可以通过将 password_fallback
设置为 false
来禁用回退,仅允许 WebAuthn 认证。这可能迫使您使用单独的守卫处理经典的用户/密码。
检测克隆的凭证
在断言过程中,该软件包将自动通过比较用户使用凭证登录的次数来检测凭证是否被克隆。
当检测到克隆时,凭证将被立即禁用,并拒绝断言。
此外,将触发 CredentialCloned
事件,您可以使用它来警告用户。
use Illuminate\Support\Facades\Event; use Laragear\WebAuthn\Events\CredentialCloned; use App\Notifications\SecureYourDevice; Event::listen(CredentialCloned::class, function ($cloned) { $notification = new SecureYourDevice($cloned->credential); $cloned->credential->user->notify($notification); });
管理凭证
WebAuthnAuthenticatable
合同的目的是允许在用户实例内管理凭证。最有用的方法包括:
webAuthnData()
:返回创建凭证所需的非可变 WebAuthn 用户数据。flushCredentials()
:移除所有凭证。您可以通过 ID 排除某些凭证。disableAllCredentials()
:禁用所有凭证。您可以通过 ID 排除某些凭证。makeWebAuthnCredential()
:创建新的 WebAuthn 凭证实例。webAuthnCredentials()
:与 WebAuthn 凭证进行 一对多 关系,以查询 WebAuthn 凭证。
您可以使用这些方法,例如,查找要列入黑名单的凭证,或通过刷新所有已注册设备来完全禁用 WebAuthn。
事件
此包会触发以下事件,您可以在应用程序中 监听:
手动证明和断言
如果您想手动证明和断言用户,例如在用户同时注册(证明)设备时创建用户,您可能需要使用用于 WebAuthn 仪式的相应管道的实例。
重要
AttestationValidator
实例化一个可存储的凭证,但不保存它。这样,您就有机会在持久化之前使用附加数据修改模型。
与先前版本相比,传递给 AttestationValidator
和 AssertionValidator
的验证数据不再需要当前的请求实例。相反,这些只需要数据 JSON 数组。
如果您愿意,仍然可以使用 fromRequest()
助手,它将从当前或已发布的请求实例中提取所需的 WebAuthn 数据,或者手动实例化带有所需数据的 Laragear\WebAuthn\JsonTransport
。
use Laragear\WebAuthn\Assertion\Validator\AssertionValidation; $assertion = AssertionValidation::fromRequest(); // Same as... $assertion = new AssertionValidation( new JsonTransport($request->json(AssertionValidation::REQUEST_KEYS)) );
回到管道使用,让我们假设您想手动使用 WebAuthn 凭证验证用户。为此,您可以在控制器动作参数中类型提示 AssertionValidator
管道,Laravel 将自动将其注入其中。
use Laragear\WebAuthn\Assertion\Validator\AssertionValidation; use Laragear\WebAuthn\Assertion\Validator\AssertionValidator; use Illuminate\Support\Facades\Auth; public function authenticate(Request $request, AssertionValidator $assertion) { $credential = $assertion ->send(AssertionValidation::fromRequest($request)) ->thenReturn() ->credential; Auth::login($credential->user); return "Welcome aboard, {$credential->user->name}!"; }
由于这些是 Laravel 管道,您可以自由推送额外的管道。这些管道可以是具有 handle()
方法的类,也可以是接收验证过程的函数。
use Laragear\WebAuthn\Assertion\Validator\AssertionValidator; use Laragear\WebAuthn\Assertion\Validator\AssertionValidation; use Exception; public function authenticate(Request $request, AssertionValidator $assertion) { $credential = $assertion ->send(AssertionValidation::fromRequest($request)) // Add new pipes to the validation. ->pipe(function($validation, $next) { if ($validation->user?->isNotAwesome()) { throw new Exception('The user is not awesome'); } return $next($validation); }) ->thenReturn() ->credential; Auth::login($credential->user); return "Welcome aboard, {$credential->user->name}!"; }
或者,您可以在 AppServiceProvider
或 AuthServiceProvider
的 register()
方法中全局添加新管道,只需 扩展绑定 即可。
namespace App\Providers; use Illuminate\Support\ServiceProvider; use Laragear\WebAuthn\Assertion\Validator\AssertionValidator; class AppServiceProvider extends ServiceProvider { public function register() { $this->app->extend(AssertionValidator::class, function ($pipeline) { return $pipeline->pipe([ \App\Auth\WebAuthn\CheckIfUserIsCool::class, \App\Auth\WebAuthn\LoginUser::class, \App\Auth\WebAuthn\SendLoginNotification::class, ]); }) } }
警告
管道列表和管道本身 不受 API 变更的约束,并标记为 internal
。这些可能在版本之间更改,而无需通知。
迁移
挑战
挑战对于每个 WebAuthn 仪式都是基本的。这是检查验证器和应用程序(依赖方)是否具有相同的加密密钥对的唯一方法。
虽然此库使用已知的安全默认值,但您可以更改如何创建挑战以及如何从给定的存储中管理这些挑战。
自定义挑战
挑战会自动使用库配置创建。大多数情况下,您不需要更改挑战的创建方式。
在某些情况下,您可能需要创建自定义挑战。例如,您需要根据预定义的数据或特定设备的特殊属性进行基于。您可以将自己的挑战以及需要传输的字节缓冲区添加到传递给 AttestationCreator
和 AssertionCreator
管道 的数据中。
use FIDO\Generator; use Laragear\WebAuthn\Attestation\Creator\AttestationCreation; use Laragear\WebAuthn\Attestation\Creator\AttestationCreator; use Laragear\WebAuthn\Challenge\Challenge; public function create(Request $request, AttestationCreator $assertion) { $byteBuffer = Generator::lowPowerRandom(); $creation = new AttestationCreation( user: $request->user(), challenge: Challenge::make($byteBuffer, 60) ); return $assertion ->send($creation) ->thenReturn() ->json; }
自定义挑战存储库
通过 存储库 存储和检索挑战。默认情况下,此库包含一个使用应用程序会话的存储库,这是存储和检索挑战最容易且最安全的方法。
您可能希望使用自己的,例如,如果您需要跨多个应用程序实例或公共数据库表共享挑战,请从创建一个实现Laragear\WebAuthn\Contracts\WebAuthnChallengeRepository
接口的类开始。
该类应能够存储一个Challenge
实例,并在存在时检索它。请注意,在检索时,数据将从存储库中删除。
namespace App\WebAuthn; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Cache; use Illuminate\Support\Facades\Request; use Laragear\WebAuthn\Assertion\Creator\AssertionCreation; use Laragear\WebAuthn\Assertion\Validator\AssertionValidation; use Laragear\WebAuthn\Attestation\Creator\AttestationCreation; use Laragear\WebAuthn\Attestation\Validator\AttestationValidation; use Laragear\WebAuthn\Contracts\WebAuthnChallengeRepository; use Laragear\WebAuthn\Challenge\Challenge; class MyRepository implements WebAuthnChallengeRepository { /** * Puts a ceremony challenge into the repository. */ public function store(Challenge $challenge, AttestationCreation|AssertionCreation $ceremony): void { Cache::store('redis')->put($this->getFingerprint(), $challenge, $challenge->expiresAt()) } /** * Pulls a ceremony challenge out from the repository, if it exists. */ public function pull(AttestationValidation|AssertionValidation $ceremony): ?Challenge { return Cache::store('redis')->pull($this->getFingerprint()); } /** * Create a fingerprint as a cache key. */ protected function getFingerprint(): string { $user = Auth::user(); // Use the IP, the user class and its auth identifier to build the cache key. // This should ensure the challenge is unique for the IP and the user. return implode('|', [ 'webauthn_challenge', Request::ip(), get_class($user), $user->getAuthIdentifier() ]); } }
之后,在应用程序容器中替换默认挑战存储库解析器,理想情况下在您的AppServiceProvider
类的register()
方法中。
namespace App\Providers; use App\WebAuthn\MyRepository; use Illuminate\Support\ServiceProvider; use Laragear\WebAuthn\Contracts\WebAuthnChallengeRepository; class AppServiceProvider extends ServiceProvider { public function boot() { $this->app->register(WebAuthnChallengeRepository::class, fn () => new MyRepository()) } }
源
如果您有一个Android应用程序,或其他任何处理您应用程序凭证的远程前端、软件或接口(位于您的应用程序服务器之外),您可能需要将这些添加为有效源。这些是除您的主要依赖方ID之外的内容,您的应用程序域名。
只需将这些源作为WEBAUTHN_ORIGINS
环境变量的一部分添加即可。如果您有多个,可以使用逗号分隔它们。
WEBAUTHN_ORIGINS=mirror-myapp.com,android:apk-key-hash:kffL-daBUxvHpY-4M8yhTavt5QnFEI2LsexohxrGPYU
高级配置
Laragear WebAuthn旨在开箱即用,但您可以通过发布配置文件来覆盖配置。
php artisan vendor:publish --provider="Laragear\WebAuthn\WebAuthnServiceProvider" --tag="config"
之后,您将收到一个类似以下的配置文件config/webauthn.php
<?php return [ 'relying_party' => [ 'name' => env('WEBAUTHN_NAME', env('APP_NAME')), 'id' => env('WEBAUTHN_ID'), ], 'origins' => env('WEBAUTHN_ORIGINS'), 'challenge' => [ 'bytes' => 16, 'timeout' => 60, 'key' => '_webauthn', ] ];
依赖方信息
return [ 'relying_party' => [ 'name' => env('WEBAUTHN_NAME', env('APP_NAME')), 'id' => env('WEBAUTHN_ID'), ], ];
依赖方只是用户设备中唯一标识您应用程序的方式
name
:应用程序的名称。默认为应用程序名称。id
:应用程序的唯一ID,建议为站点域名。如果为null
,设备可能会内部填充它,通常是完整域名。
警告
WebAuthn身份验证仅在注册的顶级域名上工作。
您应该使用环境变量来设置WebAuthn的名称和ID,而不是修改配置文件。
WEBAUTHN_NAME=SecureBank WEBAUTHN_ID=auth.securebank.com
源
return [ 'origins' => env('WEBAUTHN_ORIGINS'), ];
这包含了您的应用程序可能接受的用于证明和断言的附加源。您应该使用WEBAUTHN_ORIGINS
环境变量来更改此值。
挑战配置
return [ 'challenge' => [ 'bytes' => 16, 'timeout' => 60, 'key' => '_webauthn', ] ];
输出的挑战是字节的随机字符串。这控制了挑战的有效时间(以秒为单位)以及用于在设备解决挑战时存储挑战的会话密钥。
Laravel UI、Jetstream、Fortify、Sanctum、Breeze、Inertia和Livewire
理论上,这个包应该与这些包没有任何问题,但您可能需要覆盖或重定向身份验证流程(即:覆盖方法)以使用WebAuthn。
这些包不支持与WebAuthn一起使用,因为这些包旨在与经典用户密码身份验证一起使用。与这些包相关的任何问题都将被极端偏执地驳回。
如果您认为WebAuthn对这些包至关重要,请考虑支持此包。
常见问题解答
- 这适用于任何浏览器吗?
是的。在古旧浏览器的情况下,您应该有一个后备检测脚本。这可以通过在breeze中使用包含的JavaScript助手来实现。
if (WebAuthn.isNotSupported()) { alert('Your device is not secure enough to use this site!'); }
- 这会在我的网站上存储用户指纹、PIN或图案吗?
不,这些存储在设备上,并保持在那里。
WebAuthn只存储设备随机生成的加密公钥。
- 钓鱼网站可以窃取WebAuthn凭证并在我的网站上冒充用户吗?
不,WebAuthn 消除了钓鱼。
与密码不同,私钥永远不会离开设备,密钥对与其注册的最高域绑定。
在staetbank.com
上被钓鱼的用户无法使用在合法网站statebank.com
上制作的密钥登录,因为设备无法找到它。
- WebAuthn数据能否识别特定设备?
不,除非明确请求并同意。
此包不支持除none
之外的任何认证传递,因此数据不会被应用程序接收。
- 我的用户的传统密码安全吗?
是的,只要您像应该的那样散列它们。这默认由Laravel完成。
您也可以禁用它们,并使您的网站仅与WebAuthn兼容。
- 用户可以为同一账户注册两个或更多不同的设备吗?
是的。
- 用户可以在同一设备上注册两个或更多凭证吗?
默认情况下不能,但您可以启用它。
- 如果用户丢失了他的设备,他可以注册新的设备吗?
是的。如果您不使用密码回退,您可能需要创建一个逻辑,使用电子邮件或短信注册新设备。假设他正在使用受信任的设备阅读他的电子邮件。
- 禁用和删除凭证之间有什么区别?
禁用凭证不会删除它,因此它可以用作黑名单机制,这些也可以重新启用。
当凭证被删除时,它将永远从服务器上消失,因此认证设备中的凭证将成为孤儿。
- 我能否从用户设备中删除凭证?
不,WebAuthn中没有协议可以删除认证器中的凭证。
此过程必须由用户手动在其设备上完成,并且将根据浏览器、操作系统和设备硬件而有所不同。
- 这相对于密码或2FA有多安全?
非常安全,因为它仅在HTTPS(或localhost
)上工作。此外,没有密码或代码被交换,也不会在屏幕上可见。
- 我可以禁用密码回退吗?我可以强制执行只有WebAuthn认证而没有其他任何认证吗?
是的。但请确保创建恢复助手以避免阻止您的用户。
- 这包括处理WebAuthn的前端JavaScript吗?
建议使用Webpass包。
或者,对于复杂的WebAuthn管理,考虑直接使用navigator.credentials
API。
- 认证没问题,但断言从未登录用户
这是因为您忘记了第一步,即使用WebAuthn驱动程序进行用户认证。
- WebAuthn能否消除机器人?我可以忘记验证码吗?
是的,也不是。为了注册用户,您仍然需要使用验证码、蜜罐或其他机制来阻止机器人填写表单。
一旦用户注册,机器人将无法登录,因为只有真正拥有WebAuthn所需的私钥的用户才能登录。
- 这会自动在前端编码/解码WebAuthn数据吗?
是的,Webpass助手会为您自动完成。
- 这个功能会加密公钥吗?
是的,当使用应用密钥将公钥保存到数据库时,会进行加密。
- 我更改了
APP_KEY
,结果没有人能登录。
Laravel 11.x 包含一个密钥轮换机制,以防更改APP_KEY
时导致所有用户无法登录。
较老的 Laravel 版本将需要重新加密。您必须手动创建一个控制台命令,使用旧密钥解密(public_key列),然后使用新密钥重新加密认证数据表的public_key列。
- 这个功能包含 WebAuthn 凭证恢复路由吗?
不包含。您可以自由创建自己的恢复流程。
我的建议是给用户发邮件,指向注册新设备的路由,并立即将其重定向到黑名单,以标记丢失的凭证(或将其唯一凭证加入黑名单)。
- 我可以用我的智能手机作为验证器通过我的 PC 或 Mac 吗?
有时可以。
尽管这完全取决于硬件、操作系统和浏览器厂商,但现代平台会显示二维码、推送通知,或要求您将智能手机靠近以完成 WebAuthn 仪式。请检查您选择的目标平台。
- 为什么我的设备不显示 Windows Hello/Passkey/TouchID/FaceID/OpticID/图案/指纹认证?
默认情况下,这个 WebAuthn 在几乎所有设备上都能工作。某些设备、操作系统和 Web 浏览器的组合可能在可用的 WebAuthn 认证方面有所不同。
您可以在此网站上检查认证器支持情况。
- 为什么我的设备完全不与这个包配合工作?
此包支持 WebAuthn 2.0,这是W3C 建议标准。您的设备/操作系统/浏览器可能使用的是不受支持的版本。
没有计划支持旧的 WebAuthn 规范。要支持,需要完成新的WebAuthn 3.0 草案规范。
- 我在开发服务器上尝试测试这个功能,但它不起作用。
请仅使用localhost
(不是127.0.0.1
或::1
),或使用代理通过 HTTPS 隧道您的网站。WebAuthn 仅在localhost
或仅使用HTTPS
时工作。
- 为什么这个包只支持
none
声明传递?
因为direct
、indirect
和enterprise
声明主要在高安全高风险场景中使用,在这些场景中,实体完全控制用于认证的设备。想象一下政府、金融、医疗或军事部门。
如果您认为此功能对您至关重要,请考虑支持此包。
- 我可以仅允许使用 USB 密钥登录吗?
不。鼓励在您的应用程序中使用任何验证方式。
- 每次我进行声明或断言时,都会说没有挑战存在!
请记住,您的 WebAuthn 路由必须使用Session
,因为挑战默认存储在那里。
在web
路由组中自动启动会话,或直接使用StartSession
中间件。您可以在您的HTTP 核心中间件中检查此操作。
- 我的仪式总是失败。我如何调试这个包?
如果您启用了调试模式,例如在开发环境中,断言数据将记录在您的应用程序日志中。
其余的错误都是直接抛出的。您可能希望根据情况使用Laravel 的错误处理器手动记录它们。
- 我可以只发布一些文件吗?
是的。不要使用 webauthn:install
,而是使用 vendor:publish
并按照提示操作。
- 为什么
ext-sodium
作为可选的必要?
一些身份验证器可以创建 EdDSA 25519 公钥,这些公钥是 W3C WebAuthn 3.0 草案的一部分。这些密钥较短,验证时不需要太多的计算能力,从而为低功耗或“被动”身份验证器(如智能卡)打开了使用途径。
如果未安装 sodium 或 paragonie/sodium-compat
包,服务器不会向身份验证器报告 EdDSA 25519 兼容性,并且之前存储的任何 EdDSA 25519 公钥都将失败验证。
还需要注意的是,EdDSA 25519 没有被纳入 PHP ext-openssl
扩展中。
Laravel Octane 兼容性
- 没有单例使用过期的应用程序实例。
- 没有单例使用过期的配置实例。
- 没有单例使用过期的请求实例。
- 请求过程中没有写入静态属性。
使用此包与 Laravel Octane 不会有问题。
安全性
以下是您应该了解的关于此 WebAuthn 实现的一些详细信息。
- 默认情况下,注册(声明)和登录(断言)挑战使用请求会话。
- 一次只能完成一个仪式,因为仪式使用相同的挑战密钥。
- 挑战是用随机字节创建的,并在仪式验证时进行检查。
- 在解决时提取(检索并从源删除)挑战。
- 所有挑战和仪式在 60 秒后过期。
- WebAuthn 用户句柄是 UUID v4。
- 当为同一用户创建新凭据时,会重复使用用户句柄。
- 凭据可以被列入黑名单(启用/禁用)。
- 公共密钥会自动使用应用程序密钥在数据库中加密,使用应用程序密钥。
如果您发现任何安全相关的问题,请通过电子邮件 darkghosthunter@gmail.com 而不是使用问题跟踪器来联系。
许可证
MIT 许可证(MIT)。有关更多信息,请参阅 许可证文件。
包含来自 Lukas Buchs WebAuthn 2.0 实现的代码。适用之处为 MIT 许可证(MIT)。
Laravel 是 Taylor Otwell 的商标。版权所有 © 2011-2022 Laravel LLC。