feature #38333 [Uid] make UUIDv6 always return truly random nodes to prevent leaking the MAC of the host (nicolas-grekas)

This PR was merged into the 5.2-dev branch.

Discussion
----------

[Uid] make UUIDv6 always return truly random nodes to prevent leaking the MAC of the host

| Q             | A
| ------------- | ---
| Branch?       | master
| Bug fix?      | no
| New feature?  | yes
| Deprecations? | no
| Tickets       | -
| License       | MIT
| Doc PR        | -

As explained in http://gh.peabody.io/uuidv6/, the wording of the UUIDv1 spec suggests that using the MAC of the host is preferred to compute the "node" field of UUIDs. This is what the uuid extension does, and the reason why the 12 last chars of the UUIDv1 it generates are stable. But this is a privacy leak. There are stories in the wild about how knowing the MAC has been abused in the past.

UUIDv6 prefers putting a secure random number there.

So here is the PR to do so.

Commits
-------

b9c61ca86c [Uid] make UUIDv6 always return truly random nodes to prevent leaking the MAC of the host
This commit is contained in:
Fabien Potencier 2020-09-29 08:35:35 +02:00
commit 3113fce1df
3 changed files with 32 additions and 1 deletions

View File

@ -1,6 +1,11 @@
CHANGELOG
=========
5.2.0
-----
* made UUIDv6 always return truly random node fields to prevent leaking the MAC of the host
5.1.0
-----

View File

@ -89,6 +89,14 @@ class UuidTest extends TestCase
$this->assertSame('3499710062d0', $uuid->getNode());
}
public function testV6IsSeeded()
{
$uuidV1 = Uuid::v1();
$uuidV6 = Uuid::v6();
$this->assertNotSame(substr($uuidV1, 24), substr($uuidV6, 24));
}
public function testBinary()
{
$uuid = new UuidV4(self::A_UUID_V4);

View File

@ -14,6 +14,8 @@ namespace Symfony\Component\Uid;
/**
* A v6 UUID is lexicographically sortable and contains a 60-bit timestamp and 62 extra unique bits.
*
* Unlike UUIDv1, this implementation of UUIDv6 doesn't leak the MAC address of the host.
*
* @experimental in 5.1
*
* @author Nicolas Grekas <p@tchwork.com>
@ -22,11 +24,27 @@ class UuidV6 extends Uuid
{
protected const TYPE = 6;
private static $seed;
public function __construct(string $uuid = null)
{
if (null === $uuid) {
$uuid = uuid_create(\UUID_TYPE_TIME);
$this->uid = substr($uuid, 15, 3).substr($uuid, 9, 4).$uuid[0].'-'.substr($uuid, 1, 4).'-6'.substr($uuid, 5, 3).substr($uuid, 18);
$this->uid = substr($uuid, 15, 3).substr($uuid, 9, 4).$uuid[0].'-'.substr($uuid, 1, 4).'-6'.substr($uuid, 5, 3).substr($uuid, 18, 6);
// uuid_create() returns a stable "node" that can leak the MAC of the host, but
// UUIDv6 prefers a truly random number here, let's XOR both to preserve the entropy
if (null === self::$seed) {
self::$seed = [random_int(0, 0xffffff), random_int(0, 0xffffff)];
}
$node = unpack('N2', hex2bin('00'.substr($uuid, 24, 6)).hex2bin('00'.substr($uuid, 30)));
$this->uid .= sprintf('%06x%06x',
(self::$seed[0] ^ $node[1]) | 0x010000,
self::$seed[1] ^ $node[2]
);
} else {
parent::__construct($uuid);
}