生成和验证一次性密码的库。

维护者

详细信息

github.com/jiripudil/otp

源代码

问题

资助包维护!
jiripudil

1.0.1 2023-05-05 06:57 UTC

This package is auto-updated.

Last update: 2024-09-09 19:24:15 UTC


README

OTP是一个库,用于生成和验证符合HOTP(RFC 4226)和TOTP(RFC 6238)算法的一次性密码。这些一次性密码通常在用户认证过程中作为第二因素使用。简而言之,您的应用程序和用户的OTP应用程序都能根据共享密钥生成一个数字。每次需要验证用户时,都要求他们输入由其应用程序生成的代码,并使用此库进行验证。

安装和需求

$ composer require jiripudil/otp

OTP需要PHP >= 8.1。

用法

此库的入口点是JiriPudil\OTP\OTP

$otp = new JiriPudil\OTP\OTP('My Application', $otpType);

它期望您提供以下内容:

  1. 发行者名称$issuer这通常由最终用户应用程序用于区分各种服务的OTP,因此应清楚地识别您的应用程序。
  2. OTP类型$otp此库提供了HOTP和TOTP的实现。以下为详细信息。
  3. 哈希算法$algorithm这是可选的,默认为SHA-1。⚠请注意,某些最终用户实现(如Google Authenticator)仅支持此算法。

基于时间的OTP

JiriPudil\OTP\TimeBasedOtp类实现了RFC 6238中的TOTP,可能是最常用的一种OTP类型。它在固定的30秒间隔内生成代码。⚠虽然间隔可以更改,但某些最终用户实现不支持默认以外的间隔。

使用基于时间的OTP需要您的服务器和用户的应用程序中的时钟同步。为了弥补时间之间的可能差异,您可以提供一个可选的$tolerance,它确定在当前时间之前和之后应该考虑多少个时间段为有效。默认为1,这意味着上一个和下一个周期的代码将通过验证。

$otpType = new JiriPudil\OTP\TimeBasedOTP();

基于HMAC的OTP

JiriPudil\OTP\HmacBasedOTP类实现了RFC 4226中的HOTP。这种OTP类型不依赖于时间,而是依赖于用户的应用程序和服务器都保留的计数器。因此,该类需要您提供一个JiriPudil\OTP\HmacBasedOTP\CounterRepository的实现,该实现检索和更新给定$account的计数器。您的实现应在某些持久存储(如关系数据库)上操作。

用户的计数器每次请求新代码时都会增加,而服务器的计数器仅在验证尝试成功后才会增加。为了解决计数器值可能的去同步,您可以配置一个$lookAhead参数,告诉此库检查几个后续的计数器值。此参数默认为3。

$otpType = new JiriPudil\OTP\HmacBasedOTP($myCounterRepository);

设置双因素认证

首先,您需要生成一个随机密钥。OTP类提供了一个方法来确保密钥足够长,以符合配置的哈希算法

$secret = $otp->generateSecret();

密钥必须是用户的唯一密钥,因此应该存储在其他用户数据的地方,最好加密,因为它是一个非常敏感的值

$myUserRepository->encryptAndSaveOtpSecret($myUser, $secret);

与用户相关联的第二个值是账户名称:这应该是一个唯一标识应用程序中用户的值,例如他们的电子邮件地址。这两条信息以 JiriPudil\OTP\Account\AccountDescriptor 的形式暴露给此 OTP 库。在 JiriPudil\OTP\Account\SimpleAccountDescriptor 中有一个方便的简单实现,对于大多数情况应该足够使用。

$account = new SimpleAccountDescriptor($myUser->getEmailAddress(), $myUser->getOtpSecret());

然后,您可以调用 getProvisioningUri 来获取用于设置最终用户 OTP 应用程序的 URI。您可以可选地指定生成的代码应该有多少位数字 - 这可以是一个介于 6 和 8 之间的值,默认值为 6,这是最常用的值。

$uri = $otp->getProvisioningUri($account, digits: 6);

此 URI 通常以二维码的形式显示,用户应用程序可以扫描。或者,您可以直接显示 Base32 编码的秘密,以便用户输入或复制到他们的应用程序中。

$encodedSecret = $account->getSecret()->asBase32();

验证

一旦用户在其 OTP 应用程序中设置了账户,您可以要求他们输入代码,并轻松验证设置是否正确。

if ($otp->verify($account, $enteredCode, expectedDigits: 6)) {
	// successfully verified
} else {
	// incorrect code
}

在此阶段,您应生成并显示一组恢复代码,以防用户无法访问其 OTP 应用程序。之后,您可以认为用户在您的应用程序中的 OTP 设置已完成,并可以开始在他们认证期间要求代码作为第二因素。

客户端使用

此包也可以用作 OTP 客户端。

$code = $otp->generate($account, digits: 6);