kunststube / csrfp
用于跨站请求伪造保护的签名令牌生成器。
Requires
- php: >=5.3.0
This package is not auto-updated.
Last update: 2024-09-24 10:41:47 UTC
README
这个库是一个简单的签名生成器,用于防止跨站请求伪造,使用签名令牌。它不需要在服务器端存储有效的令牌,因此是无状态的。
上下文
通过在每个表单中包含一个令牌,可以绕过跨站请求伪造,该令牌对攻击者难以复制。收到表单提交后,会检查令牌的有效性,并根据令牌的有效性判断提交的数据是有效还是无效。
一种实现这个想法的方法是生成一个随机值,将其存储在用户会话中以及在表单的隐藏字段中,然后在表单提交时检查提交的值是否与会话中存储的值相同。这种方法的一个缺点是需要服务器端状态和存储空间。当想要允许用户同时打开多个表单/标签页时,这种方法也会变得更加复杂,可能允许同时存在多个有效的令牌。
Kunststube\CSRFP 库使用签名方法。使用秘密对随机生成的令牌进行签名,该秘密在服务器上静态存储。随机令牌及其签名版本一起嵌入到表单中作为签名。在收到表单提交后,再次从提交的令牌和已知的秘密中生成签名,并将其与提交的签名进行比较。如果生成签名的实体知道秘密,那么签名应该是有效的,这证明了签名令牌最初来自服务器本身。
简单用法
<?php require_once 'CSRFP/SignatureGenerator.php'; $secret = '948thksehbf23fnoug2p4g2o...'; // well chosen secret $signer = new Kunststube\CSRFP\SignatureGenerator($secret); if ($_POST) { if (!$signer->validateSignature($_POST['_token'])) { header('HTTP/1.0 400 Bad Request'); exit; } } ?> <form action="" method="post"> <?php printf('<input type="hidden" name="_token" value="%s">', htmlspecialchars($signer->getSignature())); ?> ... <input type="submit" value="Submit"> </form>
每次实例化 SignatureGenerator
需要使用相同的秘密。要生成签名令牌,只需调用 SignatureGenerator::getSignature
并将值嵌入到隐藏表单字段中。在表单提交后,使用 SignatureGenerator::validateSignature
验证此令牌。
时间限制的有效性
签名包含生成时的时间戳。这可以用来在一段时间后使其过期。时间戳是签名生成过程的一部分,不能被修改。默认情况下,签名在几小时后过期(有关默认值,请参阅 SignatureGenerator::$validityWindow
)。您可以使用 SignatureGenerator::setValidityWindow
设置自己的有效期。
$signer->setValidityWindow(time() - 3600); $signer->setValidityWindow('-1 hour'); $signer->setValidityWindow(new DateTime('-1 hour'));
该方法接受一个整数 UNIX 时间戳、一个将由 strtotime
评估的字符串或一个 DateTime
实例。任何早于设置时间戳的签名都将被视为已过期。默认超时应该是一个合理的值,确保签名最终会过期,同时不会让慢速用户感到沮丧。根据您的需求进行调整,使其更加严格或更加宽松。
添加数据
签名可以用于防止表单字段注入,或者可以与特定用户相关联。可以使用 SignatureGenerator::addValue
和 SignatureGenerator::addKeyValue
将数据添加到签名生成过程中。
$signer->addValue('foo'); $signer->addKeyValue('bar', 'baz');
签名仅在生成令牌时添加了相同数据且在验证时也添加了相同数据时才有效。为了防止表单字段注入,您应该使用SignatureGenerator::addValue
添加您预期在提交的表单中接收的所有<input>
元素的名称。您想与签名关联的任何其他数据,如用户ID,应使用SignatureGenerator::addKeyValue
添加。
例如,在生成令牌时:
<?php $signer = new Kunststube\CSRFP\SignatureGenerator($secret); // including user id in signature // 'userid' is an arbitrarily chosen key name $signer->addKeyValue('userid', $_SESSION['User']['id']); // including names of valid form fields in signature $signer->addValue('_token'); $signer->addValue('firstname'); $signer->addValue('lastname'); ?> <form action="" method="post"> <?php printf('<input type="hidden" name="_token" value="%s">', htmlspecialchars($signer->getSignature())); ?> <input type="text" name="firstname"> <input type="text" name="lastname"> <input type="submit" value="Submit"> </form>
在验证令牌时,使用提交的表单字段作为验证的一部分。
$signer = new Kunststube\CSRFP\SignatureGenerator($secret); // including user id in signature validation $signer->addKeyValue('userid', $_SESSION['User']['id']); // including submitted form fields in signature validation foreach (array_keys($_POST) as $key) { $signer->addValue($key); } if (!$signer->validateSignature($_POST['_token'])) { // error }
这样,如果任何不属于原始签名的字段与表单一起提交,它将无法验证。如果您正在使用JavaScript动态添加表单字段,请务必小心。
注意
添加表单字段的缺点是在生成签名和验证签名时需要添加相同的表单字段。这需要保持预期和实际表单字段的列表同步,如果不妥善处理,很容易导致代码重复。为了获得最佳效果,我建议将此库作为更大的表单生成函数/类/库的一部分使用,该库可以处理此功能。
签名格式
生成的签名具有以下格式
1352582467:PdyfgHNZt...1Uqg==:94KNdWzg4...7iwHw==
timestamp | random token | signed token
随机令牌和签名令牌是base64编码的数据。默认情况下,整个签名长度约为188字节。
默认签名格式如下,简化版
timestamp + ":" + token + ":" + signed token
其中
timestamp = unsigned integer
token = base64 encoded random value
signed token = base64 encoded hash
hash = HMAC_SHA512(timestamp + token + data, secret)
data = all added values
data
已排序,因此添加值的顺序无关紧要。上述描述省略了有关数据用于哈希的确切格式的技术细节,请参阅源代码。
加密提供者
可以在实例化SignatureGenerator
时作为构造函数的第二个参数传递一个替代的CryptoProvider
,该提供者提供随机数源和哈希算法。请参阅ICryptoProvider.php
和CryptoProvider.php
。
PSR-0
该存储库组织方式使其内容可以倒入到文件夹Kunststube/CSRFP/
中,并且命名符合PSR-0规范。
警告
仅在您已审查并信任其前提和实现,或者您信任社区已审查并认为它是安全的时,才应使用安全相关软件。虽然作者相当自信该软件按上述描述工作,但尚未经过同行的批判性审查。作者不对软件的功能做出任何承诺或保证。
信息
版本:0.1(首次发布)
作者:David Zentgraf
联系方式:csrfp@kunststube.net
许可证:公有领域