ayesh/stateless-csrf

基于密钥的PHP 8无状态CSRF令牌生成器和验证器。无状态意味着您不需要在会话或数据库中存储CSRF令牌。

v1.4.1 2024-03-15 06:09 UTC

This package is auto-updated.

Last update: 2024-09-15 07:10:40 UTC


README

Packagist license CI

ayesh/stateless-csrf 是一个PHP库,用于生成和验证无状态CSRF保护令牌。这意味着生成的令牌不会存储在服务器上的数据库或磁盘上。相反,使用只有服务器知道的密钥和用于识别浏览器的线索的组合。

因为我们没有存储生成的令牌,这个库提供对重放攻击的保护。

要求

  • PHP 7.4或更高版本。
  • PHP内置扩展:Hash和JSON(除非PHP手动编译且不包含这些扩展,否则默认可用)。

功能

  • 使用SHA-256 HMAC生成安全令牌。
  • 可选地设置令牌的过期时间。
  • 生成的令牌是URL安全的。
  • 优化以与控制反转容器一起使用。
  • 令牌验证是时间攻击安全的。

安装

在您的终端中复制粘贴以下内容

composer require ayesh/stateless-csrf

示例

不带变量的简单示例

在库可以生成令牌之前,必须用密钥喂食。这个密钥可以是任何长度的字符串,并在HMAC操作中用作密钥。

<?php 

use Ayesh\StatelessCSRF\StatelessCSRF;

$csrf_generator = new StatelessCSRF('your-secret-key-here');
$token = $csrf_generator->getToken('unique-id-for-key');

$csrf_generator->validate('unique-id-for-key', $token);

上面是最简单的示例。首先,我们使用密钥(your-secret-key-here)初始化CSRF令牌生成器。任何使用相同密钥实例化的StatelessCSRF都将能够验证其他生成的令牌。

在理想的使用情况下,您不会做这样的事情。这个库旨在与IoC容器一起使用。生成单个StatelessCSRF实例,并使用它生成所需的令牌数量。在后续请求中,新的(使用相同密钥生成的)StatelessCSRF实例将能够验证它们。有关更详细示例,请参阅README底部的示例。

带令牌过期的示例。

因为这个库不提供重放攻击保护,所以为令牌设置过期时间更有意义。令牌的过期时间在生成时提供。在getToken方法中,将第二个参数设置为令牌应过期的UNIX时间戳。

<?php 

use Ayesh\StatelessCSRF\StatelessCSRF;

$csrf_generator = new StatelessCSRF('your-secret-key-here');
$token = $csrf_generator->getToken('unique-id-for-key', time() + 3600); // Expires in an hour.

$csrf_generator->validate('unique-id-for-key', $token);

validate()中,您可以提供当前时间作为第三个参数。如果不提供,令牌过期时间戳将与当前系统时间(time()返回的时间)进行比较。

过期时间已签名,因此攻击者无法更改时间戳并绕过过期。

带有用户代理、IP地址等验证的丰富示例。

虽然基于密钥的令牌与过期时间结合提供了良好的保护,但您可以通过用户代理字符串和IP地址验证使事情更加严格。对于这个库,只需要在生成和验证阶段提供相同的“粘合”值。

您可以使用任何唯一标识用户的价值。用户代理字符串和对方IP地址是两个很好的例子。

<?php 

use Ayesh\StatelessCSRF\StatelessCSRF;

$csrf_generator = new StatelessCSRF('your-secret-key-here');
$csrf_generator->setGlueData('ip', $_SERVER['REMOTE_ADDR']);
$csrf_generator->setGlueData('user-agent', $_SERVER['HTTP_USER_AGENT']);

$token = $csrf_generator->getToken('unique-id-for-key', time() + 3600); // Expires in an hour.

$csrf_generator->validate('unique-id-for-key', $token);

在上面的片段中,ipuser-agent是任意值。您可以添加任意数量的粘合值。在相同键上调用两次setGlueData将覆盖旧值。

在验证器实例中,必须设置相同的粘合值集合,并按相同的顺序设置。我故意省略了粘合值的排序,以鼓励调用者使用某种容器来获取 StatelessCSRF 实例,而不是每次需要验证令牌时都创建一个。

使用 Slim PHP 框架的示例

use Ayesh\StatelessCSRF\StatelessCSRF;

$container['csrf'] = static function (Container $container): StatelessCSRF {
	$settings = $container->get('settings');
	$csrf = new StatelessCSRF($settings['secret_key']);
	$request = $container->get('request');
	$csrf->setGlueData('user_agent', $request->getHeaderLine('user-agent'));

	$server = $request->getServerParams();
	$csrf->setGlueData('ip', $server['REMOTE_ADDR']);

	return $csrf;
};

现在,从容器中,你可以通过 $container['csrf'] 获取 StatelessCSRF 实例,并且它将准备好使用 getTokenvalidate() 调用。

贡献

欢迎贡献。请随意打开一个问题,或发送 PR。对于 PR,我也很欣赏适当的测试覆盖率。