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

Skip to content

Commit ddd6767

Browse files
author
Robin Chalas
committed
feature #30385 [SecurityBundle] Validate the IPs configured in access_control (javiereguiluz)
This PR was squashed before being merged into the 4.3-dev branch (closes #30385). Discussion ---------- [SecurityBundle] Validate the IPs configured in access_control | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #30299 | License | MIT | Doc PR | - Commits ------- 857ac95 [SecurityBundle] Validate the IPs configured in access_control
2 parents 7908549 + 857ac95 commit ddd6767

File tree

5 files changed

+85
-0
lines changed

5 files changed

+85
-0
lines changed

src/Symfony/Bundle/SecurityBundle/DependencyInjection/MainConfiguration.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,15 @@ private function addAccessControlSection(ArrayNodeDefinition $rootNode)
143143
->integerNode('port')->defaultNull()->end()
144144
->arrayNode('ips')
145145
->beforeNormalization()->ifString()->then(function ($v) { return [$v]; })->end()
146+
->beforeNormalization()->always()->then(function ($v) {
147+
foreach ($v as $ip) {
148+
if (false === $this->isValidIp($ip)) {
149+
throw new \LogicException(sprintf('The given "%s" value in the "access_control" config option is not a valid IP address.', $ip));
150+
}
151+
}
152+
153+
return $v;
154+
})->end()
146155
->prototype('scalar')->end()
147156
->end()
148157
->arrayNode('methods')
@@ -419,4 +428,30 @@ private function addEncodersSection(ArrayNodeDefinition $rootNode)
419428
->end()
420429
;
421430
}
431+
432+
private function isValidIp(string $cidr): bool
433+
{
434+
$cidrParts = explode('/', $cidr);
435+
436+
if (1 === \count($cidrParts)) {
437+
return false !== filter_var($cidrParts[0], FILTER_VALIDATE_IP);
438+
}
439+
440+
$ip = $cidrParts[0];
441+
$netmask = $cidrParts[1];
442+
443+
if (!ctype_digit($netmask)) {
444+
return false;
445+
}
446+
447+
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) {
448+
return $netmask <= 32;
449+
}
450+
451+
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6)) {
452+
return $netmask <= 128;
453+
}
454+
455+
return false;
456+
}
422457
}

src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/FormLoginBundle/Resources/config/routing.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,15 @@ secured-by-one-ip:
3131
secured-by-two-ips:
3232
path: /secured-by-two-ips
3333

34+
secured-by-one-real-ip:
35+
path: /secured-by-one-real-ip
36+
37+
secured-by-one-real-ip-with-mask:
38+
path: /secured-by-one-real-ip-with-mask
39+
40+
secured-by-one-real-ipv6:
41+
path: /secured-by-one-real-ipv6
42+
3443
form_logout:
3544
path: /logout_path
3645

src/Symfony/Bundle/SecurityBundle/Tests/Functional/SecurityRoutingIntegrationTest.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,16 @@ public function testSecurityConfigurationForMultipleIPAddresses($config)
7171
{
7272
$allowedClientA = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => $config], ['REMOTE_ADDR' => '1.1.1.1']);
7373
$allowedClientB = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => $config], ['REMOTE_ADDR' => '2.2.2.2']);
74+
$allowedClientC = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => $config], ['REMOTE_ADDR' => '203.0.113.0']);
7475
$barredClient = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => $config], ['REMOTE_ADDR' => '192.168.1.1']);
7576

7677
$this->assertAllowed($allowedClientA, '/secured-by-two-ips');
7778
$this->assertAllowed($allowedClientB, '/secured-by-two-ips');
79+
80+
$this->assertRestricted($allowedClientA, '/secured-by-one-real-ip');
81+
$this->assertRestricted($allowedClientA, '/secured-by-one-real-ipv6');
82+
$this->assertAllowed($allowedClientC, '/secured-by-one-real-ip-with-mask');
83+
7884
$this->assertRestricted($barredClient, '/secured-by-two-ips');
7985
}
8086

@@ -100,6 +106,15 @@ public function testSecurityConfigurationForExpression($config)
100106
$this->assertAllowed($allowedClient, '/protected-via-expression');
101107
}
102108

109+
public function testInvalidIpsInAccessControl()
110+
{
111+
$this->expectException(\LogicException::class);
112+
$this->expectExceptionMessage('The given "256.357.458.559" value in the "access_control" config option is not a valid IP address.');
113+
114+
$client = $this->createClient(['test_case' => 'StandardFormLogin', 'root_config' => 'invalid_ip_access_control.yml']);
115+
$client->request('GET', '/unprotected_resource');
116+
}
117+
103118
private function assertAllowed($client, $path)
104119
{
105120
$client->request('GET', $path);

src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/StandardFormLogin/config.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ security:
3939
- { path: ^/secure-but-not-covered-by-access-control$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
4040
- { path: ^/secured-by-one-ip$, ip: 10.10.10.10, roles: IS_AUTHENTICATED_ANONYMOUSLY }
4141
- { path: ^/secured-by-two-ips$, ips: [1.1.1.1, 2.2.2.2], roles: IS_AUTHENTICATED_ANONYMOUSLY }
42+
# these real IP addresses are reserved for docs/examples (https://tools.ietf.org/search/rfc5737)
43+
- { path: ^/secured-by-one-real-ip$, ips: 198.51.100.0, roles: IS_AUTHENTICATED_ANONYMOUSLY }
44+
- { path: ^/secured-by-one-real-ip-with-mask$, ips: '203.0.113.0/24', roles: IS_AUTHENTICATED_ANONYMOUSLY }
45+
- { path: ^/secured-by-one-real-ipv6$, ips: 0:0:0:0:0:ffff:c633:6400, roles: IS_AUTHENTICATED_ANONYMOUSLY }
4246
- { path: ^/highly_protected_resource$, roles: IS_ADMIN }
4347
- { path: ^/protected-via-expression$, allow_if: "(is_anonymous() and request.headers.get('user-agent') matches '/Firefox/i') or is_granted('ROLE_USER')" }
4448
- { path: .*, roles: IS_AUTHENTICATED_FULLY }
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
imports:
2+
- { resource: ./../config/default.yml }
3+
4+
security:
5+
encoders:
6+
Symfony\Component\Security\Core\User\User: plaintext
7+
8+
providers:
9+
in_memory:
10+
memory:
11+
users:
12+
johannes: { password: test, roles: [ROLE_USER] }
13+
14+
firewalls:
15+
default:
16+
form_login: ~
17+
logout: ~
18+
anonymous: ~
19+
20+
access_control:
21+
# the '256.357.458.559' IP is wrong on purpose, to check invalid IP errors
22+
- { path: ^/unprotected_resource$, ips: [1.1.1.1, 256.357.458.559], roles: IS_AUTHENTICATED_ANONYMOUSLY }

0 commit comments

Comments
 (0)