From 2caaef8adac3bcca8f799b58ef67e6a3b7bfa6f4 Mon Sep 17 00:00:00 2001 From: Alexander Schranz Date: Tue, 23 May 2023 01:51:38 +0200 Subject: [PATCH] Move UriSigner from HttpKernel to HttpFoundation package --- UPGRADE-6.4.md | 1 + .../Extension/HttpKernelExtensionTest.php | 2 +- src/Symfony/Bridge/Twig/composer.json | 4 +- .../Resources/config/services.php | 5 +- .../Component/HttpFoundation/CHANGELOG.md | 1 + .../HttpFoundation/Tests/UriSignerTest.php | 86 ++++++++++++++ .../Component/HttpFoundation/UriSigner.php | 111 ++++++++++++++++++ src/Symfony/Component/HttpKernel/CHANGELOG.md | 1 + .../EventListener/FragmentListener.php | 2 +- .../AbstractSurrogateFragmentRenderer.php | 2 +- .../Fragment/FragmentUriGenerator.php | 2 +- .../Fragment/HIncludeFragmentRenderer.php | 2 +- .../EventListener/FragmentListenerTest.php | 2 +- .../Fragment/EsiFragmentRendererTest.php | 2 +- .../Fragment/HIncludeFragmentRendererTest.php | 2 +- .../Fragment/SsiFragmentRendererTest.php | 2 +- .../HttpKernel/Tests/UriSignerTest.php | 3 + .../Component/HttpKernel/UriSigner.php | 94 +-------------- .../Component/HttpKernel/composer.json | 2 +- 19 files changed, 225 insertions(+), 101 deletions(-) create mode 100644 src/Symfony/Component/HttpFoundation/Tests/UriSignerTest.php create mode 100644 src/Symfony/Component/HttpFoundation/UriSigner.php diff --git a/UPGRADE-6.4.md b/UPGRADE-6.4.md index a60d158f4d344..4950151722ad8 100644 --- a/UPGRADE-6.4.md +++ b/UPGRADE-6.4.md @@ -108,6 +108,7 @@ HttpKernel * [BC break] `BundleInterface` no longer extends `ContainerAwareInterface` * [BC break] Add native return types to `TraceableEventDispatcher` and to `MergeExtensionConfigurationPass` * Deprecate `Kernel::stripComments()` + * Deprecate `UriSigner`, use `UriSigner` from the HttpFoundation component instead Messenger --------- diff --git a/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php b/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php index a53e64a425390..e7f58f4f48aee 100644 --- a/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php +++ b/src/Symfony/Bridge/Twig/Tests/Extension/HttpKernelExtensionTest.php @@ -18,10 +18,10 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\UriSigner; use Symfony\Component\HttpKernel\Fragment\FragmentHandler; use Symfony\Component\HttpKernel\Fragment\FragmentRendererInterface; use Symfony\Component\HttpKernel\Fragment\FragmentUriGenerator; -use Symfony\Component\HttpKernel\UriSigner; use Twig\Environment; use Twig\Loader\ArrayLoader; use Twig\RuntimeLoader\RuntimeLoaderInterface; diff --git a/src/Symfony/Bridge/Twig/composer.json b/src/Symfony/Bridge/Twig/composer.json index 36014a590b41f..843dbedb8f635 100644 --- a/src/Symfony/Bridge/Twig/composer.json +++ b/src/Symfony/Bridge/Twig/composer.json @@ -31,7 +31,7 @@ "symfony/form": "^6.3|^7.0", "symfony/html-sanitizer": "^6.1|^7.0", "symfony/http-foundation": "^5.4|^6.0|^7.0", - "symfony/http-kernel": "^6.2|^7.0", + "symfony/http-kernel": "^6.4|^7.0", "symfony/intl": "^5.4|^6.0|^7.0", "symfony/mime": "^6.2|^7.0", "symfony/polyfill-intl-icu": "~1.0", @@ -59,7 +59,7 @@ "symfony/console": "<5.4", "symfony/form": "<6.3", "symfony/http-foundation": "<5.4", - "symfony/http-kernel": "<6.2", + "symfony/http-kernel": "<6.4", "symfony/mime": "<6.2", "symfony/serializer": "<6.2", "symfony/translation": "<5.4", diff --git a/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.php b/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.php index d7b2ad9029114..905e16f9b9e9c 100644 --- a/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.php +++ b/src/Symfony/Bundle/FrameworkBundle/Resources/config/services.php @@ -35,6 +35,7 @@ use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Session\SessionInterface; +use Symfony\Component\HttpFoundation\UriSigner; use Symfony\Component\HttpFoundation\UrlHelper; use Symfony\Component\HttpKernel\CacheClearer\ChainCacheClearer; use Symfony\Component\HttpKernel\CacheWarmer\CacheWarmerAggregate; @@ -47,7 +48,7 @@ use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\HttpKernel\KernelInterface; -use Symfony\Component\HttpKernel\UriSigner; +use Symfony\Component\HttpKernel\UriSigner as HttpKernelUriSigner; use Symfony\Component\Runtime\Runner\Symfony\HttpKernelRunner; use Symfony\Component\Runtime\Runner\Symfony\ResponseRunner; use Symfony\Component\Runtime\SymfonyRuntime; @@ -157,6 +158,8 @@ class_exists(WorkflowEvents::class) ? WorkflowEvents::ALIASES : [] param('kernel.secret'), ]) ->alias(UriSigner::class, 'uri_signer') + ->alias(HttpKernelUriSigner::class, 'uri_signer') + ->deprecate('symfony/framework-bundle', '6.4', 'The "%alias_id%" alias is deprecated, use "'.UriSigner::class.'" instead.') ->set('config_cache_factory', ResourceCheckerConfigCacheFactory::class) ->args([ diff --git a/src/Symfony/Component/HttpFoundation/CHANGELOG.md b/src/Symfony/Component/HttpFoundation/CHANGELOG.md index 04267c3e975db..603314b009d94 100644 --- a/src/Symfony/Component/HttpFoundation/CHANGELOG.md +++ b/src/Symfony/Component/HttpFoundation/CHANGELOG.md @@ -6,6 +6,7 @@ CHANGELOG * Make `HeaderBag::getDate()`, `Response::getDate()`, `getExpires()` and `getLastModified()` return a `DateTimeImmutable` * Support root-level `Generator` in `StreamedJsonResponse` + * Add `UriSigner` from the HttpKernel component 6.3 --- diff --git a/src/Symfony/Component/HttpFoundation/Tests/UriSignerTest.php b/src/Symfony/Component/HttpFoundation/Tests/UriSignerTest.php new file mode 100644 index 0000000000000..dfbe81e8827f9 --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/Tests/UriSignerTest.php @@ -0,0 +1,86 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation\Tests; + +use PHPUnit\Framework\TestCase; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\UriSigner; + +class UriSignerTest extends TestCase +{ + public function testSign() + { + $signer = new UriSigner('foobar'); + + $this->assertStringContainsString('?_hash=', $signer->sign('http://example.com/foo')); + $this->assertStringContainsString('?_hash=', $signer->sign('http://example.com/foo?foo=bar')); + $this->assertStringContainsString('&foo=', $signer->sign('http://example.com/foo?foo=bar')); + } + + public function testCheck() + { + $signer = new UriSigner('foobar'); + + $this->assertFalse($signer->check('http://example.com/foo?_hash=foo')); + $this->assertFalse($signer->check('http://example.com/foo?foo=bar&_hash=foo')); + $this->assertFalse($signer->check('http://example.com/foo?foo=bar&_hash=foo&bar=foo')); + + $this->assertTrue($signer->check($signer->sign('http://example.com/foo'))); + $this->assertTrue($signer->check($signer->sign('http://example.com/foo?foo=bar'))); + $this->assertTrue($signer->check($signer->sign('http://example.com/foo?foo=bar&0=integer'))); + + $this->assertSame($signer->sign('http://example.com/foo?foo=bar&bar=foo'), $signer->sign('http://example.com/foo?bar=foo&foo=bar')); + } + + public function testCheckWithDifferentArgSeparator() + { + $this->iniSet('arg_separator.output', '&'); + $signer = new UriSigner('foobar'); + + $this->assertSame( + 'http://example.com/foo?_hash=rIOcC%2FF3DoEGo%2FvnESjSp7uU9zA9S%2F%2BOLhxgMexoPUM%3D&baz=bay&foo=bar', + $signer->sign('http://example.com/foo?foo=bar&baz=bay') + ); + $this->assertTrue($signer->check($signer->sign('http://example.com/foo?foo=bar&baz=bay'))); + } + + public function testCheckWithRequest() + { + $signer = new UriSigner('foobar'); + + $this->assertTrue($signer->checkRequest(Request::create($signer->sign('http://example.com/foo')))); + $this->assertTrue($signer->checkRequest(Request::create($signer->sign('http://example.com/foo?foo=bar')))); + $this->assertTrue($signer->checkRequest(Request::create($signer->sign('http://example.com/foo?foo=bar&0=integer')))); + } + + public function testCheckWithDifferentParameter() + { + $signer = new UriSigner('foobar', 'qux'); + + $this->assertSame( + 'http://example.com/foo?baz=bay&foo=bar&qux=rIOcC%2FF3DoEGo%2FvnESjSp7uU9zA9S%2F%2BOLhxgMexoPUM%3D', + $signer->sign('http://example.com/foo?foo=bar&baz=bay') + ); + $this->assertTrue($signer->check($signer->sign('http://example.com/foo?foo=bar&baz=bay'))); + } + + public function testSignerWorksWithFragments() + { + $signer = new UriSigner('foobar'); + + $this->assertSame( + 'http://example.com/foo?_hash=EhpAUyEobiM3QTrKxoLOtQq5IsWyWedoXDPqIjzNj5o%3D&bar=foo&foo=bar#foobar', + $signer->sign('http://example.com/foo?bar=foo&foo=bar#foobar') + ); + $this->assertTrue($signer->check($signer->sign('http://example.com/foo?bar=foo&foo=bar#foobar'))); + } +} diff --git a/src/Symfony/Component/HttpFoundation/UriSigner.php b/src/Symfony/Component/HttpFoundation/UriSigner.php new file mode 100644 index 0000000000000..091ac03e479d4 --- /dev/null +++ b/src/Symfony/Component/HttpFoundation/UriSigner.php @@ -0,0 +1,111 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\HttpFoundation; + +/** + * Signs URIs. + * + * @author Fabien Potencier + */ +class UriSigner +{ + private string $secret; + private string $parameter; + + /** + * @param string $secret A secret + * @param string $parameter Query string parameter to use + */ + public function __construct(#[\SensitiveParameter] string $secret, string $parameter = '_hash') + { + $this->secret = $secret; + $this->parameter = $parameter; + } + + /** + * Signs a URI. + * + * The given URI is signed by adding the query string parameter + * which value depends on the URI and the secret. + */ + public function sign(string $uri): string + { + $url = parse_url($uri); + $params = []; + + if (isset($url['query'])) { + parse_str($url['query'], $params); + } + + $uri = $this->buildUrl($url, $params); + $params[$this->parameter] = $this->computeHash($uri); + + return $this->buildUrl($url, $params); + } + + /** + * Checks that a URI contains the correct hash. + */ + public function check(string $uri): bool + { + $url = parse_url($uri); + $params = []; + + if (isset($url['query'])) { + parse_str($url['query'], $params); + } + + if (empty($params[$this->parameter])) { + return false; + } + + $hash = $params[$this->parameter]; + unset($params[$this->parameter]); + + return hash_equals($this->computeHash($this->buildUrl($url, $params)), $hash); + } + + public function checkRequest(Request $request): bool + { + $qs = ($qs = $request->server->get('QUERY_STRING')) ? '?'.$qs : ''; + + // we cannot use $request->getUri() here as we want to work with the original URI (no query string reordering) + return $this->check($request->getSchemeAndHttpHost().$request->getBaseUrl().$request->getPathInfo().$qs); + } + + private function computeHash(string $uri): string + { + return base64_encode(hash_hmac('sha256', $uri, $this->secret, true)); + } + + private function buildUrl(array $url, array $params = []): string + { + ksort($params, \SORT_STRING); + $url['query'] = http_build_query($params, '', '&'); + + $scheme = isset($url['scheme']) ? $url['scheme'].'://' : ''; + $host = $url['host'] ?? ''; + $port = isset($url['port']) ? ':'.$url['port'] : ''; + $user = $url['user'] ?? ''; + $pass = isset($url['pass']) ? ':'.$url['pass'] : ''; + $pass = ($user || $pass) ? "$pass@" : ''; + $path = $url['path'] ?? ''; + $query = $url['query'] ? '?'.$url['query'] : ''; + $fragment = isset($url['fragment']) ? '#'.$url['fragment'] : ''; + + return $scheme.$user.$pass.$host.$port.$path.$query.$fragment; + } +} + +if (!class_exists(\Symfony\Component\HttpKernel\UriSigner::class, false)) { + class_alias(UriSigner::class, \Symfony\Component\HttpKernel\UriSigner::class); +} diff --git a/src/Symfony/Component/HttpKernel/CHANGELOG.md b/src/Symfony/Component/HttpKernel/CHANGELOG.md index f1a003cba8f52..fa58ba8e52fb0 100644 --- a/src/Symfony/Component/HttpKernel/CHANGELOG.md +++ b/src/Symfony/Component/HttpKernel/CHANGELOG.md @@ -13,6 +13,7 @@ CHANGELOG * Add class `DebugLoggerConfigurator` * Deprecate `Kernel::stripComments()` * Support the `!` character at the beginning of a string as a negation operator in the url filter of the profiler + * Deprecate `UriSigner`, use `UriSigner` from the HttpFoundation component instead 6.3 --- diff --git a/src/Symfony/Component/HttpKernel/EventListener/FragmentListener.php b/src/Symfony/Component/HttpKernel/EventListener/FragmentListener.php index 6392d699ff108..f267ba5817147 100644 --- a/src/Symfony/Component/HttpKernel/EventListener/FragmentListener.php +++ b/src/Symfony/Component/HttpKernel/EventListener/FragmentListener.php @@ -13,10 +13,10 @@ use Symfony\Component\EventDispatcher\EventSubscriberInterface; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\UriSigner; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\KernelEvents; -use Symfony\Component\HttpKernel\UriSigner; /** * Handles content fragments represented by special URIs. diff --git a/src/Symfony/Component/HttpKernel/Fragment/AbstractSurrogateFragmentRenderer.php b/src/Symfony/Component/HttpKernel/Fragment/AbstractSurrogateFragmentRenderer.php index 2d4f4b75de586..668be81e8c5cb 100644 --- a/src/Symfony/Component/HttpKernel/Fragment/AbstractSurrogateFragmentRenderer.php +++ b/src/Symfony/Component/HttpKernel/Fragment/AbstractSurrogateFragmentRenderer.php @@ -13,9 +13,9 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\UriSigner; use Symfony\Component\HttpKernel\Controller\ControllerReference; use Symfony\Component\HttpKernel\HttpCache\SurrogateInterface; -use Symfony\Component\HttpKernel\UriSigner; /** * Implements Surrogate rendering strategy. diff --git a/src/Symfony/Component/HttpKernel/Fragment/FragmentUriGenerator.php b/src/Symfony/Component/HttpKernel/Fragment/FragmentUriGenerator.php index 6d9a1311b746f..aeef41546e011 100644 --- a/src/Symfony/Component/HttpKernel/Fragment/FragmentUriGenerator.php +++ b/src/Symfony/Component/HttpKernel/Fragment/FragmentUriGenerator.php @@ -13,8 +13,8 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\UriSigner; use Symfony\Component\HttpKernel\Controller\ControllerReference; -use Symfony\Component\HttpKernel\UriSigner; /** * Generates a fragment URI. diff --git a/src/Symfony/Component/HttpKernel/Fragment/HIncludeFragmentRenderer.php b/src/Symfony/Component/HttpKernel/Fragment/HIncludeFragmentRenderer.php index fffb029217ad4..d5b6c4cd3c22a 100644 --- a/src/Symfony/Component/HttpKernel/Fragment/HIncludeFragmentRenderer.php +++ b/src/Symfony/Component/HttpKernel/Fragment/HIncludeFragmentRenderer.php @@ -13,8 +13,8 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\UriSigner; use Symfony\Component\HttpKernel\Controller\ControllerReference; -use Symfony\Component\HttpKernel\UriSigner; use Twig\Environment; /** diff --git a/src/Symfony/Component/HttpKernel/Tests/EventListener/FragmentListenerTest.php b/src/Symfony/Component/HttpKernel/Tests/EventListener/FragmentListenerTest.php index 77d07a39f3e6d..185267ba527fa 100644 --- a/src/Symfony/Component/HttpKernel/Tests/EventListener/FragmentListenerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/EventListener/FragmentListenerTest.php @@ -13,11 +13,11 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\UriSigner; use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\HttpKernel\EventListener\FragmentListener; use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException; use Symfony\Component\HttpKernel\HttpKernelInterface; -use Symfony\Component\HttpKernel\UriSigner; class FragmentListenerTest extends TestCase { diff --git a/src/Symfony/Component/HttpKernel/Tests/Fragment/EsiFragmentRendererTest.php b/src/Symfony/Component/HttpKernel/Tests/Fragment/EsiFragmentRendererTest.php index b90e8002267d4..fa9885d2753cd 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Fragment/EsiFragmentRendererTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Fragment/EsiFragmentRendererTest.php @@ -13,11 +13,11 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\UriSigner; use Symfony\Component\HttpKernel\Controller\ControllerReference; use Symfony\Component\HttpKernel\Fragment\EsiFragmentRenderer; use Symfony\Component\HttpKernel\Fragment\InlineFragmentRenderer; use Symfony\Component\HttpKernel\HttpCache\Esi; -use Symfony\Component\HttpKernel\UriSigner; class EsiFragmentRendererTest extends TestCase { diff --git a/src/Symfony/Component/HttpKernel/Tests/Fragment/HIncludeFragmentRendererTest.php b/src/Symfony/Component/HttpKernel/Tests/Fragment/HIncludeFragmentRendererTest.php index 2dd09aca3e0fd..f74887ade36f4 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Fragment/HIncludeFragmentRendererTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Fragment/HIncludeFragmentRendererTest.php @@ -13,9 +13,9 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\UriSigner; use Symfony\Component\HttpKernel\Controller\ControllerReference; use Symfony\Component\HttpKernel\Fragment\HIncludeFragmentRenderer; -use Symfony\Component\HttpKernel\UriSigner; use Twig\Environment; use Twig\Loader\ArrayLoader; diff --git a/src/Symfony/Component/HttpKernel/Tests/Fragment/SsiFragmentRendererTest.php b/src/Symfony/Component/HttpKernel/Tests/Fragment/SsiFragmentRendererTest.php index 55e73e2fcb245..4af00f9f75137 100644 --- a/src/Symfony/Component/HttpKernel/Tests/Fragment/SsiFragmentRendererTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/Fragment/SsiFragmentRendererTest.php @@ -13,11 +13,11 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\UriSigner; use Symfony\Component\HttpKernel\Controller\ControllerReference; use Symfony\Component\HttpKernel\Fragment\InlineFragmentRenderer; use Symfony\Component\HttpKernel\Fragment\SsiFragmentRenderer; use Symfony\Component\HttpKernel\HttpCache\Ssi; -use Symfony\Component\HttpKernel\UriSigner; class SsiFragmentRendererTest extends TestCase { diff --git a/src/Symfony/Component/HttpKernel/Tests/UriSignerTest.php b/src/Symfony/Component/HttpKernel/Tests/UriSignerTest.php index 4801776cce146..863502f61c229 100644 --- a/src/Symfony/Component/HttpKernel/Tests/UriSignerTest.php +++ b/src/Symfony/Component/HttpKernel/Tests/UriSignerTest.php @@ -15,6 +15,9 @@ use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpKernel\UriSigner; +/** + * @group legacy + */ class UriSignerTest extends TestCase { public function testSign() diff --git a/src/Symfony/Component/HttpKernel/UriSigner.php b/src/Symfony/Component/HttpKernel/UriSigner.php index dfc0a7d00bb84..e11ff6af1dc4f 100644 --- a/src/Symfony/Component/HttpKernel/UriSigner.php +++ b/src/Symfony/Component/HttpKernel/UriSigner.php @@ -11,99 +11,17 @@ namespace Symfony\Component\HttpKernel; -use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\UriSigner as HttpFoundationUriSigner; -/** - * Signs URIs. - * - * @author Fabien Potencier - */ -class UriSigner -{ - private string $secret; - private string $parameter; +trigger_deprecation('symfony/dependency-injection', '6.4', 'The "%s" class is deprecated, use "%s" instead.', UriSigner::class, HttpFoundationUriSigner::class); - /** - * @param string $secret A secret - * @param string $parameter Query string parameter to use - */ - public function __construct(#[\SensitiveParameter] string $secret, string $parameter = '_hash') - { - $this->secret = $secret; - $this->parameter = $parameter; - } +class_exists(HttpFoundationUriSigner::class); +if (false) { /** - * Signs a URI. - * - * The given URI is signed by adding the query string parameter - * which value depends on the URI and the secret. + * @deprecated since Symfony 6.4, to be removed in 7.0, use {@link HttpFoundationUriSigner} instead */ - public function sign(string $uri): string - { - $url = parse_url($uri); - $params = []; - - if (isset($url['query'])) { - parse_str($url['query'], $params); - } - - $uri = $this->buildUrl($url, $params); - $params[$this->parameter] = $this->computeHash($uri); - - return $this->buildUrl($url, $params); - } - - /** - * Checks that a URI contains the correct hash. - */ - public function check(string $uri): bool - { - $url = parse_url($uri); - $params = []; - - if (isset($url['query'])) { - parse_str($url['query'], $params); - } - - if (empty($params[$this->parameter])) { - return false; - } - - $hash = $params[$this->parameter]; - unset($params[$this->parameter]); - - return hash_equals($this->computeHash($this->buildUrl($url, $params)), $hash); - } - - public function checkRequest(Request $request): bool + class UriSigner { - $qs = ($qs = $request->server->get('QUERY_STRING')) ? '?'.$qs : ''; - - // we cannot use $request->getUri() here as we want to work with the original URI (no query string reordering) - return $this->check($request->getSchemeAndHttpHost().$request->getBaseUrl().$request->getPathInfo().$qs); - } - - private function computeHash(string $uri): string - { - return base64_encode(hash_hmac('sha256', $uri, $this->secret, true)); - } - - private function buildUrl(array $url, array $params = []): string - { - ksort($params, \SORT_STRING); - $url['query'] = http_build_query($params, '', '&'); - - $scheme = isset($url['scheme']) ? $url['scheme'].'://' : ''; - $host = $url['host'] ?? ''; - $port = isset($url['port']) ? ':'.$url['port'] : ''; - $user = $url['user'] ?? ''; - $pass = isset($url['pass']) ? ':'.$url['pass'] : ''; - $pass = ($user || $pass) ? "$pass@" : ''; - $path = $url['path'] ?? ''; - $query = $url['query'] ? '?'.$url['query'] : ''; - $fragment = isset($url['fragment']) ? '#'.$url['fragment'] : ''; - - return $scheme.$user.$pass.$host.$port.$path.$query.$fragment; } } diff --git a/src/Symfony/Component/HttpKernel/composer.json b/src/Symfony/Component/HttpKernel/composer.json index 7dfd487e25d36..3a8ecf5cd7cb9 100644 --- a/src/Symfony/Component/HttpKernel/composer.json +++ b/src/Symfony/Component/HttpKernel/composer.json @@ -20,7 +20,7 @@ "symfony/deprecation-contracts": "^2.5|^3", "symfony/error-handler": "^6.4|^7.0", "symfony/event-dispatcher": "^5.4|^6.0|^7.0", - "symfony/http-foundation": "^6.3.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", "symfony/polyfill-ctype": "^1.8", "psr/log": "^1|^2|^3" },