feature #32194 [HttpFoundation] Add a way to anonymize IPs (Seldaek)

This PR was merged into the 4.4 branch.

Discussion
----------

[HttpFoundation] Add a way to anonymize IPs

| Q             | A
| ------------- | ---
| Branch?       | 4.4
| Bug fix?      | no
| New feature?  | yes
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets |
| License       | MIT
| Doc PR        | symfony/symfony-docs#... <!-- required for new features --> TODO

This is helpful for GDPR compliance reasons, and it isn't much code saved but it's also good if you don't have to think about how to do it.

Commits
-------

9e62330bc4 [HttpFoundation] Add a way to anonymize IPs
This commit is contained in:
Fabien Potencier 2019-11-09 07:36:01 +01:00
commit f4c925418b
3 changed files with 60 additions and 1 deletions

View File

@ -11,7 +11,8 @@ CHANGELOG
make sure to run `CREATE INDEX EXPIRY ON sessions (sess_lifetime)` to update your database
to speed up garbage collection of expired sessions.
* added `SessionHandlerFactory` to create session handlers with a DSN
* added `IpUtils::anonymize()` to help with GDPR compliance.
4.3.0
-----

View File

@ -153,4 +153,36 @@ class IpUtils
return self::$checkedIps[$cacheKey] = true;
}
/**
* Anonymizes an IP/IPv6.
*
* Removes the last byte for v4 and the last 8 bytes for v6 IPs
*/
public static function anonymize(string $ip): string
{
$wrappedIPv6 = false;
if ('[' === substr($ip, 0, 1) && ']' === substr($ip, -1, 1)) {
$wrappedIPv6 = true;
$ip = substr($ip, 1, -1);
}
$packedAddress = inet_pton($ip);
if (4 === \strlen($packedAddress)) {
$mask = '255.255.255.0';
} elseif ($ip === inet_ntop($packedAddress & inet_pton('::ffff:ffff:ffff'))) {
$mask = '::ffff:ffff:ff00';
} elseif ($ip === inet_ntop($packedAddress & inet_pton('::ffff:ffff'))) {
$mask = '::ffff:ff00';
} else {
$mask = 'ffff:ffff:ffff:ffff:0000:0000:0000:0000';
}
$ip = inet_ntop($packedAddress & inet_pton($mask));
if ($wrappedIPv6) {
$ip = '['.$ip.']';
}
return $ip;
}
}

View File

@ -101,4 +101,30 @@ class IpUtilsTest extends TestCase
'invalid request IP with invalid proxy wildcard' => ['0.0.0.0', '*'],
];
}
/**
* @dataProvider anonymizedIpData
*/
public function testAnonymize($ip, $expected)
{
$this->assertSame($expected, IpUtils::anonymize($ip));
}
public function anonymizedIpData()
{
return [
['192.168.1.1', '192.168.1.0'],
['1.2.3.4', '1.2.3.0'],
['2a01:198:603:0:396e:4789:8e99:890f', '2a01:198:603::'],
['2a01:198:603:10:396e:4789:8e99:890f', '2a01:198:603:10::'],
['::1', '::'],
['0:0:0:0:0:0:0:1', '::'],
['1:0:0:0:0:0:0:1', '1::'],
['0:0:603:50:396e:4789:8e99:0001', '0:0:603:50::'],
['[0:0:603:50:396e:4789:8e99:0001]', '[0:0:603:50::]'],
['[2a01:198::3]', '[2a01:198::]'],
['::ffff:123.234.235.236', '::ffff:123.234.235.0'], // IPv4-mapped IPv6 addresses
['::123.234.235.236', '::123.234.235.0'], // deprecated IPv4-compatible IPv6 address
];
}
}