code-lts/u2f-php-server

FIDO U2F 注册和身份验证的服务端处理类

v1.2.2 2024-07-24 15:01 UTC

This package is auto-updated.

Last update: 2024-08-24 15:09:29 UTC


README

Latest Stable Version GitHub license codecov

为 PHP 提供的 FIDO U2F 注册和身份验证的服务端处理。

保护您的在线账户和做您能做的事情来保护您的数据非常重要,并且随着黑客变得更加复杂,这一点变得越来越重要。FIDO 的 U2F 允许您添加一种简单且不引人注目的第二种因素认证方法,允许您的服务和/或应用程序的用户将硬件密钥链接到他们的账户。

关于此库分叉的故事

一切始于 2020 年 10 月的这个 问题。我迅速努力构建测试并提交了一个 拉取请求。但由于库的作者消失了,所以没有任何内容被合并。

这个分叉有什么变化?

对于第一个发布版本:没有任何变化,只是编写了测试。并且完成了对 PHP 8.1 的支持。未来的版本可能会有所变化。

U2F php 库系列

基础库

https://github.com/code-lts/U2F-php-server

内容

  1. 安装
  2. 需求
    1. OpenSSL
    2. 客户端处理
    3. HTTPS 和 SSL
  3. 术语
  4. 推荐的数据存储结构
  5. 流程
    1. 注册流程
    2. 身份验证流程
  6. 示例代码
    1. 兼容性检查
    2. 注册代码
    3. 身份验证代码
  7. 框架
    1. Laravel
    2. Yii
    3. CodeIgniter
  8. 许可证
  9. 致谢

安装

composer require code-lts/u2f-php-server

需求

在开始使用此之前,您需要了解一些 重要事项

  1. OpenSSL 您需要至少 OpenSSL 1.0.0 或更高版本。
  2. 客户端处理 您需要能够与某种设备进行通信。
  3. HTTPS URL 这非常重要,没有 HTTPS,U2F 将无法工作。
  4. 数据存储 您需要某种类型的数据存储来存储所有已注册的 U2F 用户(尽管如果您有一个具有用户认证的系统,我假设您已经处理了这一点)。

OpenSSL

此存储库需要 OpenSSL 1.0.0 或更高版本。有关安装 OpenSSL 的更多详细信息,请参阅 php 手册

还可以查看 兼容性代码,以检查您是否安装了正确的 OpenSSL 版本,并且不确定如何检查其他方式。

客户端(与 USB 设备通信的魔法 JavaScript 部分)

我的假设是,如果您正在寻找将 U2F 身份验证添加到 php 系统,那么您可能也在寻找一些客户端处理。您有一个启用了 U2F 的 USB 设备,并且希望该 USB 设备与浏览器以及运行 php 的服务器进行通信。

  1. 谷歌已经解决了这个问题: u2f-api.js
  2. Mastahyeti 创建了一个专门用于谷歌JavaScript客户端API的仓库: https://github.com/mastahyeti/u2f-api

HTTPS 和 SSL

为了使U2F工作,您的网站/服务必须使用HTTPS URL。没有HTTPS URL,您的代码将无法工作,所以为您的localhost和您的生产环境都获取一个。 https://letsencrypt.openssl.ac.cn/ 基本上加密一切。

术语

HID : 人机界面设备,例如USB设备 像这些设备

推荐的数据存储结构

您不需要完全遵循此结构,但您需要将注册数据与用户关联。您还需要存储密钥句柄、公钥和证书,计数器不是100%必要的,但它会使您的应用程序更安全。

待办事项:描述

流程

注册流程

  1. 用户导航到应用程序中的第二个因素认证页面。

... 待办事项:添加注册过程流程的其余部分 ...

身份验证流程

  1. 用户像平常一样导航到其登录页面,提交用户名和密码。
  2. 服务器收到POST请求认证数据,发生正常的用户名 + 密码验证。
  3. 在认证成功后,应用程序检查是否需要第二个因素认证。我们假设需要,否则用户将在这个阶段登录。
  4. 应用程序从应用程序数据存储中获取用户的注册签名:$registrations
  5. 应用程序获取其ID,通常是应用程序可访问的域:$appId
  6. 应用程序调用U2F::makeAuthentication($registrations, $appId),该方法返回一个SignRequest对象数组:$authenticationRequest
  7. 应用程序将数组JSON编码并传递给视图
  8. 当浏览器加载页面时,JavaScript将触发u2f.sign(authenticationRequest, function(data){ // 回调逻辑 })函数
  9. 视图将使用JavaScript / 浏览器轮询主机机器的端口以查找FIDO U2F设备
  10. 一旦找到HID,JavaScript / 浏览器将发送带有数据的签名请求。
  11. HID将提示用户授权签名请求
  12. 在成功后,HID返回认证数据
  13. JavaScript接收HID返回的数据并将其传递给服务器
  14. 应用程序接收返回的数据并将其传递给U2F::authenticate($authenticationRequest, $registrations, $authenticationResponse)方法
  15. 如果方法返回一个注册项且不抛出异常,则认证完成。
  16. 设置用户的会话,通知用户成功,并重定向。

示例代码

有关此存储库的完整工作代码示例,请参阅专门的示例存储库

您也可以使用以下方式安装它

$ git clone https://github.com/code-lts/U2F-php-server-examples.git
$ cd u2f-php-server-examples
$ composer install
  1. 兼容性代码
  2. 注册代码
    1. 步骤1:启动
    2. 步骤2:与HID通信
    3. 步骤3:验证和存储
  3. 身份验证代码
    1. 步骤1:启动
    2. 步骤2:与HID通信
    3. 步骤3:验证

兼容性代码

您只需在每个安装中调用此方法一次,并且仅在类给您意外错误的情况下进行调试时才需要。此方法调用将检查您的OpenSSL版本并确保它至少为1.0.0。

<?php

require __DIR__ . '/vendor/autoload.php';

use CodeLts\U2F\U2FServer\U2FServer as U2F;

var_dump(U2F::checkOpenSSLVersion());

注册代码

注册步骤1

开始注册过程

我们假设用户已成功认证并希望注册。

<?php

require __DIR__ . '/vendor/autoload.php';

use CodeLts\U2F\U2FServer\U2FServer as U2F;

session_start();

// This can be anything, but usually easier if you choose your applications domain and top level domain.
$appId = 'yourdomain.tld';

// Call the makeRegistration method, passing in the app ID
$registrationData = U2F::makeRegistration($appId);

// Store the request for later
$_SESSION['registrationRequest'] = $registrationData['request'];

// Extract the request and signatures, JSON encode them so we can give the data to our javaScript magic
$jsRequest = json_encode($registrationData['request']);
$jsSignatures = json_encode($registrationData['signatures']);

// now pass the data to a fictitious view.
echo View::make('template/location/u2f-registration.html', [
    'jsRequest' => $jsRequest,
    'jsSignatures' => $jsSignatures,
]);

注册步骤2

客户端,与USB通信

U2F密钥令牌的非AJAX客户端注册。当然,您可以在应用程序中使用AJAX,但使用AJAX和回调演示线性过程更容易。

<html>
<head>
    <title>U2F Key Registration</title>
</head>
<body>
    <h1>U2F Registration</h1>
    <h2>Please enter your FIDO U2F device into your computer's USB port. Then confirm registration on the device.</h2>

    <div style="display:none;">
        <form id="u2f_submission" method="post" action="auth/u2f-registration/confirm">
            <input id="u2f_registration_response" name="registration_response" value="" />
        </form>
    </div>

<script type="javascript" src="https://raw.githubusercontent.com/google/u2f-ref-code/master/u2f-gae-demo/war/js/u2f-api.js"></script>
<script>
    setTimeout(function() {

        // A magic JS function that talks to the USB device. This function will keep polling for the USB device until it finds one.
        u2f.register([<?php echo $jsRequest ?>], <?php echo $jsSignatures ?>], function(data) {

            // Handle returning error data
            if(data.errorCode && errorCode != 0) {
                alert('registration failed with error: ' + data.errorCode);
                // Or handle the error however you'd like.

                return;
            }

            // On success process the data from USB device to send to the server
            var registration_response = JSON.stringify(data);

            // Get the form items so we can send data back to the server
            var form = document.getElementById('u2f_submission');
            var response = document.getElementById('u2f_registration_response');

            // Fill and submit form.
            response.value = JSON.stringify(registration_response);
            form.submit();
        });
    }, 1000);
</script>
</body>
</html>

注册步骤3

验证和密钥存储

这是注册的最后阶段。将注册响应数据与原始请求数据进行验证。

<?php

require('vendor/autoload.php');
use CodeLts\U2F\U2FServer\U2FServer as U2F;

session_start();

// Fictitious function representing getting the authenticated user object
$user = getAuthenticatedUser();

try {

    // Validate the registration response against the registration request.
    // The output are the credentials you need to store for U2F authentication.
    $validatedRegistration = U2F::register(
        $_SESSION['registrationRequest'],
        json_decode($_POST['u2f_registration_response'])
    );

    // Fictitious function representing the storing of the validated U2F registration data against the authenticated user.
    $user->storeRegistration($validatedRegistration);

    // Then let your user know what happened
    $userMessage = 'Success';
} catch( Exception $e ) {
    $userMessage = 'We had an error: '. $e->getMessage();
}

//Fictitious view.
echo View::make('template/location/u2f-registration-result.html', [
    'userMessage' => $userMessage,
]);

身份验证代码

认证步骤1

开始认证过程

我们假设用户已成功认证,并且之前已注册使用FIDO U2F。

<?php

require('vendor/autoload.php');
use CodeLts\U2F\U2FServer\U2FServer as U2F;

session_start();

// Fictitious function representing getting the authenticated user object
$user = getAuthenticatedUser();

// Fictitious function, get U2F registrations associated with the user
$registrations = $user->U2FRegistrations();

// This can be anything, but usually easier if you choose your applications domain and top level domain.
$appId = 'yourdomain.tld';

// Call the U2F makeAuthentication method, passing in the user's registration(s) and the app ID
$authenticationRequest = U2F::makeAuthentication($registrations, $appId);

// Store the request for later
$_SESSION['authenticationRequest'] = $authenticationRequest;

// now pass the data to a fictitious view.
echo View::make('template/location/u2f-authentication.html', [
    'authenticationRequest' => $authenticationRequest,
]);

认证步骤 2

客户端,与USB通信

U2F密钥令牌的非AJAX客户端端认证。当然,您可以在应用程序中使用AJAX,但不用AJAX和回调函数来展示线性过程会更简单。

<html>
<head>
    <title>U2F Key Authentication</title>
</head>
<body>
    <h1>U2F Authentication</h1>
    <h2>Please enter your FIDO U2F device into your computer's USB port. Then confirm authentication on the device.</h2>

    <div style="display:none;">
        <form id="u2f_submission" method="post" action="auth/u2f-authentication/confirm">
            <input id="u2f_authentication_response" name="authentication_response" value="" />
        </form>
    </div>

    <script type="javascript" src="https://raw.githubusercontent.com/google/u2f-ref-code/master/u2f-gae-demo/war/js/u2f-api.js"></script>
    <script>
    setTimeout(function() {

        // Magic JavaScript talking to your HID
        u2f.sign(<?php echo $authenticationRequest; ?>, function(data) {

            // Handle returning error data
            if(data.errorCode && errorCode != 0) {
                alert('Authentication failed with error: ' + data.errorCode);
                // Or handle the error however you'd like.

                return;
            }

            // On success process the data from USB device to send to the server
            var authentication_response = JSON.stringify(data);

            // Get the form items so we can send data back to the server
            var form = document.getElementById('u2f_submission');
            var response = document.getElementById('u2f_authentication_response');

            // Fill and submit form.
            response.value = JSON.stringify(authentication_response);
            form.submit();
        });
    }, 1000);
    </script>
</body>
</html>

认证步骤 3

验证

这是认证的最后阶段。将认证响应数据与原始请求数据进行验证。

<?php

require('vendor/autoload.php');
use CodeLts\U2F\U2FServer\U2FServer as U2F;

session_start();

// Fictitious function representing getting the authenticated user object
$user = authenticatedUser();

// Fictitious function, get U2F registrations associated with the user
$registrations = $user->U2FRegistrations();

try {

    // Validate the authentication response against the registration request.
    // The output are the credentials you need to store for U2F authentication.
    $validatedAuthentication = U2F::authenticate(
        $_SESSION['authenticationRequest'],
        $registrations,
        json_decode($_POST['u2f_authentication_response'])
    );

    // Fictitious function representing the updating of the U2F token count integer.
    $user->updateU2FRegistrationCount($validatedAuthentication);

    // Then let your user know what happened
    $userMessage = 'Success';
} catch( Exception $e ) {
    $userMessage = 'We had an error: '. $e->getMessage();
}

//Fictitious view.
echo View::make('template/location/u2f-authentication-result.html', [
    'userMessage' => $userMessage,
]);

再次提醒,如果您只想下载一些示例代码进行测试,请安装此仓库中为该仓库编写的完整工作示例代码,请参阅专门的示例仓库

您也可以使用以下方式安装它

$ git clone https://github.com/code-lts/U2F-php-server-examples.git
$ cd u2f-php-server-examples
$ composer install

许可证

该仓库采用BSD许可证。请在此处查看详细信息

致谢

此仓库最初基于Yubico php-u2flib-server