Problem/Motivation
PHP is looking at deprecating usage of uniqid because it doesn't provide good uniqueness. Which means we're probably not getting the uniqueness we expect in the handful of places we're using it across core.
Deprecation RFC: https://wiki.php.net/rfc/deprecate-uniqid
Old discussion on uniqueness. https://marc.info/?l=php-internals&m=148314015430338&w=2
Proposed resolution
I propose we get ahead of any decision there and clean up usage of a potentially problematic function.
Remaining tasks
Decide on how we remove.
1. Inline some hash'd random implementation?
2. Provide a drupal_uniqid() implementation?
3. other?
User interface changes
n/a
API changes
New method.
Hashes will be calculated different but this should not be breaking. There is "randomness" in uniqid so there should be no expectation about the predictability of that randomness built in to our API.
Data model changes
n/a?
Comments
Comment #2
neclimdulHere are the current matches in core for review:
Mostly in migrate with some outliers in the DB, Lock, Twig and that one testing salt in bootstrap.
Comment #7
neclimdulI guess I'll just take a stab at something and see what happens.
Comment #14
catchUseful information here: https://git.drupalcode.org/project/drupal/-/merge_requests/4801#note_208955
Comment #15
mfbtldr I found that Crypt::randomBytesBase64(16) is 3x faster than uniqid(), because uniqid() sleeps for a full microsecond on many platforms.
But if Crypt::randomBytesBase64() is considered too much of a CPU hog, PHP 7.3+ function hrtime() could be used to build a much faster time-based "uniqid" (to which extra entropy could be added).
Comment #16
mondrakeOpened https://github.com/mglaman/phpstan-drupal/issues/604 for adding a rule to prevent usage of uniqid(), so that if we do this then we’re not regressing later.
Comment #17
mglamanI do not think this code should go into phpstan-drupal. It seems like a candidate for a third-party library like https://github.com/ekino/phpstan-banned-code which allows banning particular functions and other code to be used.
Drupal core does not need to add that library, though. It could provide the rule itself and register it within the phpstan.neon
So we could have
\Drupal\Core\Phpstan\Rules\NoUniqidRulethat checksFuncCallfor that function.Then the phpstan.neon just needs the following added:
Comment #18
mondrakeWell if a PHPStan extension covers the use case already, then why reinvent the wheel? Will open a spin-off to experiment.
Comment #19
mondrakeFiled #3390368: Adopt ekino/phpstan-banned-code and use it to prevent further usage of uniqid(), md5(), sha1(). Thanks @mglaman for the pointer!!
Comment #20
mglamanThe suggestion was to avoid adding a dependency for a pretty simplistic rule.
And the package has other things in it not quite needed. And it makes assumptions:
Comment #21
mondrake#20 yes hit that, see the child issue.
Still I think if we do sth Drupal, we should still make it configurable so that one rule class, many blocked functions, as opposed to as many rule classes as the possible functions we will ever want to block.
Comment #22
catchWe could potentially use the cspell banned word list for this, see #3274896: Add some words we'd prefer to avoid to Drupal core's flagWords for a different use case.
It would mean you can't explain anywhere why you're not using uniqid() (at least not without using cspell:ignore) but no dependency and no code involved to use that.
I could see us using this this md5() and sha1() once #3307718: Implement xxHash for non-cryptographic use-cases is in too.
Comment #23
mondrakeMmm IMHO mixing spell checking with static analysis is no good. And I'm no purist :)
Just adding a single ignoreErrors entry could make the library do what we need ATM, see https://www.drupal.org/pift-ci-job/2774106 which also bans md5() and sha1().
The good things - 1) it explains it's the use of the function that is no good (vs spellcheck); 2) we can baseline the errors now, like anything PHPStan, stop the bleeding, and fix the existing usage at different pace.
Comment #24
mfbIn case anyone was curious, I did some benchmarking of various unique ID methods, although just on one Linux system so this is very anecdotal.
Crypt::randomBytesBase64(16)was faster thansymfony/uidpackage'sUuid::v4()(which outputs basically the same thing in a different format).My homemade function
hrtime(TRUE) . '-' . mt_rand()is faster still. Thehrtime()counter wraps around when the system reboots.(Edited to remove some wrong info re: cross-platform behavior, that was just due to the systems rebooting.)
Comment #25
kim.pepperIn #3408045: [PP-1] Replace using mt_rand() to generate a unique file upload progress ID with bin2hex(random_bytes(16) we a proposing to use
bin2hex(random_bytes(16)I would be interested to see how that compares to the other options.Comment #26
mfbIn my testing, bin2hex(random_bytes(16)) was about 22% faster than Crypt::randomBytesBase64(16), so it would be in second place in the chart above. of course it generates a 45% longer string (32 characters instead of 22 characters).
Comment #27
kim.pepperAh good to know. I had to look up the reason we had
Crypt::randomBytesBase64()and makes sense if you need it to be URL-safe.Comment #29
godotislate#3581442: Replace usage of uniqid() in the Database system is in and replaced
uniqidwithbin2hex(random_bytes(12)).