dsentker / url-signature
一个安全的URI生成和验证库
Requires
- php: ^7.2 || ^8.0
- league/uri-parser: ^1.4
- league/uri-query-parser: ^1.0
Requires (Dev)
- phpstan/phpstan: ^0.12.2
- phpunit/phpunit: ^8.0
README
通过签名创建URL以防止修改
这个小型PHP >7.2.0库允许开发者使用哈希创建URL,以防止URL部分的修改。
为什么?
渗透测试人员和实际攻击者常用的常见攻击方法是捕获包含"id"值的URL(如/user/view?id=1234
),并手动更改此值以尝试绕过授权检查。虽然当URL被调用时,应用程序应该始终进行某种类型的授权检查,但还有另一个步骤可以帮助防止URL更改:签名值。
此签名值是使用当前URL的内容以及应用程序独有的"秘密"值构建的。然后将此签名附加到URL上,并可以直接在链接中使用。当URL被使用并且收到请求时,签名将与其他URL值进行比较。如果没有匹配,检查将失败。
安装
通过Composer安装很简单
composer require dsentker/url-signature
如果您不喜欢composer,您可以下载此存储库并使用任何PSR-4-Autoloader来加载所有内容。
还有一个Symfony捆绑包可用
composer require dsentker/url-signature-bundle
用法
要签名(或验证)URL,需要一个密钥(当然是保密的)。该密钥用于对URL的特殊部分*进行哈希处理,并将它们作为签名附加到URL的查询字符串中。
*) 您可以自行决定哪些URL部分应该被哈希处理。
稍后,在验证过程中,使用相同的密钥对当前URL进行哈希处理。然后将此哈希值与查询字符串中的哈希值进行比较。
签名URL
<?php require_once 'vendor/autoload.php'; use UrlSignature\Builder; use UrlSignature\HashConfiguration; // Secret is loaded from a configuration outside of the library $configuration = new HashConfiguration($_ENV['SECRET']); $builder = new Builder($configuration); $url = $builder->signUrl('https://example.com/foo?bar=42'); // http://example.com?foo?bar=42&_signature=90b7ac1...
在这个示例中,我们创建了一个新的配置对象,并使用它来根据提供的数据和URL创建新的Builder
实例。结果中的$url
包含签名(哈希)值,并将其附加到查询字符串中。
验证URL
等式的另一半是URL的验证。库提供了一个Validator
类来帮助完成这项工作。验证器必须使用与构建器相同的配置进行初始化。否则将计算不同的哈希值。
<?php use UrlSignature\Validator; $validator = new Validator($configuration); // Use the same $configuration here var_dump($validator->isValid('https://example.com/foo?bar=42&_signature=90b7ac1...')); // returns true or false, depending on the signature // If you want to catch Exceptions to determine the cause of an invalid URL, use Validator::verify() instead $validator->verify('http://example.com?foo=this+is+a+test&_signature=90b7ac1...'); // Returns true or a \UrlSignature\Exception\ValidationException.
Validator::isValid($url)
返回一个基于验证结果的布尔值,没有其他内容。Validator::verify($url)
如果URL签名(或过期参数)无效,将抛出一些异常
- ValidationException
- SignatureNotFoundException(如果不在查询字符串中)
- SignatureInvalidException(如果存在,但为空或无效)
- SignatureExpiredException(如果过期参数已过期,如果签名中未包含过期参数,则不会抛出异常)
过期URL
库还提供了创建将在验证失败时过期的URL的功能。要使用此功能,只需在调用signUrl()
方法时传递第二个参数。此值可以是相对字符串(由PHP的strtotime解析)或\DateTime对象
<?php $builder = new Builder($configuration); $url = $builder->signUrl('http://example.com/foo?bar=42', '+10 minutes'); // https://example.com?foo=bar&_expires=1521661473&_signature=009e2d70...
您会注意到新添加了一个URL参数,即_expires
值。当调用validate
时,此值会自动读取以确保URL没有超时。如果已超时,即使其他数据正确,结果也将为false
。
即使攻击者试图更新_expires
日期以延长URL长度,验证也会失败,因为那不是它最初与之一同散列的_expires
值。
高级配置
重命名查询参数
可以通过配置对象修改URL查询键“_expires”和“_signature”
<?php $querySignatureName = '_hash'; $queryExpiresName = 'ttl'; $configuration = new \UrlSignature\HashConfiguration('my-secret-key', $querySignatureName, $queryExpiresName); $hashedUrl = (new Builder($configuration))->signUrl('https://example.com/?id=1234', new \DateTime('MONDAY NEXT WEEK')); var_dump($hashedUrl); // https://example.com/?id=1234&ttl=123456789&_hash=009e2d70...
控制要签名的URL部分
默认情况下,以下URL部分被认为用于哈希生成
- 主机(example.com)
- 路径(/foo/bar)
- 查询字符串(?qux=baz)
配置允许使用位掩码常量修改这些组件。使用HashConfiguration::setHashMask()
传递标志(使用竖线|连接标志)。
例如,考虑将URL方案添加到哈希过程中。这意味着只有当协议(https <-> http)更改时,URL的验证才会失败。
<?php use UrlSignature\HashConfiguration; $config = new HashConfiguration('secret'); // Complete example: Use *ALL* parts of the URL for hashing $config->setHashMask( HashConfiguration::FLAG_HASH_SCHEME | HashConfiguration::FLAG_HASH_HOST | HashConfiguration::FLAG_HASH_PORT | HashConfiguration::FLAG_HASH_PATH | HashConfiguration::FLAG_HASH_QUERY | HashConfiguration::FLAG_HASH_FRAGMENT );
限制和安全注意事项
此库从一个或多个URL部分创建哈希。这意味着,只有当给定的签名与当前签名匹配时,URL才是有效的。像任何其他哈希事件(例如,哈希密码)一样,这被认为非常安全。为了使签名的URL真正安全,开发者必须确保(在接收请求时)检查是否需要哈希检查以及/或提交的签名是否正确。换句话说,如果用户能够获取不带签名的URL,则最佳哈希算法将不起作用。
不要在以下情况下使用此库...
- ...您不确定请求处理部分(例如,控制器)是否可以随时检查哈希。
- ...您的目标是防止将URL分发到未经授权的人员(这不是此库的目的!)
- ...此库是唯一旨在防止用户检索他们不应获取的内容的审计机制。
- ...您想验证包含查询字符串中数组的哈希URI哈希(请参阅问题 #3)
致谢
基于psecio的想法,该项目由我分叉以提高PHP 7.x应用程序的代码。这些调整导致了一个独立的库(这个)和一个symfony捆绑包。
依赖项
该库使用thephpleague/uri的URL函数来解析、提取和(重新)构建URL组件。对于单元测试,使用PhpUnit。
期待您的帮助!
如果您发现错误,有任何一般性问题或想实现一个功能,欢迎合作。
路线图
- 停止支持PHP 7.2
- 重构哈希过程,为问题 #3 提供解决方案
提交错误和功能请求
错误和功能请求在GitHub上跟踪。
Symfony 4/5 捆绑包
我还创建了一个用于Symfony的捆绑包(具有twig支持和注解)。
测试
./vendor/bin/phpunit
测试过程中可能会出现两秒钟的短暂中断。内置了sleep(2)
来测试超时功能的验证。
另请参阅
- URL Signature Bundle for Symfony 4
- ivanakimov/hashids A PHP Version from hashids.org
- HashidsBundle The relating Symfony Bundle from roukmoute