mober/rememberme

安全的“记住我”功能

v5.0.4 2024-03-21 21:34 UTC

README

此库实现了在网站上实现安全“记住我”功能的最佳实践。登录信息和唯一的安全令牌存储在cookie中。如果用户访问网站,cookie中的登录信息将与服务器上存储的信息进行比较。如果令牌匹配,则用户将被登录。用户可以在多个计算机/浏览器上拥有登录cookie。

升级到版本 5.x

从v5.0.0开始,此库使用declare(strict_types=1);,仅与PHP 8.0及以上版本兼容,并带有更改后的默认值

  • 使用SHA-2(256)而不是SHA-1
  • 令牌使用URL安全的base64编码(而不是十六进制)
  • 令牌分隔符现在是URL安全的(credential现在可能包含令牌分隔符)
  • 令牌长度从16字节增加到32字节

虽然可以配置哈希算法、令牌长度和令牌编码,但分隔符不可配置。新的默认值提高了安全性,同时减少了cookie的大小。遗憾的是,这使得v5.0.0+与旧版本不兼容。您应该清除令牌存储,因为所有旧令牌都将被检测为无效。

新选项:配置令牌轮换

Authenticator类有一个新的setter setTokenRotationEnabled()(默认:true)。如名称所示,这将启用或禁用令牌轮换。这是由于令牌轮换问题而实现的

  • 并发请求将提交相同的Triplet,但只有一个将被接受。其他请求将触发操作检测,因为它具有正确的永久令牌但无效的一次性令牌
  • 如果页面加载被用户取消或由于网络问题,浏览器可能无法接收到新的Triplet。在下一次访问时,它尝试提交旧的cookie,再次具有正确的永久但无效的一次性令牌,并触发操作检测。

这可能导致用户似乎在所有设备上“随机注销”。使用此选项,您可以决定是否希望获得最大的安全性或更好的用户体验。请注意,如果(太多)假阳性,用户也可能开始忽略“您的cookie被盗”警告。

安装

对于PHP 8.0或更高版本,将安装版本5+。对于PHP 7.4及更早版本,将安装不再维护的版本4。

composer require mober/rememberme

使用示例

请参阅example目录中的示例。您可以使用以下命令在本地机器上运行它:

php -S 127.0.0.1:8085 -t example

要了解基本的应用程序结构,请查看index.phpuser_is_looged_in.php模板。

该示例使用文件系统在服务器端存储令牌。在大多数情况下,最好将存储与PDOStorage类交换。

数据库配置

为了使用数据库作为存储,您需要提供PDOStorage的实例

$storage = new PDOStorage([
    'TableName'             => 'tokens',
    'CredentialColumn'      => 'credential',
    'TokenColumn'           => 'token',
    'PersistentTokenColumn' => 'persistent_token',
    'ExpiresColumn'         => 'expires',
    'Connection'            => /* supply your instance of PDO here */,
]);

$auth = new Authenticator(storage: $storage);

请参阅resources/sql目录,了解创建模式示例。

cookie配置

默认情况下,cookie的有效期为一周,且适用于设置它的域名下的所有路径。它无法通过JavaScript访问/修改,并将通过HTTP连接传输。如果您的应用程序需要不同的配置(例如,如果您正在使用HTTPS并希望通过仅允许通过安全连接传输cookie来增强安全性),您可以创建自己的PHPCookie实例

$expire = strtotime('1 week', 0);

$cookie = new PHPCookie(
    name: 'REMEMBERME',
    expireTime: $expire,
    path: '/',
    domain: 'example.org',
    secure: true,
    httpOnly: true,
    sameSite: 'Lax',
);

$auth = new Authenticator(cookie: $cookie);
$auth->setExpireTime($cookie->getExpireTime());

令牌安全性

此库默认使用random_bytes函数生成32字节的令牌。这对大多数应用程序来说应该足够安全。

如果您需要更高的安全性,请使用自定义令牌生成器实例化Authenticator类。以下示例生成64字节Base64编码的令牌

$tokenGenerator = new DefaultToken(64, DefaultToken::FORMAT_BASE64);
$auth = new Authenticator($storage, $tokenGenerator);

如果您想对随机令牌的生成有更多控制,请查看RandomLib。Rememberme有一个可以使用的RandomLibToken类。

清理过期令牌

从您的存储(文件系统或数据库)中清理过期令牌的最佳方法是编写一个小脚本,该脚本初始化您的令牌存储类并调用其cleanExpiredTokens方法。定期使用cron作业或其他工作方法运行此脚本。

如果您不能定期运行清理脚本,并且网站流量较低,您可以在每次页面调用时通过以下方式初始化Authenticator类来清理存储

 $auth = new Authenticator($storage);
 $auth->setCleanExpiredTokensOnLogin(true);

工作原理

此库受到了Barry Jaspan的文章《Improved Persistent Login Cookie Best Practice》的极大启发。该库可以防止以下攻击场景

  • 用户的计算机被盗或被破坏,攻击者可以使用现有的"Remember Me" cookie登录。用户知道这种情况发生了。用户可以远程使所有登录cookie失效。
  • 攻击者已经获取了"Remember Me" cookie并使用它登录。用户不知道这一点。下次他尝试使用被盗的cookie登录时,他会收到警告,并且所有登录cookie都会失效。
  • 攻击者已从服务器获取登录令牌数据库。存储的令牌被哈希处理,因此他必须付出计算努力(彩虹表或暴力破解)才能使用它们。
  • 攻击者尝试通过系统性地生成"Remember Me" cookie来进行暴力破解登录。默认安全设置下,每秒100次尝试(一个非常高的数字,可能会出现在服务器日志中),需要8个月的时间才能有50%的机会猜对一个cookie的值。