此软件包已被废弃,不再维护。未建议替代软件包。

使用设备、指纹或生物识别数据验证用户。再见了,密码!

v3.0.2 2021-11-01 21:17 UTC

This package is auto-updated.

Last update: 2022-07-12 19:49:10 UTC


README

Lukenn Sabellano - Unsplash (UL) #RDufjtg6JpQ

Latest Stable Version License Coverage Status Laravel Octane Compatible

Larapass

使用设备、指纹或生物识别数据验证用户。再见了,密码!

这使您能够在 Laravel 认证驱动程序内部使用 WebAuthn 认证,并提供了一切除了厨房用具之外的东西。

要求

  • PHP 7.4 或 PHP 8.0
  • Laravel 7.18(2020年7月)或 Laravel 8.x

对于 Laravel 9.x 的支持和以后的版本,请使用 Laragear WebAuthn

安装

只需在控制台中运行并使用 Composer 依赖它。

composer require darkghosthunter/larapass

遗憾的是,使用 WebAuthn 并非易事,此软件包允许您以最简单的方式启用 WebAuthn。

目录

什么是 WebAuthn?它是如何使用指纹或其他方法的?

简而言之,主流浏览器都兼容 Web 认证 API,将认证推向设备(指纹、面部识别、图案、代码等),而不是明文密码。

此软件包使用自定义 用户提供者 来验证来自设备的 WebAuthn 有效负载。

如果您对 WebAuthn 有任何疑问,请查看此小型常见问题解答。要深入了解,请访问 WebAuthn.ioWebAuthn.meGoogle WebAuthn 教程

设置

我们需要确保您的用户可以注册他们的设备并使用它们进行认证。

  1. 添加 eloquent-webauthn 驱动程序.
  2. 创建 webauthn_credentials
  3. 实现合约和特质

之后,您可以使用包含的控制器和辅助工具快速开始 WebAuthn,以简化您的工作。

  1. 注册路由
  2. 使用 JavaScript 辅助工具
  3. 设置账户恢复

1. 添加 eloquent-webauthn 驱动程序

本包包含一个与Eloquent兼容的用户提供者,用于验证来自设备的WebAuthn响应。

前往您的config/auth.php配置文件,将您使用的提供者驱动程序更改为eloquent-webauthn

return [
    // ...

    'providers' => [
        'users' => [
            // 'driver' => 'eloquent', // Default Eloquent User Provider 
            'driver' => 'eloquent-webauthn',
            'model' => App\User::class,
        ],
    ]
];

如果您计划为WebAuthn创建自己的用户提供者驱动程序,请记住注入WebAuthnAssertValidator以正确验证用户和传入的响应。

2. 创建webauthn_credentials

通过发布迁移文件并迁移表来创建webauthn_credentials表。

php artisan vendor:publish --provider="DarkGhostHunter\Larapass\LarapassServiceProvider" --tag="migrations"
php artisan migrate

3. 实现契约和特质

WebAuthnAuthenticatable契约和WebAuthnAuthentication特质添加到Authenticatable用户类或任何使用身份验证的类。

<?php

namespace App;

use Illuminate\Foundation\Auth\User as Authenticatable;
use DarkGhostHunter\Larapass\Contracts\WebAuthnAuthenticatable;
use DarkGhostHunter\Larapass\WebAuthnAuthentication;

class User extends Authenticatable implements WebAuthnAuthenticatable
{
    use WebAuthnAuthentication;

    // ...
}

此特质用于将用户模型与数据库中包含的WebAuthn数据相关联。

4. 注册路由(可选)

最后,您需要添加注册和验证用户的路由。如果您想快速开始,只需发布Larapass中包含的控制器。

php artisan vendor:publish --provider="DarkGhostHunter\Larapass\LarapassServiceProvider" --tag="controllers"

您可以将这些路由定义复制粘贴到您的routes/web.php文件中。

use App\Http\Controllers\Auth\WebAuthnRegisterController;
use App\Http\Controllers\Auth\WebAuthnLoginController;

Route::post('webauthn/register/options', [WebAuthnRegisterController::class, 'options'])
     ->name('webauthn.register.options');
Route::post('webauthn/register', [WebAuthnRegisterController::class, 'register'])
     ->name('webauthn.register');

Route::post('webauthn/login/options', [WebAuthnLoginController::class, 'options'])
     ->name('webauthn.login.options');
Route::post('webauthn/login', [WebAuthnLoginController::class, 'login'])
     ->name('webauthn.login');

在您的前端脚本中,将请求指向这些路由。

如果您想要完全控制,可以选择不使用这些辅助控制器并使用您自己的逻辑。如果您需要从头开始,可以使用AttestWebAuthnAssertsWebAuthn特质。

5. 使用JavaScript助手(可选)

本包包含一个方便的脚本,用于通过WebAuthn处理注册和登录。要使用它,只需将larapass.js资产发布到应用程序的公共资源中。

php artisan vendor:publish --provider="DarkGhostHunter\Larapass\LarapassServiceProvider" --tag="public"

您将收到vendor/larapass/js/larapass.js文件,您可以将它包含到您的身份验证视图中并按需使用它。

<script src="{{ asset('vendor/larapass/js/larapass.js') }}"></script>

<!-- Registering credentials -->
<script>
    const register = (event) => {
        event.preventDefault()
        new Larapass({
            register: 'webauthn/register',
            registerOptions: 'webauthn/register/options'
        }).register()
          .then(response => alert('Registration successful!'))
          .catch(response => alert('Something went wrong, try again!'))
    }

    document.getElementById('register-form').addEventListener('submit', register)
</script>

<!-- Login users -->
<script>
    const login = (event) => {
        event.preventDefault()
        new Larapass({
            login: 'webauthn/login',
            loginOptions: 'webauthn/login/options'
        }).login({
            email: document.getElementById('email').value
        }).then(response => alert('Authentication successful!'))
          .catch(error => alert('Something went wrong, try again!'))
    }

    document.getElementById('login-form').addEventListener('submit', login)
</script>

如果您使用默认值,可以省略路由列表声明。上面的示例只是为了展示。请确保根据您的需求修改此脚本。

此外,此助手允许在注册和登录请求上使用标题。

new Larapass({
    login: 'webauthn/login',
    loginOptions: 'webauthn/login/options'
}).login({
    email: document.getElementById('email').value,
}, {
    'My-Custom-Header': 'This is sent with the signed challenge',
})

您可以将它复制粘贴并导入到Laravel MixBabelWebpack之类的转译器中。如果脚本不符合您的需求,您可以自由创建自己的。

记住用户

您可以通过在将签名登录挑战从前端推送时将WebAuthn-Remember标题值设置为true来启用它。我们可以使用包含的JavaScript助手轻松做到这一点。

new Larapass.login({
    email: document.getElementById('email').value
}, {
    'WebAuthn-Remember': true
})

或者,如果您使用自己的脚本,可以将remember键添加到输出的JSON有效负载中。两种方式都受接受。

您可以在AssertsWebAuthn特性中覆盖此行为。

6. 设置账户恢复(可选)

您可能希望提供一个方法来“恢复”账户,如果用户丢失了他的凭据,这基本上是一种附加新凭据的方式。您可以使用控制器,这些控制器也被发布了,以及这些路由

use App\Http\Controllers\Auth\WebAuthnDeviceLostController;
use App\Http\Controllers\Auth\WebAuthnRecoveryController;

Route::get('webauthn/lost', [WebAuthnDeviceLostController::class, 'showDeviceLostForm'])
     ->name('webauthn.lost.form');
Route::post('webauthn/lost', [WebAuthnDeviceLostController::class, 'sendRecoveryEmail'])
     ->name('webauthn.lost.send');

Route::get('webauthn/recover', [WebAuthnRecoveryController::class, 'showResetForm'])
     ->name('webauthn.recover.form');
Route::post('webauthn/recover/options', [WebAuthnRecoveryController::class, 'options'])
     ->name('webauthn.recover.options');
Route::post('webauthn/recover/register', [WebAuthnRecoveryController::class, 'recover'])
     ->name('webauthn.recover');

这些包括新视图翻译行,因此如果您对包含的内容不满意,可以覆盖它们。

您还可以通过用户sendCredentialRecoveryNotification方法的resources/vendor/larapass中的视图和发送的通知来覆盖视图。

之后,不要忘记在您的config/auth.php中添加一个新的token代理。我们将需要它来存储恢复过程中的令牌。

return [
    // ...

    'passwords' => [
        'users' => [
            'provider' => 'users',
            'table' => 'password_resets',
            'expire' => 60,
            'throttle' => 60,
        ],

        // New for WebAuthn
        'webauthn' => [
            'provider' => 'users', // The user provider using WebAuthn.
            'table' => 'web_authn_recoveries', // The table to store the recoveries.
            'expire' => 60,
            'throttle' => 60,
        ],
    ],
];

确认中间件

遵循与password.confirm中间件相同的原理,Larapass包括一个webauthn.confirm中间件,它将在用户进入特定路由之前要求用户使用其设备进行确认。

Route::get('this/is/important', function () {
    return 'This is very important!';
})->middleware('webauthn.confirm');

发布控制器时,WebAuthnConfirmController将位于您的控制器文件中,准备接受确认。您只需复制粘贴以下内容来注册路由

Route::get('webauthn/confirm', 'Auth\WebAuthnConfirmController@showConfirmForm')
     ->name('webauthn.confirm.form');
Route::post('webauthn/confirm/options', 'Auth\WebAuthnConfirmController@options')
     ->name('webauthn.confirm.options');
Route::post('webauthn/confirm', 'Auth\WebAuthnConfirmController@confirm')
     ->name('webauthn.confirm');

与往常一样,您可以选择使用自己的逻辑退出。对于这些情况,请查看ConfirmsWebAuthn特性以开始。

您可以在配置中更改记住确认的时间

事件

由于所有身份验证都由Laravel本身处理,因此包含的唯一事件AttestationSuccessful,它在注册成功时触发。它包括凭据持久化的用户。

您可以使用此事件来,例如,通知用户已注册新设备。为此,您可以使用监听器

public function handle(AttestationSuccessful $event)
{
    $event->user->notify(
        new DeviceRegisteredNotification($event->credential->getId())
    );
}

使用WebAuthn的操作

此包简化了操作WebAuthn 仪式(认证和断言)。为此,使用方便的WebAuthn外观。

认证(注册)

使用generateAttestationvalidateAttestation为您的用户。后者返回已验证的凭据,因此您可以手动保存它们。

<?php

use App\User; 
use Illuminate\Support\Facades\Auth;
use DarkGhostHunter\Larapass\Facades\WebAuthn;

$user = Auth::user();

// Create an attestation for a given user.
return WebAuthn::generateAttestation($user);

然后我们稍后可以验证它

<?php

use App\User; 
use Illuminate\Support\Facades\Auth;
use DarkGhostHunter\Larapass\Facades\WebAuthn;

$user = Auth::user();

// Verify it
$credential = WebAuthn::validateAttestation(
    request()->json()->all(), $user
);

// And save it.
if ($credential) {
    $user->addCredential($credential);
} else {
    return 'Something went wrong with your device!';
}

断言(登录)

断言可以通过使用generateAssertion创建请求,并通过validateAssertion进行验证。

<?php

use App\User; 
use DarkGhostHunter\Larapass\Facades\WebAuthn;

// Find the user to assert, if there is any
$user = User::where('email', request()->input('email'))->first();

// Create an assertion for the given user (or a blank one if not found);
return WebAuthn::generateAssertion($user);

然后我们稍后可以验证它

<?php

use App\User;
use Illuminate\Support\Facades\Auth;
use DarkGhostHunter\Larapass\Facades\WebAuthn;

// Verify the incoming assertion.
$credentials = WebAuthn::validateAssertion(
    request()->json()->all()
);

// If is valid, login the user of the credentials.
if ($credentials) {
    Auth::login(
        User::getFromCredentialId($credentials->getPublicKeyCredentialId())
    );
}

凭证

您可以通过直接从用户实例中调用WebAuthnAuthenticatable合约来管理用户凭证。最有用的方法有:

  • hasCredential():检查用户是否有指定的凭证ID。
  • addCredential():添加新的凭证来源。
  • removeCredential():通过ID删除现有的凭证。
  • flushCredentials():删除所有凭证。您可以排除具有特定ID的凭证。
  • enableCredential():将现有的凭证ID包含在身份验证中。
  • disableCredential():将现有的凭证ID排除在身份验证之外。
  • getFromCredentialId():如果存在,则返回具有给定凭证ID的用户。

您可以使用这些方法,例如,将被盗设备/凭证列入黑名单并注册新的,或者通过清除所有已注册设备完全禁用WebAuthn。

高级配置

Larapass旨在即插即用,但您可以简单地发布配置文件来覆盖配置。

php artisan vendor:publish --provider="DarkGhostHunter\Larapass\LarapassServiceProvider" --tag="config"

之后,您将收到一个包含类似以下数组的config/larapass.php配置文件:

<?php

return [
    'relaying_party' => [
        'name' => env('WEBAUTHN_NAME', env('APP_NAME')),
        'id'   => env('WEBAUTHN_ID'),
        'icon' => env('WEBAUTHN_ICON'),
    ],
    'bytes' => 16,
    'timeout' => 60,
    'cache' => env('WEBAUTHN_CACHE'),
    'algorithms' => [
        \Cose\Algorithm\Signature\ECDSA\ES256::class,
        \Cose\Algorithm\Signature\EdDSA\Ed25519::class,
        \Cose\Algorithm\Signature\ECDSA\ES384::class,
        \Cose\Algorithm\Signature\ECDSA\ES512::class,
        \Cose\Algorithm\Signature\RSA\RS256::class,
    ],
    'attachment' => null,
    'conveyance' => 'none',
    'login_verify' => 'preferred',
    'userless' => null,
    'unique' => false,
    'fallback' => true,
    'confirm_timeout' => 10800,
];

中继方信息

return [
    'relaying_party' => [
        'name' => env('WEBAUTHN_NAME', env('APP_NAME')),
        'id'   => env('WEBAUTHN_ID'),
        'icon' => env('WEBAUTHN_ICON'),
    ],
];

中继方是唯一标识用户设备中应用程序的一种方式。

  • name:应用程序的名称。默认为应用程序名称。
  • id:应用程序的可选域名。如果为null,设备将内部填充。
  • icon:可选的BASE64(最大128字节)中的图像数据或图像URL。

请考虑使用基础域名(如myapp.com)作为id,以允许所有子域名(如foo.myapp.com)上的凭证。

挑战配置

return [
    'bytes' => 16,
    'timeout' => 60,
    'cache' => env('WEBAUTHN_CACHE'),
];

待签署的输出挑战是一个字节的随机字符串。这控制了挑战的字节数、超时(之后将标记为无效)以及用于存储在设备解决挑战时使用的缓存。

算法

return [
    'algorithms' => [
        \Cose\Algorithm\Signature\ECDSA\ES256::class,   // ECDSA with SHA-256
        \Cose\Algorithm\Signature\EdDSA\Ed25519::class, // EdDSA
        \Cose\Algorithm\Signature\ECDSA\ES384::class,   // ECDSA with SHA-384
        \Cose\Algorithm\Signature\ECDSA\ES512::class,   // ECDSA with SHA-512
        \Cose\Algorithm\Signature\RSA\RS256::class,     // RSASSA-PKCS1-v1_5 with SHA-256
    ],
];

这控制了验证器(设备)将如何操作以创建公钥-私钥。这些COSE算法是与设备内和漫游密钥最兼容的,因为一些必须在低带宽协议上传输。

除非您知道自己在做什么,否则请勿添加或删除类。真的。只需保持它们原样。

密钥附件

return [
     'attachment' => null,
];

默认情况下,用户决定使用什么进行注册。如果您想仅使用跨平台身份验证(如USB密钥、CA服务器或证书),请将其设置为true,或如果想要强制仅设备身份验证,则设置为false

认证传达

return [
    'conveyance' => null,
];

认证传达表示是否应该由您验证设备密钥。虽然大多数时候不需要,但您可以将此更改为indirect(您验证它来自可信赖的来源)或direct(设备包含验证数据)。

如果您不知道自己在做什么,请保持原样。

登录验证

return [
    'login_verify' => 'preferred',
];

默认情况下,大多数认证器会在登录时要求用户验证。如果您想没有任何例外,可以将其设置为required

您还可以使用discouraged来仅检查用户是否存在(如“继续”按钮),这可能会使登录更快,但安全性略低。

当将无用户设置为preferredrequired时,将自动将其重置为required

无用户登录(一键,无输入)

return [
    'userless' => null,
];

您可以在设备注册时激活无用户登录,也称为一键登录或无输入登录。在这种情况下,应将其更改为preferred,因为并非所有设备都支持该功能。

如果已激活(不是nulldiscouraged),则登录验证将是强制性的。

这不会影响登录流程,只会影响证明(注册)。

唯一

return [
    'unique' => false,
];

如果为true,设备将限制设备只能创建一个凭据。这是通过告诉设备用户已经拥有的凭据ID列表来实现的。如果至少有一个已经在设备中存在,则后者将返回错误。

密码回退

return [
    'fallback' => true,
];

默认情况下,此包允许在凭据不是WebAuthn JSON有效负载时使用相同的eloquent-webauthn驱动程序以密码登录用户。

禁用回退将仅验证WebAuthn凭据。为了处理经典的用户/密码场景,您可能需要创建一个单独的守卫。

确认超时

return [
    'confirm_timeout' => 10800,
];

当使用确认中间件时,确认将在设置的一定秒数内保持。默认为3小时,对于大多数场景来说足够了。

证明和元数据语句支持

如果您需要非常高的安全级别,应使用证明和元数据语句。您基本上会要求认证器提供其真实性,并以多种方式进行检查。

为此,请查看此文章并根据需要扩展服务容器中的类。

<?php

use Webauthn\AttestationStatement\AttestationStatementSupport;
use Webauthn\AttestationStatement\AndroidSafetyNetAttestationStatementSupport;

$this->app->extend(AttestationStatementSupport::class, function ($manager) {
    $manager->add(new AndroidSafetyNetAttestationStatementSupport());
});

安全

以下是关于此WebAuthn实现的一些详细信息

  • 注册(证明)仅限于域名、IP和用户。
  • 登录(断言)如果指定,则仅限于域名、IP和用户。
  • 缓存的挑战在解决后总是会被遗忘,独立于结果。
  • 缓存的挑战TTL与WebAuthn超时相同(默认60秒)。
  • 包含的控制器包括WebAuthn端点的节流。
  • 用户ID(句柄)是随机UUID v4。
  • 凭据可以被列入黑名单(启用/禁用)。

如果您发现任何安全相关的问题,请通过电子邮件darkghosthunter@gmail.com而不是使用问题跟踪器。

作为旁注,请记住,如果您的应用程序位于负载均衡器后面,请正确配置您的应用程序

常见问题解答

  • 这适用于任何浏览器吗?

。对于旧浏览器,你应该有一个回退检测脚本。这可以通过包含的JavaScript辅助工具轻松完成。

if (! Larapass.supportsWebAuthn()) {
   alert('Your device is not secure enough to use this site!');
}
  • 我的网站是否存储了用户的指纹、PIN码或图案?

不。它存储设备生成的公钥。

  • 钓鱼网站能否窃取WebAuthn凭证并在我的网站上使用它们?

不。WebAuthn终结了钓鱼。

  • WebAuthn数据能否识别特定的设备?

不,除非明确请求并同意。

  • 我的用户的传统密码安全吗?

是的,只要你像应该的那样对它们进行散列,并且已经保护了你的应用程序密钥。这默认由Laravel完成。你也可以禁用它

  • 用户能否注册两个或更多设备

是的。

  • 如果凭据被克隆会发生什么?

由于“登录”计数器将大于原始设备报告的,用户将无法通过身份验证。要干预该过程,请在服务容器中修改断言验证器并添加自己的CounterChecker

$this->app->bind(CounterChecker::class, function () {
    return new \App\WebAuthn\MyCountChecker;
});

在你的计数器检查器内部,你可能希望在计数器低于报告的值时抛出异常。

<?php

namespace App\WebAuthn;

use Webauthn\Counter\CounterChecker;
use App\Exceptions\WebAuthn\CredentialCloned;
use Webauthn\PublicKeyCredentialSource as Credentials;

class MyCountChecker implements CounterChecker
{
    public function check(Credentials $credentials, int $currentCounter) : void
    {
        if ($credentials->getCounter() <= $currentCounter) {
            throw new CredentialCloned($credentials);
        } 
    }
}
  • 如果用户丢失了他的设备,他能注册新设备吗?

是的,使用这些恢复辅助工具

  • 禁用和删除凭据之间的区别是什么?

禁用凭据不会删除它,因此如果用户恢复它,可以稍后手动启用。当凭据被删除时,它将永远消失。

  • 用户能否从其设备中删除其凭据?

是的。如果这样做,服务器上凭据的其他部分将被虚拟地遗弃。你可能想向用户显示注册凭据的列表,以便它们可以删除。

  • 这是对密码或2FA有多安全?

极其安全,因为它仅在HTTPS(或localhost)上工作,并且没有密码交换,或屏幕上可见的代码。

  • 我能禁用密码回退吗?我能强制只进行WebAuthn身份验证吗?

是的。只需确保使用恢复辅助工具,以免将用户锁定在外。

  • 这包括前端JavaScript吗?

是的,但它非常基本

  • 这会在前端自动编码/解码字符串吗?

是的,包含的WebAuthn辅助工具会自动为您完成。

  • 这包括凭据恢复路由吗?

是的。

  • 我能否通过PC桌面/笔记本电脑/终端使用我的智能手机作为认证器?

这取决于操作系统和硬件。有些可能需要在“帐户”中预先配对设备。其他一些可能仅适用于USB密钥。这取决于硬件和软件供应商。

  • 为什么我的设备不显示Windows Hello/TouchId/FaceId/指纹认证?

默认情况下,此WebAuthn实现几乎接受任何东西。某些设备、操作系统和Web浏览器的组合可能不同,不知道为WebAuthn身份验证提供什么。换句话说,这不是我的错。

  • 我正在尝试在我的开发服务器上测试这个,但它不起作用

请仅使用localhost,或使用ngrok(或类似)通过HTTPS隧道您的网站。WebAuthn仅在localhost或HTTPS上工作。

许可证

MIT许可证(MIT)。有关更多信息,请参阅许可证文件

Laravel是Taylor Otwell的商标。版权所有©2011-2020 Laravel LLC。