ayesh / stateless-csrf
基于密钥的PHP 8无状态CSRF令牌生成器和验证器。无状态意味着您不需要在会话或数据库中存储CSRF令牌。
Requires
- php: ^8.3
- ext-json: *
Requires (Dev)
- phpunit/phpunit: ^10.5.5
README
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);
在上面的片段中,ip
和user-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
实例,并且它将准备好使用 getToken
和 validate()
调用。
贡献
欢迎贡献。请随意打开一个问题,或发送 PR。对于 PR,我也很欣赏适当的测试覆盖率。