Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0486a18

Browse files
committed
Allow URL and URN to be used as redirection URIs
1 parent b4128fd commit 0486a18

File tree

2 files changed

+57
-34
lines changed

2 files changed

+57
-34
lines changed

src/Symfony/Component/Security/Http/HttpUtils.php

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,20 @@ public function checkRequestPath(Request $request, string $path)
148148
*/
149149
public function generateUri(Request $request, string $path)
150150
{
151-
if (str_starts_with($path, 'http') || !$path) {
151+
$url = parse_url($path);
152+
153+
// Prevent path traversal (ex: https://example.com/../../file)
154+
if (preg_match('#/\.\.?(/|$)#', $url['path'])) {
155+
return $request->getUriForPath('/');
156+
}
157+
158+
// Prevent protocol-relative redirection (ex: //example.com/file)
159+
if (!isset($url['scheme']) && isset($url['host'], $url['path'])) {
160+
return $request->getUriForPath('/');
161+
}
162+
163+
// absolute URL
164+
if (isset($url['scheme'], $url['host'])) {
152165
return $path;
153166
}
154167

src/Symfony/Component/Security/Http/Tests/HttpUtilsTest.php

Lines changed: 43 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -25,37 +25,52 @@
2525

2626
class HttpUtilsTest extends TestCase
2727
{
28-
public function testCreateRedirectResponseWithPath()
28+
/**
29+
* @dataProvider validRequestDomainUrls
30+
*/
31+
public function testCreateRedirectResponseWithPath(?string $domainRegexp, string $path, string $expectedRedirectUri)
2932
{
30-
$utils = new HttpUtils($this->getUrlGenerator());
31-
$response = $utils->createRedirectResponse($this->getRequest(), '/foobar');
33+
$utils = new HttpUtils($this->getUrlGenerator(), null, $domainRegexp);
34+
$response = $utils->createRedirectResponse($this->getRequest(), $path);
3235

33-
$this->assertTrue($response->isRedirect('http://localhost/foobar'));
36+
$this->assertTrue($response->isRedirect($expectedRedirectUri));
3437
$this->assertEquals(302, $response->getStatusCode());
3538
}
3639

37-
public function testCreateRedirectResponseWithAbsoluteUrl()
40+
public static function validRequestDomainUrls()
3841
{
39-
$utils = new HttpUtils($this->getUrlGenerator());
40-
$response = $utils->createRedirectResponse($this->getRequest(), 'http://symfony.com/');
41-
42-
$this->assertTrue($response->isRedirect('http://symfony.com/'));
43-
}
44-
45-
public function testCreateRedirectResponseWithDomainRegexp()
46-
{
47-
$utils = new HttpUtils($this->getUrlGenerator(), null, '#^https?://symfony\.com$#i');
48-
$response = $utils->createRedirectResponse($this->getRequest(), 'http://symfony.com/blog');
49-
50-
$this->assertTrue($response->isRedirect('http://symfony.com/blog'));
51-
}
52-
53-
public function testCreateRedirectResponseWithRequestsDomain()
54-
{
55-
$utils = new HttpUtils($this->getUrlGenerator(), null, '#^https?://%s$#i');
56-
$response = $utils->createRedirectResponse($this->getRequest(), 'http://localhost/blog');
57-
58-
$this->assertTrue($response->isRedirect('http://localhost/blog'));
42+
return [
43+
'/foobar' => [
44+
null,
45+
'/foobar',
46+
'http://localhost/foobar',
47+
],
48+
'http://symfony.com/ without domain regex' => [
49+
null,
50+
'http://symfony.com/',
51+
'http://symfony.com/',
52+
],
53+
'http://localhost/blog with #^https?://symfony\.com$#i' => [
54+
'#^https?://symfony\.com$#i',
55+
'http://symfony.com/blog',
56+
'http://symfony.com/blog',
57+
],
58+
'http://localhost/blog with #^https?://%s$#i' => [
59+
'#^https?://%s$#i',
60+
'http://localhost/blog',
61+
'http://localhost/blog',
62+
],
63+
'custom scheme' => [
64+
null,
65+
'android-app://com.google.android.gm/',
66+
'android-app://com.google.android.gm/',
67+
],
68+
'custom scheme with all URL components' => [
69+
null,
70+
'android-app://foo:[email protected]:8080/software/index.html?lite=true#section1',
71+
'android-app://foo:[email protected]:8080/software/index.html?lite=true#section1',
72+
],
73+
];
5974
}
6075

6176
/**
@@ -72,22 +87,17 @@ public function testCreateRedirectResponseWithBadRequestsDomain($url)
7287
public static function badRequestDomainUrls()
7388
{
7489
return [
90+
['http:///foo'],
7591
['http://pirate.net/foo'],
92+
['//evil.com/do-bad-things'],
93+
['http://localhost/foo/../bar'],
7694
['http:\\\\pirate.net/foo'],
7795
['http:/\\pirate.net/foo'],
7896
['http:\\/pirate.net/foo'],
7997
['http://////pirate.net/foo'],
8098
];
8199
}
82100

83-
public function testCreateRedirectResponseWithProtocolRelativeTarget()
84-
{
85-
$utils = new HttpUtils($this->getUrlGenerator(), null, '#^https?://%s$#i');
86-
$response = $utils->createRedirectResponse($this->getRequest(), '//evil.com/do-bad-things');
87-
88-
$this->assertTrue($response->isRedirect('http://localhost//evil.com/do-bad-things'), 'Protocol-relative redirection should not be supported for security reasons');
89-
}
90-
91101
public function testCreateRedirectResponseWithRouteName()
92102
{
93103
$utils = new HttpUtils($urlGenerator = $this->createMock(UrlGeneratorInterface::class));

0 commit comments

Comments
 (0)