snicco / signed-url
一个小型、框架无关的库,用于创建和验证signed-url。
Requires
- php: ^7.4|^8.0
- ext-hash: *
- paragonie/constant_time_encoding: ^2.4
- snicco/testable-clock: ^2.0
- webmozart/assert: ^1.10
Requires (Dev)
- phpunit/phpunit: ^9.5.13
- snicco/signed-url-testing: ^2.0
Conflicts
- snicco/better-wp-api: <2.0.0-beta.9
- snicco/better-wp-cache: <2.0.0-beta.9
- snicco/better-wp-cache-bundle: <2.0.0-beta.9
- snicco/better-wp-cli: <2.0.0-beta.9
- snicco/better-wp-cli-testing: <2.0.0-beta.9
- snicco/better-wp-hooks: <2.0.0-beta.9
- snicco/better-wp-hooks-bundle: <2.0.0-beta.9
- snicco/better-wp-mail: <2.0.0-beta.9
- snicco/better-wp-mail-bundle: <2.0.0-beta.9
- snicco/better-wp-mail-testing: <2.0.0-beta.9
- snicco/better-wpdb: <2.0.0-beta.9
- snicco/better-wpdb-bundle: <2.0.0-beta.9
- snicco/blade-bridge: <2.0.0-beta.9
- snicco/blade-bundle: <2.0.0-beta.9
- snicco/content-negotiation-middleware: <2.0.0-beta.9
- snicco/debug-bundle: <2.0.0-beta.9
- snicco/default-headers-middleware: <2.0.0-beta.9
- snicco/eloquent: <2.0.0-beta.9
- snicco/encryption-bundle: <2.0.0-beta.9
- snicco/event-dispatcher: <2.0.0-beta.9
- snicco/event-dispatcher-testing: <2.0.0-beta.9
- snicco/guests-only-middleware: <1.0.0
- snicco/http-routing: <2.0.0-beta.9
- snicco/http-routing-bundle: <2.0.0-beta.9
- snicco/http-routing-testing: <2.0.0-beta.9
- snicco/https-only-middleware: <2.0.0-beta.9
- snicco/illuminate-container-bridge: <2.0.0-beta.9
- snicco/kernel: <2.0.0-beta.9
- snicco/kernel-testing: <2.0.0-beta.9
- snicco/method-override-middleware: <2.0.0-beta.9
- snicco/minimal-logger: <2.0.0-beta.9
- snicco/must-match-route-middleware: <2.0.0-beta.9
- snicco/no-robots-middleware: <2.0.0-beta.9
- snicco/open-redirect-protection-middleware: <2.0.0-beta.9
- snicco/payload-middleware: <2.0.0-beta.9
- snicco/pimple-bridge: <2.0.0-beta.9
- snicco/psr7-error-handler: <2.0.0-beta.9
- snicco/redirect-middleware: <2.0.0-beta.9
- snicco/session: <2.0.0-beta.9
- snicco/session-bundle: <2.0.0-beta.9
- snicco/session-psr16-bridge: <2.0.0-beta.9
- snicco/session-testing: <2.0.0-beta.9
- snicco/session-wp-bridge: <2.0.0-beta.9
- snicco/share-cookies-middleware: <2.0.0-beta.9
- snicco/signed-url-psr15-bridge: <2.0.0-beta.9
- snicco/signed-url-psr16-bridge: <2.0.0-beta.9
- snicco/signed-url-testing: <2.0.0-beta.9
- snicco/signed-url-wp-bridge: <2.0.0-beta.9
- snicco/str-arr: <2.0.0-beta.9
- snicco/templating: <2.0.0-beta.9
- snicco/templating-bundle: <2.0.0-beta.9
- snicco/testing-bundle: <2.0.0-beta.9
- snicco/trailing-slash-middleware: <2.0.0-beta.9
- snicco/wp-auth-only-middleware: <2.0.0-beta.9
- snicco/wp-capability-middleware: <2.0.0-beta.9
- snicco/wp-capapility-middleware: <1.0.0
- snicco/wp-guests-only-middleware: <2.0.0-beta.9
- snicco/wp-nonce-middleware: <2.0.0-beta.9
- dev-master
- v2.0.0-beta.9
- v2.0.0-beta.8
- v2.0.0-beta.7
- v2.0.0-beta.6
- v2.0.0-beta.5
- v2.0.0-beta.4
- v2.0.0-beta.3
- v2.0.0-beta.2
- v2.0.0-beta.1
- v1.10.1
- v1.10.0
- v1.9.1
- v1.9.0
- v1.8.1
- v1.8.0
- v1.7.0
- v1.6.2
- v1.6.1
- v1.6.0
- v1.5.0
- v1.4.2
- v1.4.1
- v1.4.0
- v1.3.0
- v1.2.1
- v1.2.0
- v1.1.3
- v1.1.2
- v1.1.1
- v1.1.0
- v1.0.2
- v1.0.1
- v1.0.0
- dev-beta
This package is auto-updated.
Last update: 2024-09-07 14:27:57 UTC
README
目录
动机
在开发Snicco项目时,我们没有找到任何好的独立PHP库用于签名url。我们需要在几个地方使用此功能,因此我们决定自己实现。
功能
- 使用强随机秘密,由CSPRNG和安全的哈希函数生成。
- 验证签名、过期时间和每个url的强制使用限制。
- PSR-7/15兼容。没有PHP超级全局变量的隐藏依赖。
- 防止基于时间的侧信道攻击。
- 在最大使用次数后永久使signed-url无效。(旋转您的秘密将使所有signed-url无效)
- 具有防御性编程,使得错误使用非常困难。
- 支持多种存储后端。
- 经过适当测试且直观的API。
虽然术语signed-url
在技术上不正确(此包使用HMACs,而不是非对称签名),但我们选择坚持Symfony和Laravel的命名方式。
安装
composer require snicco/signed-url
使用
创建一个秘密
从您的项目根目录运行以下命令,并将生成的秘密存储在安全位置,该位置位于您的web根目录之外。
vendor/bin/generate-signed-url-secret
这将输出一个类似以下的随机十六进制编码的秘密:32|1e21be67f2279e485c7c5e8291d05edda7e76ffb01ddb8eb290ce826528ad2ff
此秘密绝对不应该存储在版本控制中。
在您的应用程序中,使用类似symfony/dotenv
的方式从您的应用程序中加载秘密。
// require 'vendor/autoload.php'; $secret = \Snicco\Component\SignedUrl\Secret::fromHexEncoded(getenv('SIGNED_URL_SECRET'));
创建一个signed-url
$secret = /* */ $hmac = new Snicco\Component\SignedUrl\HMAC($secret, 'sha256') // This is a simple interface. // Use one of the inbuilt storages in the #storages section or provide your own. $storage = /* */ $signer = new \Snicco\Component\SignedUrl\UrlSigner($storage, $hmac); // The maximum lifetime in seconds that this link should be valid for. $lifetime_in_sec = 60; // The maximum amount that this link should be valid for. // After each successfully validation this amount will be decreased by 1. $usage_limit = 1; // optional: adding request context that must be the same in order to // successfully validate a signed-url. $context = ($_SERVER['REMOTE_ADDR'] ?? '') . ($_SERVER['HTTP_USER_AGENT'] ?? ''); $signed_url = $signer->sign('https://example.com/unsubscribe?user_id=12' , $lifetime_in_sec, $usage_limit, $context); $mailer = /* */ $href = $signed_url->asString(); // $href will be something like transformers: // https://example.com/unsubscribe?user_id=12expires=1639783661&signature=Del1cGmLB1wVET6PJieCrQ==|1MTBBGIpEGPVuGaKDjjrHDBusMNoWB15Ng5lKBSSLQY= $mailer->send('user12@gmail.com', "Click <a href='{{$href}}'> here <a/> to unsubscribe.")
验证signed-url
应该在中间件中执行signed-url的验证,以避免样板代码。
下面的代码示例描述了在任何PHP应用程序中验证url的手动方式。
PSR-15中间件
如果您的框架是PSR-7/PSR-15兼容的,并且支持基于路由的中间件,您可以使用我们的PSR-15中间件桥接器,这使得此操作变得非常简单。
所有PHP应用
$storage = /* */ $hmac = /* */ // Clean expired links periodically. try { // 0-100 $percentage = 2; \Snicco\Component\SignedUrl\GarbageCollector::clean($storage, $percentage); } catch (UnavailableStorage $e) { // gc did not work for some reason. Log and continue. error_log($e->getMessage()); } $validator = new \Snicco\Component\SignedUrl\SignedUrlValidator($storage, $hmac); $target = $_SERVER['REQUEST_URI'].'?'.$_SERVER['QUERY_STRING']; try { // optional context, has to be the same scheme used at creation. $context = ($_SERVER['REMOTE_ADDR'] ?? '') . ($_SERVER['HTTP_USER_AGENT'] ?? ''); $validator->validate( $target, $context); } catch (\Snicco\Component\SignedUrl\Exception\InvalidSignature $e ) { error_log("invalid signature."); echo "This link has expired. Please request a new one." } catch (\Snicco\Component\SignedUrl\Exception\SignedUrlExpired $e ) { error_log("signed url expired."); echo "This link has expired. Please request a new one." } catch (\Snicco\Component\SignedUrl\Exception\SignedUrlUsageExceeded $e ) { error_log("signed url usage exceeded."); echo "This link has expired. Please request a new one." } // Everything is valid. // If the link can be used multiple times the usage is decremented automatically by 1. echo "You have been unsubscribed."
存储类型
Snicco\SignedUrl\Contracts\SingedUrlStorage
为每个创建的signed-url保留一个标识符,并确保您的最大使用限制得到执行。
如果没有某种形式的后端存储,signed-url在过期时间戳之前可以无限次数地有效。(如果您想这样做,可以使用NullStorage
)。
会话存储(包含)
SessionStorage
接受一个 array
或任何实现 ArrayAccess
接口的对象(通过引用传递)。
// using an array. $storage = new \Snicco\Component\SignedUrl\Storage\SessionStorage($_SESSION); // using an object implementing ArrayAccess $arr = new MyArrayAccess(); $storage = new \Snicco\Component\SignedUrl\Storage\SessionStorage($arr);
NullStorage(包含)
NullStorage
不执行任何操作。不会存储签名URL,也不会实施使用限制。仅当您的签名URL在过期前应有效多次时才使用此功能。
签名URL的有效性将仅基于正确的签名和过期时间戳。
InMemory(包含)
您可以在单元测试中使用 InMemoryStorage
。
$storage = new \Snicco\Component\SignedUrl\Storage\InMemoryStorage()
PSR16-Cache(桥接包)
我们有一个专门的 PSR-16 桥接器,它允许您使用任何 PSR-16 缓存 作为存储。
实现自己的存储
实现自己的存储非常简单。您只需实现简单的 SingedUrlStorage
接口。
使用 snicco/signed-url-testing
包 测试您的实现是否符合接口的合约。
贡献
此存储库是 Snicco 项目 开发存储库的只读分割。
报告问题和发送拉取请求
请在 Snicco monorepo 中报告问题。
安全性
如果您发现安全漏洞,请遵循我们的 披露程序。