[RFC] [VOTE] Deprecations for PHP 8.4

php.internals

Gina P. Banyard

2 years ago
Hello internals, I have opened the vote for the mega deprecation RFC: https://wiki.php.net/rfc/deprecations_php_8_4 Reminder, each vote must be submitted individually. Best regards, Gina P. Banyard

Jakub Zelenka

2 years ago
Hi, On Fri, Jul 19, 2024 at 6:42 PM Gina P. Banyard <internals@gpb.moe> wrote:
> Hello internals, > > I have opened the vote for the mega deprecation RFC: > https://wiki.php.net/rfc/deprecations_php_8_4 > > Reminder, each vote must be submitted individually. > > >
Just wanted to send some reasoning of my no votes. I voted no on those output handlers as there might be potentially better solutions. The whole output stuff needs a closer look so I think we should wait on this until the review is done. Otherwise I also voted no for the mysqli_kill and mysqli_refresh functions as I feel that it's not a big deal to keep them (zero maintenance basically) and there will be likely users to use them. I think it would make sense to not add them but if there are already there I don't see a point to remove them. I think we should also keep file_put_contents array argument as it might actually be used with iovec in the future which could be a significant optimization - need to check details if that would work but if it does, it could be a pretty good optimization. The CSV one is also a bit weird because the default is non empty parameter so I'm not sure what this actually brings except some inconsistency. People that explicitly set it, do that probably for some reason. I would really prefer not to try to change this functionality as the BC breaks will cause more issues. All my other no are mainly about the BC concerns that I have. Regards Jakub

Jakub Zelenka

2 years ago
On Mon, Jul 22, 2024 at 11:59 AM Jakub Zelenka <bukka@php.net> wrote:
> I think we should also keep file_put_contents array argument as it might > actually be used with iovec in the future which could be a significant > optimization - need to check details if that would work but if it does, it > could be a pretty good optimization. >
I had a bit closer look on this one and it should be possible to optimize it for some cases. We could basically introduce something like php_stream_writev. It would need to have logic to do the same sort concatenation if filters used or for stream wrappers not supporting iovec. But for plain wrapper, we should be able to support it and it could be a good optimization for some users and a way how to cleanly expose it. So I would suggest to remove this from this list as there seems to be a good use case for this functionality. Regards Jakub

Christoph Becker

2 years ago
Hi Jakub! On 22.07.2024 at 12:59, Jakub Zelenka wrote:
> On Fri, Jul 19, 2024 at 6:42 PM Gina P. Banyard <internals@gpb.moe> wrote: > >> I have opened the vote for the mega deprecation RFC: >> https://wiki.php.net/rfc/deprecations_php_8_4 > > Just wanted to send some reasoning of my no votes. > > The CSV one is also a bit weird because the default is non empty parameter > so I'm not sure what this actually brings except some inconsistency. People > that explicitly set it, do that probably for some reason. I would really > prefer not to try to change this functionality as the BC breaks will cause > more issues.
The default "\\" likely causes more harm than good for almost anybody. It basically enables a proprietary extension to CSV (something like DSV), but there are a couple of issues where it is totally unclear what should happen, and there still might be unresolved (because unresolvable) tickets lying around about that. I do have to agree, though, that this deprecation is somewhat unfortunate, since an empty string is only accepted as of PHP 7.4.0, so there is likely some code around which passes e.g. "\0" which also disables the proprietary extension if there are no NUL bytes in the CSV file (or to be written to a CSV file). For that reason I didn't vote on that deprecation, although I would not like to keep that proprietary extension forever. Cheers, Christoph

Jakub Zelenka

2 years ago
Hi, On Mon, Jul 22, 2024 at 11:59 AM Jakub Zelenka <bukka@php.net> wrote:
> Hi, > > On Fri, Jul 19, 2024 at 6:42 PM Gina P. Banyard <internals@gpb.moe> wrote: > >> Hello internals, >> >> I have opened the vote for the mega deprecation RFC: >> https://wiki.php.net/rfc/deprecations_php_true8_4 >> <https://wiki.php.net/rfc/deprecations_php_8_4> >> >> Reminder, each vote must be submitted individually. >> >> > > I voted no on those output handlers as there might be potentially better > solutions. The whole output stuff needs a closer look so I think we should > wait on this until the review is done. >
I just had a bit closer look to output handler working and the text is actually not correct and does not exactly reflect how things works. Interestingly those two suggested deprecations have associated functionality that can be seen in the following example: https://3v4l.org/X91eu This simple example shows that returning false from the handler have a special behaviour that can be in no way replaced by throwing exception. What it does is that it flushes all buffers and does not trigger any error as far as I see. It also shows that output in the output handler is not actually always discarded and can be actually used to append text which might be actually useful functionality for some users. This is just finding from looking and testing things for around an hour and half of my time so I might missed other bits. I really think we should first try to properly understand how the whole output handling works before doing those sort of deprecations. The RFC should then contain all details about the edge cases so voters can do informed decision. I would suggest to take this part out and at least delay it till the next release. Apology for not taking look sooner but I have been pretty busy until now... Regards Jakub

Derick Rethans

2 years ago
On Fri, 19 Jul 2024, Gina P. Banyard wrote:
> Hello internals, > > I have opened the vote for the mega deprecation RFC: > https://wiki.php.net/rfc/deprecations_php_8_4 > > Reminder, each vote must be submitted individually.
I have voted no for a few, as they had no impact assesment at all: - Deprecate returning non-string values from a user output handler - Deprecate lcg_value() - Deprecate md5(), sha1(), md5_file(), and sha1_file() (just says "large impact") - Deprecate SOAP_FUNCTIONS_ALL constant and passing it to SoapServer::addFunction() And no on a few others: - Deprecate using a single underscore ''_'' as a class name (it breaks some of my ... old slides — but I also don't really the problem with this. - Remove the E_STRICT Error Level and Deprecate the E_STRICT constant? (Because I added it :-) ) cheers, Derick

Peter Stalman

2 years ago
On Mon, Jul 22, 2024 at 9:06 AM Derick Rethans <derick@php.net> wrote:
> - Deprecate md5(), sha1(), md5_file(), and sha1_file() (just says "large > impact")
About 1.2 million. https://github.com/search?q=%28md5+OR+md5_file+OR+sha1+OR+sha1_file%29+language%3APHP+&type=code The proposed deprecation of these functions in PHP due to their cryptographic insecurities seems to overlook their valid non-cryptographic applications. If we consider the context, the scope of cryptographic usage is already quite specific. We're talking about end users who are rolling their own security implementations and are unaware of the security risks but somehow know how to use these functions without reading the documentation and warnings. The number of people who fall into this specific category is quite small. Yet, this change is being proposed for their sake. It's important to note that these same users could/will easily make other security mistakes regardless of this deprecation. On the other hand, who will be impacted by these deprecations? Potentially everyone, as these are included in many projects and in many vendor packages. It's busy work for the people who aren't affected. Sure, eventually, it will all be sorted out as CI warnings slowly subside because of this. Reasons such as GIT and most cloud storages using these functions should be enough to spare them. Example: https://rclone.org/overview/ The point is that there are several reasons in 2024 to use md5 and sha1. Granted hashing passwords isn't one, but we're past that as a community already. And for the few that aren't, I'd argue there is no saving. Thanks, Peter

Weedpacket

2 years ago
On 2024-07-24 15:58, Peter Stalman wrote:
> On Mon, Jul 22, 2024 at 9:06 AM Derick Rethans <derick@php.net > <mailto:derick@php.net>> wrote: > > - Deprecate md5(), sha1(), md5_file(), and sha1_file() (just says > "large >   impact") > > > About 1.2 million. > https://github.com/search?q=%28md5+OR+md5_file+OR+sha1+OR+sha1_file%29+language%3APHP+&type=code <https://github.com/search?q=%28md5+OR+md5_file+OR+sha1+OR+sha1_file%29+language%3APHP+&type=code> > > > On the other hand, who will be impacted by these deprecations? > Potentially everyone, as these are included in many projects and in many > vendor packages.  It's busy work for the people who aren't affected. > Sure, eventually, it will all be sorted out as CI warnings slowly > subside because of this. > > Reasons such as GIT and most cloud storages using these functions should > be enough to spare them. Example: https://rclone.org/overview/ > <https://rclone.org/overview/> > > The point is that there are several reasons in 2024 to use md5 and sha1. > Granted hashing passwords isn't one, but we're past that as a community > already. And for the few that aren't, I'd argue there is no saving. >
And they would still be available as hash("md5") and hash("sha1"); the only reason they're called out as their own distinct functions today is historical inertia.

Peter Stalman

2 years ago
On Wed, Jul 24, 2024 at 3:03 PM Morgan <weedpacket@varteg.nz> wrote:
> And they would still be available as hash("md5") and hash("sha1"); the > only reason they're called out as their own distinct functions today is > historical inertia. >
Yes, I am aware of that, it's covered in the RFC and has been discussed. My issue is that I think the positive effect this will have is minimal, while the impact is very extensive. I also disagree with the notion that there is no longer a use for these algos in the present day, as there are many technologies and systems that still use these for basic checksumming. To make everyone go through and update these seems ridiculous to me, as it's basically just **renaming functions**. If it goes through, I foresee a composer package called md5-sha1-shim being a popular package. It won't stop the people this intends to save. Lots of effect with little gain. The warning in the documentation should be sufficient. Thanks, Peter

Rowan Tommins [IMSoP]

2 years ago
On 24/07/2024 23:01, Morgan wrote:
> And they would still be available as hash("md5") and hash("sha1"); the > only reason they're called out as their own distinct functions today > is historical inertia.
I don't agree that the reasons for including standalone functions are "historical". The RFC itself gives a good reason for having such functions:
> Unfortunately these cryptographically secure hash functions are only
available by means of the generic hash() function ... making using them more verbose and thus seemingly more complicated Rather than force people to use functions that we acknowledge are hard to use, surely the logical thing is to make the "right" code *easy* to use? Which means if we want people to use SHA-256, let's add a sha256() function to make it easy. This is what password_hash() and password_verify() did right: the functionality was already there in crypt(), but it's hard to use, and harder to use correctly. Providing clearer functions, even though they do the same thing, helps new developers "fall into the pit of success". The hash() function isn't quite as confusing as crypt(), but according to the manual, it currently supports 60 different algorithms, most of which I have never heard of. I'm aware that "sha256" is better than "sha1", but should I be aiming higher, and using "sha384", or maybe one of the four flavours of "sha3"? Then there's the fun-sounding "whirlpool", the faintly rude-sounding "snefru", and a bewildering fifteen flavours of "haval". A new user being told "don't use sha1(), use hash() and pick from this list" is more likely to say "ah, there's sha1, jolly good" than spend an afternoon reading cryptography journals. There's no pit of success to fall into. Regards,
-- Rowan Tommins [IMSoP]

Nick Lockheart

2 years ago
On Thu, 2024-07-25 at 22:34 +0100, Rowan Tommins [IMSoP] wrote:
> On 24/07/2024 23:01, Morgan wrote: > > And they would still be available as hash("md5") and hash("sha1"); > > the > > only reason they're called out as their own distinct functions > > today > > is historical inertia. > > > I don't agree that the reasons for including standalone functions are > "historical". The RFC itself gives a good reason for having such > functions: > >  > Unfortunately these cryptographically secure hash functions are > only > available by means of the generic hash() function ... making using > them > more verbose and thus seemingly more complicated > > > Rather than force people to use functions that we acknowledge are > hard > to use, surely the logical thing is to make the "right" code *easy* > to use? > > Which means if we want people to use SHA-256, let's add a sha256() > function to make it easy. > > > This is what password_hash() and password_verify() did right: the > functionality was already there in crypt(), but it's hard to use, and > harder to use correctly. Providing clearer functions, even though > they > do the same thing, helps new developers "fall into the pit of > success". > > The hash() function isn't quite as confusing as crypt(), but > according > to the manual, it currently supports 60 different algorithms, most of > which I have never heard of. I'm aware that "sha256" is better than > "sha1", but should I be aiming higher, and using "sha384", or maybe > one > of the four flavours of "sha3"? Then there's the fun-sounding > "whirlpool", the faintly rude-sounding "snefru", and a bewildering > fifteen flavours of "haval". > > A new user being told "don't use sha1(), use hash() and pick from > this > list" is more likely to say "ah, there's sha1, jolly good" than spend > an > afternoon reading cryptography journals. There's no pit of success to > fall into. > > > Regards, >
That's a good point. What if there were crypto functions that worked like password_hash() in that they had one generic function name, but magically used the new/better "best practice" algorithms as time went by without the need to update any calling code? Maybe there should be three generic-named functions: fast_hash() // not secure, makes UIDs quickly secure_hash() // uses best practice one-way hash algo secure_crypt() // uses best practice reversible encryption. Then the developer signals their *intent* by choosing a function name, and the algorithm magically works underneath (perhaps with the option of an ini override to make those functions work in different environments).

Mike Schinkel

2 years ago
> > On Jul 25, 2024 at 6:02 PM, <Nick Lockheart (mailto:lists@ageofdream.com)> wrote: > That's a good point. What if there were crypto functions that worked > > > like password_hash() in that they had one generic function name, but magically used the new/better "best practice" algorithms as time went by without the need to update any calling code? Maybe there should be three generic-named functions: fast_hash() // not secure, makes UIDs quickly secure_hash() // uses best practice one-way hash algo secure_crypt() // uses best practice reversible encryption. Then the developer signals their *intent* by choosing a function name, and the algorithm magically works underneath (perhaps with the option of an ini override to make those functions work in different environments). > >
If those _were_ added, I would bikeshed their names to make sure their intent was 100% clear:
>
insecure_hash() // not secure, makes UIDs quickly secure_oneway_hash() // uses best practice one-way hash algo secure_reversible_hash() // uses best practice reversible encryption. -Mike

Unnamed Person

2 years ago
On 26-7-2024 0:00, Nick Lockheart wrote:
> > That's a good point. What if there were crypto functions that worked > like password_hash() in that they had one generic function name, but > magically used the new/better "best practice" algorithms as time went > by without the need to update any calling code? > > Maybe there should be three generic-named functions: > > fast_hash() // not secure, makes UIDs quickly > secure_hash() // uses best practice one-way hash algo > secure_crypt() // uses best practice reversible encryption.
While I like the idea, this sounds like a huge nightmare in the waiting when data is stored somewhere and later compared. Example: * Let's say these functions get introduced in PHP 8.5. * `secure_hash()` is used in an application running on PHP 8.5 to secure some data before storing it in a database. This data is used in comparisons - stored vs user provided. * Now in PHP 9.1, the hash algorithm is changed. * The production environment gets updated to PHP 9.1 and suddenly the application breaks as the data verification will no longer work as the new algo is used on the user provided data, but the database stored version of the same data was created with the old algo....

Nick Lockheart

2 years ago
On Fri, 2024-07-26 at 00:44 +0200, Juliette Reinders Folmer wrote:
> On 26-7-2024 0:00, Nick Lockheart wrote: >   > > > > That's a good point. What if there were crypto functions that > > worked > > like password_hash() in that they had one generic function name, > > but > > magically used the new/better "best practice" algorithms as time > > went > > by without the need to update any calling code? > > > > Maybe there should be three generic-named functions: > > > > fast_hash() // not secure, makes UIDs quickly > > secure_hash() // uses best practice one-way hash algo > > secure_crypt() // uses best practice reversible encryption. >   >  While I like the idea, this sounds like a huge nightmare in the > waiting when data is stored somewhere and later compared. >   >  Example: >  * Let's say these functions get introduced in PHP 8.5. >  * `secure_hash()` is used in an application running on PHP 8.5 to > secure some data before storing it in a database. This data is used > in comparisons - stored vs user provided. >  * Now in PHP 9.1, the hash algorithm is changed. >  * The production environment gets updated to PHP 9.1 and suddenly > the application breaks as the data verification will no longer work > as the new algo is used on the user provided data, but the database > stored version of the same data was created with the old algo.... >  
Doesn't password_hash() handle this automatically? The result of the password_hash() function includes the hash and the algorithm used to hash it. That way password_verify() magically works with the string that came from password_hash().

Rowan Tommins [IMSoP]

2 years ago
On 25 July 2024 23:54:53 BST, Nick Lockheart <lists@ageofdream.com> wrote:
>Doesn't password_hash() handle this automatically? The result of the >password_hash() function includes the hash and the algorithm used to >hash it. That way password_verify() magically works with the string >that came from password_hash().
For password hashing, you are always retrieving the hash for a specific user, and then making a yes/no decision about it. Indeed, it's an explicit aim that an attacker can't take a password and quickly scan a captured database for matching hashes. For other uses of hashes, though, the opposite is true: you want to search for matching hashes. For instance, when you store a file in git, it calculates the SHA1 hash of its content to use as a lookup key. If that key already exists in the local database, it assumes the content is the same. That also demonstrates another difference: hashes are often shared between applications, where they need to be using an agreed algorithm. If a package manager requires SHA1 hashes of each file, you can't just substitute SHA256 hashes without some other agreed changes. Tempting though a "secure_hash" function is, I don't think it's practical for a lot of the places hashing is used. Regards, Rowan Tommins [IMSoP]

Rob Landers

2 years ago
On Fri, Jul 26, 2024, at 08:44, Rowan Tommins [IMSoP] wrote:
> > > On 25 July 2024 23:54:53 BST, Nick Lockheart <lists@ageofdream.com> wrote: > >Doesn't password_hash() handle this automatically? The result of the > >password_hash() function includes the hash and the algorithm used to > >hash it. That way password_verify() magically works with the string > >that came from password_hash(). > > For password hashing, you are always retrieving the hash for a specific user, and then making a yes/no decision about it. Indeed, it's an explicit aim that an attacker can't take a password and quickly scan a captured database for matching hashes.
You’d be surprised how many projects get this wrong and claim it isn't a security issue. If you can get the hashes, you likely have the ability to run arbitrary sql commands and since password_hash stores the salt right in the hash, you just need to crack one easy to guess password -- or just run password_hash on your machine ... then copy it to whatever user you want to login as. Very few php projects salt the passwords with something application/user specific (see: symfony's legacy password implementation which does, and new one which does not; and yes I reported it, and yes, it "isn't a security issue") to prevent this from happening. There are other bad defaults, such as pdo_mysql allowing more than one sql statement (but all other drivers not -- and mysqli is also not)... making it even easier to open yourself up to getting hacked if you use pdo with mysql; allowing a single injection to be used to insert/update or even drop tables. Security is something hard to get right, for any language and framework. PHP isn't an exception here; you have to pay attention to what you are doing and think like an attacker, every step of the way.
> > For other uses of hashes, though, the opposite is true: you want to search for matching hashes. For instance, when you store a file in git, it calculates the SHA1 hash of its content to use as a lookup key. If that key already exists in the local database, it assumes the content is the same. > > That also demonstrates another difference: hashes are often shared between applications, where they need to be using an agreed algorithm. If a package manager requires SHA1 hashes of each file, you can't just substitute SHA256 hashes without some other agreed changes. > > Tempting though a "secure_hash" function is, I don't think it's practical for a lot of the places hashing is used.
I think we can borrow from a recent RFC to return more than one thing: secure_hash($data, $algorithm = null): [$algorithm, $hash, $updated_algorithm, $updated_hash]; if you pass in an algorithm, it has to have been considered "secure" within the last two major versions*, it also returns an optional "updated" part, where it can be used to update the hash in your database, if needed. — Rob

Mike Schinkel

2 years ago
> > On Jul 25, 2024 at 5:35 PM, <Rowan Tommins [IMSoP] (mailto:imsop.php@rwec.co.uk)> wrote: > Rather than force people to use functions that we acknowledge are hard > > > to use, surely the logical thing is to make the "right" code *easy* to use? Which means if we want people to use SHA-256, let's add a sha256() function to make it easy. This is what password_hash() and password_verify() did right: the functionality was already there in crypt(), but it's hard to use, and harder to use correctly. Providing clearer functions, even though they do the same thing, helps new developers "fall into the pit of success". > >
Yes! 1000% *THIS*. -Mike

Weedpacket

2 years ago
On 2024-07-26 09:34, Rowan Tommins [IMSoP] wrote:
> On 24/07/2024 23:01, Morgan wrote: >> And they would still be available as hash("md5") and hash("sha1"); the >> only reason they're called out as their own distinct functions today >> is historical inertia. > > > I don't agree that the reasons for including standalone functions are > "historical". The RFC itself gives a good reason for having such functions: >
By "historical" I mean just md5() was in PHP in version 3, sha1() was added in 4.3, and hash() (via PECL) in 5.1.2. md5(), md5_file(), sha1(), and sha1_file() could have been deprecated when hash() became a core PHP extension in version 7, and now (or when looking at targeting 9) would have been about when we'd be discussing removing them. I'm not talking about the MD5 or SHA1 algorithms or whether they should or shouldn't be used. I'm just talking about the functions themselves. md5(), md5_file(), sha1(), and sha1_file(). They only exist because there wasn't the generic hash algorithm extension when they were created. Why do they get this special treatment today? (PS: crc32b also implemented via hash() as well as having its own function.)
> > A new user being told "don't use sha1(), use hash() and pick from this > list" is more likely to say "ah, there's sha1, jolly good" than spend an > afternoon reading cryptography journals. There's no pit of success to > fall into. >
A new user skimming through the list of string functions is likely to see see "md5()" there and think "ah, there's a hash function, jolly good".

Rowan Tommins [IMSoP]

2 years ago
On 27 July 2024 00:58:17 BST, Morgan <weedpacket@varteg.nz> wrote:
> >I'm not talking about the MD5 or SHA1 algorithms or whether they should or shouldn't be used. I'm just talking about the functions themselves. md5(), md5_file(), sha1(), and sha1_file(). They only exist because there wasn't the generic hash algorithm extension when they were created.
I understand what is being claimed (and you're not the only one claiming it), I'm just not convinced it's true. I think they have standalone functions for the same reason we added str_contains and str_starts_with - because it's convenient to have straightforward functions for common use cases. The hash() function is like a 60-piece set of interchangeable screwdriver heads, which only professionals and enthusiasts need; md5() and sha1() are like the flat-head and Phillips screwdrivers that everyone has in a drawer somewhere. The thing that always surprises me is that PHP *doesn't* have a standalone function for SHA-256, which is the only other I've ever used. To continue the analogy, we're missing a Pozidriv screwdriver, so people are misusing the Phillips one. The RFC is suggesting that we take away their flat-head and Phillips screwdrivers, and leave them with the 60-piece set, and no instructions. My suggestion is we instead give them a Pozidriv screwdriver, and write some tips on how to use it correctly. Regards, Rowan Tommins [IMSoP]

Weedpacket

2 years ago
On 2024-07-28 00:36, Rowan Tommins [IMSoP] wrote:
> > > On 27 July 2024 00:58:17 BST, Morgan <weedpacket@varteg.nz> wrote: >> >> I'm not talking about the MD5 or SHA1 algorithms or whether they should or shouldn't be used. I'm just talking about the functions themselves. md5(), md5_file(), sha1(), and sha1_file(). They only exist because there wasn't the generic hash algorithm extension when they were created. > > I understand what is being claimed (and you're not the only one claiming it), I'm just not convinced it's true.
I'm just looking at the manual's version information about when the functions were introduced. Seems pretty unambiguous: md5, sha1, hash: versions 3, 4, and 5 (via PECL).
> I think they have standalone functions for the same reason we added
str_contains and str_starts_with - because it's convenient to have straightforward functions for common use cases.
>
Because there weren't any purpose-built functions that did the job, forcing users to use other functions in expensive ways for what is internally a pretty simple task. There is a purpose-built function for hashing.
> The hash() function is like a 60-piece set of interchangeable screwdriver heads, which only professionals and enthusiasts need; md5() and sha1() are like the flat-head and Phillips screwdrivers that everyone has in a drawer somewhere. > > The thing that always surprises me is that PHP *doesn't* have a standalone function for SHA-256, which is the only other I've ever used. >
Why a SHA2 algorithm? Why not a SHA3 one? How about standalone functions for both, and then when SHA4 comes along (as it inevitably will) another standalone function for one of its variants?
> To continue the analogy, we're missing a Pozidriv screwdriver, so people are misusing the Phillips one. The RFC is suggesting that we take away their flat-head and Phillips screwdrivers, and leave them with the 60-piece set, and no instructions. > > My suggestion is we instead give them a Pozidriv screwdriver, and write some tips on how to use it correctly. >
Or leave them them the 60-piece set (which includes flat-head and Phillips screwdrivers, so they're not being taken away), and write some tips on how to use it correctly.

Rob Landers

2 years ago
On Sun, Jul 28, 2024, at 00:14, Morgan wrote:
> On 2024-07-28 00:36, Rowan Tommins [IMSoP] wrote: > > > > > > On 27 July 2024 00:58:17 BST, Morgan <weedpacket@varteg.nz> wrote: > >> > >> I'm not talking about the MD5 or SHA1 algorithms or whether they should or shouldn't be used. I'm just talking about the functions themselves. md5(), md5_file(), sha1(), and sha1_file(). They only exist because there wasn't the generic hash algorithm extension when they were created. > > > > I understand what is being claimed (and you're not the only one claiming it), I'm just not convinced it's true. > > I'm just looking at the manual's version information about when the > functions were introduced. Seems pretty unambiguous: md5, sha1, hash: > versions 3, 4, and 5 (via PECL). > > > I think they have standalone functions for the same reason we added > str_contains and str_starts_with - because it's convenient to have > straightforward functions for common use cases. > > > Because there weren't any purpose-built functions that did the job, > forcing users to use other functions in expensive ways for what is > internally a pretty simple task. There is a purpose-built function for > hashing. > > > The hash() function is like a 60-piece set of interchangeable screwdriver heads, which only professionals and enthusiasts need; md5() and sha1() are like the flat-head and Phillips screwdrivers that everyone has in a drawer somewhere. > > > > The thing that always surprises me is that PHP *doesn't* have a standalone function for SHA-256, which is the only other I've ever used. > > > > Why a SHA2 algorithm? Why not a SHA3 one? How about standalone functions > for both, and then when SHA4 comes along (as it inevitably will) another > standalone function for one of its variants? > > > > To continue the analogy, we're missing a Pozidriv screwdriver, so people are misusing the Phillips one. The RFC is suggesting that we take away their flat-head and Phillips screwdrivers, and leave them with the 60-piece set, and no instructions. > > > > My suggestion is we instead give them a Pozidriv screwdriver, and write some tips on how to use it correctly. > > > Or leave them them the 60-piece set (which includes flat-head and > Phillips screwdrivers, so they're not being taken away), and write some > tips on how to use it correctly. > > > Regards, > > Rowan Tommins > > [IMSoP] >
I'd love to see a "hashing" namespace and all of these given their own functions with docblocks and manual pages instead of the current generic "god of hash" page which doesn't even list the hash functions available; you have to click on hash_algos and then look at the var_dump of hash algorithms. From there, you can google each one and try to understand what each one is good at and why you would use murmur3a over murmer3f, then try to figure out which one is the version that is compatible with javascript but not compatible with c# or maybe the other way around... (I recently got to go on that ride). If we are going to deprecate the standalone functions (see the sha1 page, which at least links to a page about the sha1 algorithm, or the md5 rfc, which links to the md5 rfc) we should seriously invest in documenting these hashing algorithms and explaining them. In the very least, link to their respective RFCs. — Rob

Tim Düsterhus

2 years ago
Hi On 7/28/24 00:25, Rob Landers wrote:
> I'd love to see a "hashing" namespace and all of these given their own functions with docblocks and manual pages instead of the current generic "god of hash" page which doesn't even list the hash functions available; you have to click on hash_algos and then look at the var_dump of hash algorithms. From there, you can google each one and try to understand what each one is good at and why you would use murmur3a over murmer3f, then try to figure out which one is the version that is compatible with javascript but not compatible with c# or maybe the other way around... (I recently got to go on that ride).
The problem with adding standalone functions for every algorithm is that it would result in a combinatorial explosion of available functions. Unless we want to make some of them "second class citizens" with reduced functionality or generally want to remove everything except for incremental hashing, we would need the following for each of them (with hash standing for the algorithm): - hash() - hash_hmac() - hash_file() - hash_init() - hash_hmac_init() - hash_update() (could also be a method) - hash_update_file() (could also be a method) - hash_update_stream() (could also be a method) - hash_final() (could also be a method) - hash_hkdf() - hash_pbkdf2() That clearly does not scale. See also my previous reply to Rowan regarding the documentation. It's clearly a topic for the farer future, but if / When tagged unions [1] make it in the language, I plan to propose an update allowing to pass an algorithm enum, including all the necessary options to the hash functions. This would make the algorithms more discoverable, easier to independently document and remove the footgun of needing to provide a matching untyped options array (which incidentally is also listed in the deprecation RFC as a footgun). [1] https://wiki.php.net/rfc/tagged_unions I'd also like to note that providing the hash algorithms by a generic interface is not particularly unusual. Here's two examples: - node.js: https://nodejs.org/api/crypto.html#class-hash - Python: https://docs.python.org/3/library/hashlib.html (which also takes a named constructor, which would be reasonably similar to my tagged-union proposal above). Other languages, such as Ruby or Golang, appear to use a Hash interface with appropriate methods, but I am not sure if this is a good fit for PHP given the way the documentation is structured, with a dedicated page for each and every function or method, and the fact that PHP does not provide for extension methods, which would require implementing the convenience functionality for each algorithm separately - or as standalone functions. Best regards Tim Düsterhus

Rowan Tommins [IMSoP]

2 years ago
On 27 July 2024 23:14:32 BST, Morgan <weedpacket@varteg.nz> wrote:
>Why a SHA2 algorithm? Why not a SHA3 one? How about standalone functions for both, and then when SHA4 comes along (as it inevitably will) another standalone function for one of its variants?
You tell me. As I have repeatedly said, I don't actually know anything about these algorithms. SHA-256 is the only one on the list which I've heard of, and I'm aware it's newer than SHA-1. I don't know why SHA-512 isn't "better", I don't know why nobody talks about SHA-3, and I don't know if one of the others in the list is absolutely amazing and should be everyone's default forever. As far as I can see, nobody, in this whole discussion, has actually stepped up and explained what users should be using, once we have taught them that MD5 and SHA-1 are bad.
>Or leave them them the 60-piece set (which includes flat-head and Phillips screwdrivers, so they're not being taken away), and write some tips on how to use it correctly.
So go ahead and write those tips. You don't need an RFC vote to improve the documentation. Here is my offer to those arguing in favour of this deprecation: If you show me a draft of a comprehensive improvement to the manual to explain how users should be choosing a hashing algorithm, I will consider changing my vote. I am also happy to help with proofreading, and working out how to format it into DocBook that fits nicely in the manual. As long as the deprecation rests on "somebody in the next 10 years might get round to improving the manual", my vote remains a firm No. Regards, Rowan Tommins [IMSoP]

Kamil Tekiela

2 years ago
On Sun, Jul 28, 2024, 08:42 Rowan Tommins [IMSoP] <imsop.php@rwec.co.uk> wrote:
> > > On 27 July 2024 23:14:32 BST, Morgan <weedpacket@varteg.nz> wrote: > > >Why a SHA2 algorithm? Why not a SHA3 one? How about standalone functions > for both, and then when SHA4 comes along (as it inevitably will) another > standalone function for one of its variants? > > You tell me. As I have repeatedly said, I don't actually know anything > about these algorithms. SHA-256 is the only one on the list which I've > heard of, and I'm aware it's newer than SHA-1. I don't know why SHA-512 > isn't "better", I don't know why nobody talks about SHA-3, and I don't know > if one of the others in the list is absolutely amazing and should be > everyone's default forever. > > As far as I can see, nobody, in this whole discussion, has actually > stepped up and explained what users should be using, once we have taught > them that MD5 and SHA-1 are bad. > > > >Or leave them them the 60-piece set (which includes flat-head and > Phillips screwdrivers, so they're not being taken away), and write some > tips on how to use it correctly. > > So go ahead and write those tips. You don't need an RFC vote to improve > the documentation. > > > Here is my offer to those arguing in favour of this deprecation: If you > show me a draft of a comprehensive improvement to the manual to explain how > users should be choosing a hashing algorithm, I will consider changing my > vote. > > I am also happy to help with proofreading, and working out how to format > it into DocBook that fits nicely in the manual. > > As long as the deprecation rests on "somebody in the next 10 years might > get round to improving the manual", my vote remains a firm No. > > > Regards, > Rowan Tommins > [IMSoP] >
I have voted yes only because I thought it's about removing inconsistent function alias. I can't see anything wrong with this hashing algorithms and I don't consider them unsafe. However, as someone pointed out this doesn't seem to be correct as the crc32 function isn't part of the depreciation proposal. I am confused now as to why we are trying to deprecate these functions at all. If it's about people confusing the hashing algorithms with password key stretching algorithms then that's not a valid reason. A red warning in the documentation should aid people in clearing this confusion.

Weedpacket

2 years ago
On 2024-07-28 18:42, Rowan Tommins [IMSoP] wrote:
> > > On 27 July 2024 23:14:32 BST, Morgan <weedpacket@varteg.nz> wrote: > >> Why a SHA2 algorithm? Why not a SHA3 one? How about standalone functions for both, and then when SHA4 comes along (as it inevitably will) another standalone function for one of its variants? > > You tell me. As I have repeatedly said, I don't actually know anything about these algorithms. SHA-256 is the only one on the list which I've heard of, and I'm aware it's newer than SHA-1. I don't know why SHA-512 isn't "better", I don't know why nobody talks about SHA-3, and I don't know if one of the others in the list is absolutely amazing and should be everyone's default forever. > > As far as I can see, nobody, in this whole discussion, has actually stepped up and explained what users should be using, once we have taught them that MD5 and SHA-1 are bad. > > >> Or leave them them the 60-piece set (which includes flat-head and Phillips screwdrivers, so they're not being taken away), and write some tips on how to use it correctly. > > So go ahead and write those tips. You don't need an RFC vote to improve the documentation. > > > Here is my offer to those arguing in favour of this deprecation: If you show me a draft of a comprehensive improvement to the manual to explain how users should be choosing a hashing algorithm, I will consider changing my vote. > > I am also happy to help with proofreading, and working out how to format it into DocBook that fits nicely in the manual. > > As long as the deprecation rests on "somebody in the next 10 years might get round to improving the manual", my vote remains a firm No. > > > Regards, > Rowan Tommins > [IMSoP]
Hey, all I'm doing is pointing out that the only reason those functions were standalone to start with is because when they were added they were the only ones around; they weren't introduced as "easier to use" alternatives to the more generic case. If hash() had been added in PHP with half a dozen different algorithms right at the beginning, would md5() and sha1() have been given special treatment? Possibly: MD5 (and later SHA1) got all the publicity at the time. Whether they are "bad" or "should not be used" has nothing to do with that. I understand that the RFC is hard on them because they are broken algorithms that don't have any advantages over others that have been added since and therefore the language shouldn't be encouraging their use by providing dedicated functions for them, I'm just pointing out that those dedicated functions are historical artefacts. I haven't seen an explanation of what makes them "easier to use": if you want to use md5() (for whatever reason: I don't care) it's not that hard to write hash("md5") instead. I just went through a file deduplication utility of mine and did exactly that. Yes, I am using MD5 as a message digest algorithm.

Tim Düsterhus

2 years ago
Hi On 7/28/24 08:42, Rowan Tommins [IMSoP] wrote:
>> Why a SHA2 algorithm? Why not a SHA3 one? How about standalone functions for both, and then when SHA4 comes along (as it inevitably will) another standalone function for one of its variants? > > You tell me. As I have repeatedly said, I don't actually know anything about these algorithms. SHA-256 is the only one on the list which I've heard of, and I'm aware it's newer than SHA-1. I don't know why SHA-512 isn't "better", I don't know why nobody talks about SHA-3, and I don't know if one of the others in the list is absolutely amazing and should be everyone's default forever. > > As far as I can see, nobody, in this whole discussion, has actually stepped up and explained what users should be using, once we have taught them that MD5 and SHA-1 are bad.
Let me attempt to give an explanation. As of today users should use in order of priority: 1. The hash function they need for interoperability: If a service provides a SHA-1 checksum, then there is no choice and SHA-1 needs to be used. 2. The hash function their security team requests them to use. 3. A function from the SHA-2 family, with SHA-256 being a good default choice, because that's the secure default choice across the industry. See also: https://news.ycombinator.com/item?id=14469614 and specifically https://news.ycombinator.com/item?id=14469730 ("there are hash cryptographers who think SHA-2 may never be broken"). To expand on (3): - SHA-256 and SHA-224 are literally the same, except for the initial values and the fact that SHA-224 returns fewer bits. - SHA-512, SHA-384, SHA-512/224 and SHA-512/256 are literally the same, except for the initial values and the fact that the latter 3 return fewer bits. - The main structure of SHA-512 and SHA-256 is the same, SHA-512 just uses 64-bit operations and larger chunks. Wikipedia explains this in detail: https://en.wikipedia.org/wiki/SHA-2#Pseudocode - SHA-512 and its variants are faster than SHA-256 and its variants, the reason is that SHA-256 is restricted to 32-bit operations. But: See below. - The truncated variants are immune to so-called length-extension attacks, but using a HMAC protects against that and thus is the recommended usage. As for the speed difference, I've created a (pending) PR to improve the speed of SHA-256 2x to 5x (depending on the input length), by leveraging the SHA-NI instruction set when available. When it's not available, the SSE2 implementation improves the speed by 1.3x: https://github.com/php/php-src/pull/15152 (Credit where credit is due: The implementation was written by Dr. Colin Percival, I just did the PHP integration).
>> Or leave them them the 60-piece set (which includes flat-head and Phillips screwdrivers, so they're not being taken away), and write some tips on how to use it correctly. > > So go ahead and write those tips. You don't need an RFC vote to improve the documentation. > > > Here is my offer to those arguing in favour of this deprecation: If you show me a draft of a comprehensive improvement to the manual to explain how users should be choosing a hashing algorithm, I will consider changing my vote. > > I am also happy to help with proofreading, and working out how to format it into DocBook that fits nicely in the manual. > > As long as the deprecation rests on "somebody in the next 10 years might get round to improving the manual", my vote remains a firm No. >
I'm seeing that you already found the issue discussing improvements to the documentation, but for reference for readers following along: https://github.com/php/doc-en/issues/3616 Please also see my previous email regarding the docs improvements I've already made: The examples for the hash() functions should now all use sha256 (matching the explanation above), please point out if I missed any. Best regards Tim Düsterhus PS: I know that life can get in the way, but as it fits the topic of your last paragraph I'd like to note that I don't believe you followed up regarding the documentation feedback back when the PHP 8.3 deprecation RFC (https://externals.io/message/120422#120601) happened.

Mike Schinkel

2 years ago
> On Jul 27, 2024, at 8:36 AM, Rowan Tommins [IMSoP] <imsop.php@rwec.co.uk> wrote: > On 27 July 2024 00:58:17 BST, Morgan <weedpacket@varteg.nz> wrote: >> >> I'm not talking about the MD5 or SHA1 algorithms or whether they should or shouldn't be used. I'm just talking about the functions themselves. md5(), md5_file(), sha1(), and sha1_file(). They only exist because there wasn't the generic hash algorithm extension when they were created. > > I understand what is being claimed (and you're not the only one claiming it), I'm just not convinced it's true. I think they have standalone functions for the same reason we added str_contains and str_starts_with - because it's convenient to have straightforward functions for common use cases. > > The hash() function is like a 60-piece set of interchangeable screwdriver heads, which only professionals and enthusiasts need; md5() and sha1() are like the flat-head and Phillips screwdrivers that everyone has in a drawer somewhere. > > The thing that always surprises me is that PHP *doesn't* have a standalone function for SHA-256, which is the only other I've ever used. > > To continue the analogy, we're missing a Pozidriv screwdriver, so people are misusing the Phillips one. The RFC is suggesting that we take away their flat-head and Phillips screwdrivers, and leave them with the 60-piece set, and no instructions. > > My suggestion is we instead give them a Pozidriv screwdriver, and write some tips on how to use it correctly.
I rise in support of this mindset. Some of us like to draw inspiration from other languages, and in that vein one of the things that makes Go such a joy to program in is the fact the Go team continues to add "convenience" functions with every new 6 month release. Many (all?) of the functions the Go team adds could have been written in "userland" but they represent such common use-cases that the Go team decided to make them easy and obvious. They even soft deprecate functions and structs that are not ideal and replace them with ones with better names and better signatures. If Go had started with the string and array functions PHP has today they would almost certainly replaced them by now, ~15 years into Go's tenure. It is a shame that PHP's culture is so hostile towards adding functionality that could also be added in userland, especially when that functionality would simplify and standardize algorithms that are non-obvious and/or too easy to implement incorrectly. If the PHP culture embraced moving common use-cases into core it would make PHP much more pleasurable to program in and make it much less likely that PHP programs would have bugs and/or security vulnerabilities.
> On Jul 27, 2024, at 6:14 PM, Morgan <weedpacket@varteg.nz> wrote: > Why a SHA2 algorithm? Why not a SHA3 one? How about standalone functions for both, and then when SHA4 comes along (as it inevitably will) another standalone function for one of its variants?
Yes. Yes, And Yes. And ideally within a `\PHP` namespace. -Mike P.S. But as we know a standardized `\PHP` namespace is apparently never going to happen although for the life of me I still cannot understand why not — and I was here during the voting down of that RFC ~4 years ago — given how so many other languages had done the equivalent.

Weedpacket

2 years ago
On 2024-07-28 15:54, Mike Schinkel wrote:
> > Many (all?) of the functions the Go team adds could have been written in "userland" but they represent such common use-cases that the Go team decided to make them easy and obvious. They even soft deprecate functions and structs that are not ideal and replace them with ones with better names and better signatures. If Go had started with the string and array functions PHP has today they would almost certainly replaced them by now, ~15 years into Go's tenure. > > It is a shame that PHP's culture is so hostile towards adding functionality that could also be added in userland, especially when that functionality would simplify and standardize algorithms that are non-obvious and/or too easy to implement incorrectly. If the PHP culture embraced moving common use-cases into core it would make PHP much more pleasurable to program in and make it much less likely that PHP programs would have bugs and/or security vulnerabilities. >
I, too, wish there was more willingness to add useful functions to core. Just saying "they can be implemented in userland" is a bit of a cop-out because, duh, PHP is Turing-complete. A lot of the existing array functions could be replicated by userland (ab)use of array_reduce, and yet no-one would suggest removing them, and if they'd been absent a lot of people would be asking for them. Anyone else wish that sort() took its argument by value instead of by reference? (Solvable in userland.) Or how about a named argument that allowed you to provide a key function to sort on instead of a comparator? (Solvable in userland.) Okay, the first change would break a lot, but an alternate sorted() function that did behave that way could be added.
>> On Jul 27, 2024, at 6:14 PM, Morgan <weedpacket@varteg.nz> wrote: >> Why a SHA2 algorithm? Why not a SHA3 one? How about standalone functions for both, and then when SHA4 comes along (as it inevitably will) another standalone function for one of its variants? > > Yes. Yes, And Yes. > > And ideally within a `\PHP` namespace. >
At that point you've got \PHP\sha3() instead of hash("sha3-?"), and now you've (a) lost the word "hash" indicator of what's going on, and (b) hidden the choice of "?" from the user. I'm not really seeing an improvement.

Mike Schinkel

2 years ago
> On Jul 28, 2024, at 9:19 PM, Morgan <Weedpacket@varteg.nz> wrote: > I, too, wish there was more willingness to add useful functions to core.
:-)
>>> On Jul 27, 2024, at 6:14 PM, Morgan <weedpacket@varteg.nz> wrote: >>> Why a SHA2 algorithm? Why not a SHA3 one? How about standalone functions for both, and then when SHA4 comes along (as it inevitably will) another standalone function for one of its variants? >> Yes. Yes, And Yes. >> And ideally within a `\PHP` namespace. > At that point you've got \PHP\sha3() instead of hash("sha3-?"), and now you've (a) lost the word "hash" indicator of what's going on, and (b) hidden the choice of "?" from the user. I'm not really seeing an improvement.
Well, your comments are based on assumptions that they would have to be implemented as you were envisioning when you wrote your reply. My "Yes. Yes. And Yes" was not intended to be full RFC that fleshed out all the considerations and proposed a specific implementation. IOW, there are definitely ways to address your criticisms if we are open-minded in what could be considered. :-)
> At that point you've got \PHP\sha3()
I'm sure you will find it ironic in hindsight like I do that you chose `sha3` (vs. `md5`) as the function to illustrate your argument about not having the word "hash" given how SHA is an acronym for "Secure Hash Algorithm." :-) By the same token, we could complain about how parse_url(), urlencode() and urldecode() all lost the word "resource." :-o Seriously though, some acronyms are well-known enough — or easily discovered enough — that we should be able to use them as function names without lamenting they are not spelled out. But if the concern is they are not grouped together as hashing functions than — had we had a `\PHP` namespace as an option — we could easily have: - \PHP\Hashing\md5() - \PHP\Hashing\sha1() - \PHP\Hashing\sha256() - \PHP\Hashing\sha3() - etc. Also, there is no reason we have to be exhaustive. The pareto principle is always one we should consider when deciding when anything should be elevated to having its own dedicated function.
> instead of hash("sha3-?")
The problem here is semantic information is encoded in a string rather than in a named symbol and thus is not recognized in the AST when parsing and requires a hack of diving into the string in order to validate. So typically, no type checking, no auto-complete, and potentially delayed error detection. Using strings where symbols would be better is a common wart in PHP — such as PHP not having a first-class type for class, interface or function — so we have to pass around names as non-typesafe strings instead. BTW, I asked ChatGPT to opine on the problems caused with strings-as-symbols from computer science and software engineering perspectives, and this is what it gave me: https://chatgpt.com/share/17d57881-c411-4b64-863a-d0692b4a4577
> and (b) hidden the choice of "?" from the user. I'm not really seeing an improvement.
What's wrong with something like? use PHP\Hashing\sha3; use PHP\Bits; ... $hash_224 = sha3($data,Bits::224); $hash_256 = sha3($data,Bits::256); $hash_384 = sha3($data,Bits::384); $hash_512 = sha3($data,Bits::512); The point I am trying to get across is that improving the developer experience is not a binary true or false endeavor. There are many ways to improve DX, but they all must start with a openness to consider doing it.
> On Jul 28, 2024, at 9:19 PM, Morgan <weedpacket@varteg.nz> wrote: > > Hey, all I'm doing is pointing out that the only reason those functions were standalone to start with is because when they were added they were the only ones around; they weren't introduced as "easier to use" alternatives to the more generic case. If hash() had been added in PHP with half a dozen different algorithms right at the beginning, would md5() and sha1() have been given special treatment? Possibly: MD5 (and later SHA1) got all the publicity at the time. > > I haven't seen an explanation of what makes them "easier to use": if you want to use md5() (for whatever reason: I don't care) it's not that hard to write hash("md5") instead. I just went through a file deduplication utility of mine and did exactly that. Yes, I am using MD5 as a message digest algorithm.
But just because they were historical artifacts doesn't mean that they should be frowned on, or removed. `echo` is also a historical artifact, but no one is arguing we should get rid of this: echo "Hello World"; And then require developers to use this instead: fprintf(STDOUT, "Hello World"); ¯\_(ツ)_/¯ -Mike

Rowan Tommins [IMSoP]

2 years ago
On 29 July 2024 02:19:23 BST, Morgan <weedpacket@varteg.nz> wrote:
>At that point you've got \PHP\sha3() instead of hash("sha3-?"), and now you've (a) lost the word "hash" indicator of what's going on, and (b) hidden the choice of "?" from the user. I'm not really seeing an improvement.
Once again, you're assuming users have any idea a) what the numbers in the SHA3 variants mean, and b) how to choose between them. I've seen plenty of uses of SHA-256 in the wild, and none of the other SHA2 variants. I don't know why, I presume people with far more knowledge than me have decided that is a good choice of variant. So when I'm looking for "something better than sha1()", I look for sha256(), remember it doesn't exist, and write hash('sha256', ...) If I'm doing it wrong, and should be making some calculation to choose SHA-382 or SHA-512, please let me know. But don't assume that just forcing me to put the algorithm name in qoute marks is going to make me know, or care, what the name actually means. Regards, Rowan Tommins [IMSoP]

Tim Düsterhus

2 years ago
Hi On 7/24/24 05:58, Peter Stalman wrote:
> is already quite specific. We're talking about end users who are rolling > their own security implementations and are unaware of the security risks > but somehow know how to use these functions without reading the > documentation and warnings.
No, we are talking about end users who are following tutorials that were written when PHP 4 was the most recent PHP version. We are also talking about end users who look at existing code bases for "inspiration", see md5() used, notice that the output looks random and use it, believing they know what they are doing, but in that process use it in a way that is insecure. As an example, using md5_file() to implement a cache buster is fine, but a less-experienced developer may believe that md5_file() uniquely identifies the file contents and use it in a way where strong collision-resistance against an adversary is required.
> On the other hand, who will be impacted by these deprecations? Potentially > everyone, as these are included in many projects and in many vendor > packages. It's busy work for the people who aren't affected. Sure, > eventually, it will all be sorted out as CI warnings slowly subside because > of this.
I'm positive that even existing projects written by experienced developers would benefit from re-checking if their use of MD5 and SHA-1 is actually safe instead of assuming that this is the case, when the specific functionality has been untouched for the last 10 years. Looking back at my own code, I'm seeing places where using SHA-1 is not strictly insecure, but where a stronger hash function nevertheless would have been more appropriate, if only to simplify code audits. I just used sha1(), because it was temptingly convenient compared to hash('sha256', …). Best regards Tim Düsterhus

Nick Lockheart

2 years ago
On Thu, 2024-07-25 at 17:33 +0200, Tim Düsterhus wrote:
> > As an example, using md5_file() to implement a cache buster is fine, > but a less-experienced developer may believe that md5_file() uniquely > identifies the file contents and use it in a way where strong > collision-resistance against an adversary is required. > > I'm positive that even existing projects written by experienced > developers would benefit from re-checking if their use of MD5 and > SHA-1 is actually safe instead of assuming that this is the case, > when the specific functionality has been untouched for the last 10 > years.
Isn't the philosophy of open source software "tools, not policy"? I'm in the process of refactoring an old framework and I just found a use of sha1(). It's being used to generate a unique resource lock. It doesn't need to be secure, just a fast and random UID.

Tim Düsterhus

2 years ago
Hi On 7/25/24 19:28, Nick Lockheart wrote:
> I'm in the process of refactoring an old framework and I just found a > use of sha1(). It's being used to generate a unique resource lock. It > doesn't need to be secure, just a fast and random UID.
SHA-1 is a deterministic algorithm, thus it is unable to generate a random UID. Whatever this code is doing can most likely be more reliably achieved in a different way. Best regards Tim Düsterhus

Peter Stalman

2 years ago
On Thu, Jul 25, 2024 at 8:33 AM Tim Düsterhus <tim@bastelstu.be> wrote:
> No, we are talking about end users who are following tutorials that were > written when PHP 4 was the most recent PHP version. > > We are also talking about end users who look at existing code bases for > "inspiration", see md5() used, notice that the output looks random and > use it, believing they know what they are doing, but in that process use > it in a way that is insecure. >
Hi Tim, How prevalent is this exactly? PHP 4 ended support in 2008. I think putting warning labels on these things in the docs is enough, but we can't go around locking up every kitchen knife just because there are some idiots out there who read a book from the 50s about the war. And like I said previously, this change isn't what is going to determine if those people will write good, reliable, secure code. If their learning insticast can't see past a blog tutorial from 20 years ago, not even to look up the function in the manual, they will not ever achieve that.
> I'm positive that even existing projects written by experienced > developers would benefit from re-checking if their use of MD5 and SHA-1 > is actually safe instead of assuming that this is the case, when the > specific functionality has been untouched for the last 10 years. >
You can say this about pretty much every software project in existence, regarding anything. I just don't think it's up to PHP to mandate these checks. If you want to create a fund for developers to go review their code on the clock, fine, but don't force it on them. Might as well deprecate everything each major version to force people to rewrite their projects to "current best practices". If I wanted to do that, I'd just use the JS framework of the month.
> Looking back at my own code, I'm seeing places where using SHA-1 is not > strictly insecure, but where a stronger hash function nevertheless would > have been more appropriate, if only to simplify code audits. I just used > sha1(), because it was temptingly convenient compared to hash('sha256', …). >
sha1 was the "proper" alternative to md5, until it wasn't. md5 superceeded crc32, which btw, why isn't that on the hit-list? You're using sha256? It's soooo outdated, use sha512 and key it with hmac, you casual /s SHA-1 is a deterministic algorithm, thus it is unable to generate a
> random UID. Whatever this code is doing can most likely be more reliably > achieved in a different way.
ALL hashing functions are deterministic. That's the whole point, and applies to sha256 just the same. You want to be able to hash the same content and get the same hash. Just the complexity and chance of collision changes. The reliability and security you are concerned with in this scenario really depends on what randomness you feed it. Thanks, Peter

Peter Stalman

2 years ago
On Thu, Jul 25, 2024 at 11:35 PM Peter Stalman <sarkedev@gmail.com> wrote:
> If their learning insticast >
*instincts. I should also clarify, I'm not against deprecations in general. However, the benefits should outweigh the costs. If something is getting unmaintainable, no longer supported, inherently insecure etc, those are all good reasons. `password_hash` as mentioned was a great addition, and should/did solve this very issue. Even someone reading a blog tutorial from 11 years ago would be able to see this used properly. But md5/sha1 are not bad functions, they do *exactly* what they say on the box. Being able to do the exact same thing by spelling the function slightly differently isn't even deprecating them, just deprecating an alias. They're only *bad* if used in a *bad way*, and that to me is not enough of a reason. Thanks, Peter

Gina P. Banyard

2 years ago
On Friday, 26 July 2024 at 08:09, Peter Stalman <sarkedev@gmail.com> wrote: > On Thu, Jul 25, 2024 at 11:35 PM Peter Stalman <sarkedev@gmail.com> wrote: > >> If their learning insticast > > *instincts. > > I should also clarify, I'm not against deprecations in general. However, the benefits should outweigh the costs. If something is getting unmaintainable, no longer supported, inherently insecure etc, those are all good reasons. `password_hash` as mentioned was a great addition, and should/did solve this very issue. Even someone reading a blog tutorial from 11 years ago would be able to see this used properly. > > But md5/sha1 are not bad functions, they do *exactly* what they say on the box. Being able to do the exact same thing by spelling the function slightly differently isn't even deprecating them, just deprecating an alias. They're only *bad* if used in a *bad way*, and that to me is not enough of a reason. Stephen Rees-Carter, a security expert that has performed countless security audits on Wordpress and Laravel websites, would like to disagree with the fact that it is not enough of a good reason. [1] A warning on a documentation page is useless, as nobody is forced to read it. Yet again the PHP community doesn't care about security of its users, current and future, and just prefers the convenience of needing to type less characters and not go back fix some code for better design. I am not sure why I was expecting something else, but I guess I am just disappointed. I suppose we are truly becoming Oracle. Sincerely, Gina P. Banyard [1] https://x.com/valorin/status/1816593881791860963

Reinis Rozitis

2 years ago
> Yet again the PHP community doesn't care about security of its users, current and future, and just prefers the convenience of needing to type less characters and not go back fix some code for better design. > > > Gina P. Banyard
If you describe it in such a dramatic fashion, then there is no reason to keep sha/md5 functionality in hash too? One could come up also with a different statement - "the PHP community doesn't care about backwards compatibility (in favor of questionable deprecations/removals)" (which at some point even borders with some "Karma farming" [1]) [1] https://socket.dev/blog/openssf-warns-of-reputation-farming-using-closed-github-issues-and-prs rr

Benjamin Eberlei

2 years ago
Am 26.07.2024, 12:03:53 schrieb Gina P. Banyard <internals@gpb.moe>:
> On Friday, 26 July 2024 at 08:09, Peter Stalman <sarkedev@gmail.com> > wrote: > > On Thu, Jul 25, 2024 at 11:35 PM Peter Stalman <sarkedev@gmail.com> wrote: > >> If their learning insticast >> > > *instincts. > > I should also clarify, I'm not against deprecations in general. However, > the benefits should outweigh the costs. If something is getting > unmaintainable, no longer supported, inherently insecure etc, those are all > good reasons. `password_hash` as mentioned was a great addition, and > should/did solve this very issue. Even someone reading a blog tutorial from > 11 years ago would be able to see this used properly. > > But md5/sha1 are not bad functions, they do *exactly* what they say on the > box. Being able to do the exact same thing by spelling the function > slightly differently isn't even deprecating them, just deprecating an > alias. They're only *bad* if used in a *bad way*, and that to me is not > enough of a reason. > > > Stephen Rees-Carter, a security expert that has performed countless > security audits on Wordpress and Laravel websites, would like to disagree > with the fact that it is not enough of a good reason. [1] > A warning on a documentation page is useless, as nobody is forced to read > it. > > Yet again the PHP community doesn't care about security of its users, > current and future, and just prefers the convenience of needing to type > less characters and not go back fix some code for better design. > > I am not sure why I was expecting something else, but I guess I am just > disappointed. > I suppose we are truly becoming Oracle. > > Sincerely, > > Gina P. Banyard > > [1] https://x.com/valorin/status/1816593881791860963 >
The only thing that removal of these functions would cause is a.) make people rant about php unnecessarily b.) 99.9% would counter the removal of these functions bys adding this kind of code in their bootstrap, maybe include a polyfill library via composer. if (!function_exists(‚md5‘)) { function md5($data) { return hash(‚md5‘, $data); }}

Christoph Becker

2 years ago
On 26.07.2024 at 12:03, Gina P. Banyard wrote:
> Stephen Rees-Carter, a security expert that has performed countless security audits on Wordpress and Laravel websites, would like to disagree with the fact that it is not enough of a good reason. [1] > A warning on a documentation page is useless, as nobody is forced to read it.
Right, but even a deprecation notice is likely to be ignored by those (either use the shut-up operator, or use hash("md5), or maybe a polyfill to support old PHP versions), so the deprecation wouldn't help in such cases. (I've recently seen a new release of a software which still uses <https://www.openwall.com/phpass/>. Apparently, the notice to prefer the password_*() API has been ignored or overlooked.) On the other hand, I'm quite confident that a deprecation could be useful for some developers, who would at least reconsider the use of md5/sha1 hashes, but just have overlooked this; although some static analysis should report respective issues. However, there is certainly code without any static analysis, where at least this discussion appears to be helpful, e.g. our php-sdk-binary-tools might reconsider their use of md5() and md5(uniqid())[2]. Note that I'm not against these deprecations, but I'm also not strongly in favor. I see valid arguments from both proponents and opponents.
> [1] https://x.com/valorin/status/1816593881791860963
[2] <https://github.com/php/php-sdk-binary-tools/issues/21> Cheers, Christoph

Larry Garfield

2 years ago
On Fri, Jul 26, 2024, at 11:11 AM, Christoph M. Becker wrote:
> On 26.07.2024 at 12:03, Gina P. Banyard wrote: > >> Stephen Rees-Carter, a security expert that has performed countless security audits on Wordpress and Laravel websites, would like to disagree with the fact that it is not enough of a good reason. [1] >> A warning on a documentation page is useless, as nobody is forced to read it. > > Right, but even a deprecation notice is likely to be ignored by those > (either use the shut-up operator, or use hash("md5), or maybe a polyfill > to support old PHP versions), so the deprecation wouldn't help in such > cases. > > (I've recently seen a new release of a software which still uses > <https://www.openwall.com/phpass/>. Apparently, the notice to prefer > the password_*() API has been ignored or overlooked.) > > On the other hand, I'm quite confident that a deprecation could be > useful for some developers, who would at least reconsider the use of > md5/sha1 hashes, but just have overlooked this; although some static > analysis should report respective issues. However, there is certainly > code without any static analysis, where at least this discussion appears > to be helpful, e.g. our php-sdk-binary-tools might reconsider their use > of md5() and md5(uniqid())[2]. > > Note that I'm not against these deprecations, but I'm also not strongly > in favor. I see valid arguments from both proponents and opponents. > >> [1] https://x.com/valorin/status/1816593881791860963 > > [2] <https://github.com/php/php-sdk-binary-tools/issues/21> > > Cheers, > Christoph
One thing to remind people about, the deprecations for md5(), sha1(), and uniqid() explicitly say they cannot be outright removed before PHP 10. That's at least 6 years away. That gives a loooooong time for documentation, tutorials, instructions, and code to be updated. That long deprecation period is the reason why I was comfortable voting yes. This isn't something that would happen tomorrow. It would be in at least two presidential elections from now. --Larry Garfield

Bilge

2 years ago
On Fri, 26 Jul 2024, 15:22 Larry Garfield, <larry@garfieldtech.com> wrote:
> > That long deprecation period is the reason why I was comfortable voting > yes. This isn't something that would happen tomorrow. It would be in at > least two presidential elections from now. >
Real elections or rigged elections? 😁

Rowan Tommins [IMSoP]

2 years ago
On Fri, 26 Jul 2024, at 15:20, Larry Garfield wrote:
> One thing to remind people about, the deprecations for md5(), sha1(), > and uniqid() explicitly say they cannot be outright removed before PHP > 10. That's at least 6 years away. That gives a loooooong time for > documentation, tutorials, instructions, and code to be updated.
It also gives a loooooong time for us to update that documentation *before* we start raising deprecation notices, so that there's a chance for someone to actually know what they're supposed to do about it. When I formally proposed deprecation of utf8_encode and utf8_decode, I didn't even post the RFC for discussion before I had written two documentation PRs, one to improve documentation even if the RFC failed; and another proposing the wording if it passed. In contrast, I voted against the deprecation of strftime() because no effort had been made to explain how users should replace it. Surprise surprise, nobody has spent any more effort in the 3.5 years since the deprecation passed, and the only advice in the documentation remains:
> Instead use the IntlDateFormatter::format() method.
On Fri, 26 Jul 2024, at 15:27, Christoph M. Becker wrote:
> Well, you are supposed to also check the hash_hmac() documentation...
Why would I, if I'm not using that function? For that matter, when should I be using that function? I'm not even being facetious here, I am genuinely lacking in relevant expertise, and the summary for hash_hmac() is meaningless unless you already know what it does:
> Generate a keyed hash value using the HMAC method
If the problem is that the web is full of bad documentation, find or write some GOOD documentation. Then, work out how best to signpost users to that documentation. Deprecating md5() and sha1() does neither. Regards,
-- Rowan Tommins [IMSoP]

Unnamed Person

2 years ago
On 26-7-2024 16:20, Larry Garfield wrote:
> > One thing to remind people about, the deprecations for md5(), sha1(), and uniqid() explicitly say they cannot be outright removed before PHP 10. That's at least 6 years away. That gives a loooooong time for documentation, tutorials, instructions, and code to be updated.
Considering that the hash() function was introduced in PHP 5.1.2 (January 2006) and password_*() in PHP 5.5 (June 2013), I don't share your optimism about tutorials being updated within six years ....

Rowan Tommins [IMSoP]

2 years ago
On 26 July 2024 11:03:53 BST, "Gina P. Banyard" <internals@gpb.moe> wrote:
>Yet again the PHP community doesn't care about security of its users, current and future, and just prefers the convenience of needing to type less characters and not go back fix some code for better design.
This is a gross misrepresentation of what people are saying. I am in favour of the *aim* of educating users to use better hashing functions, but I don't agree that the proposed deprecation is the right way to achieve that aim. Maybe some people who already know SHA1 is outdated will be prompted to say "huh, I hadn't realised we used it there, let's add a backlog task to migrate to something else". But just as likely they'll do that during a security audit anyway. The people you really want to reach, those who don't know much about it, will do a find-and-replace from "sha1(" to "hash('sha1', " and gain nothing. The deprecation *might* make sense alongside introducing some new functions that we want people to discover instead, but on its own, I don't think the benefits outweigh the costs. Regards, Rowan Tommins [IMSoP]

Mike Schinkel

2 years ago
> On Jul 26, 2024, at 6:03 AM, Gina P. Banyard <internals@gpb.moe> wrote: > > Stephen Rees-Carter, a security expert that has performed countless security audits on Wordpress and Laravel websites, would like to disagree with the fact that it is not enough of a good reason. [1]
People who work in emergency rooms think that motorcycles are the ultimate evil and should be banned, because emergency room workers are the ones who see all of the carnage of the small percent who wreck their motorcycles, and they see none of motorcycling's upsides. Similarly, security experts see everything through the lens of security issues, because they see the problems FAR more often than everyone else. And as security expertise, they don't see code through other lenses where security is not an issue. Not saying the input of a security experts is not useful, but one man's input is only one side of the story, just like emergency room workers vs. motorcycles.
> Yet again the PHP community doesn't care about security of its users, current and future, and just prefers the convenience of needing to type less characters and not go back fix some code for better design.
Explicitly stated, that is a straw man argument, which Rowan already called out. Different people weight risks, costs, and benefits differently, and just because you might feel your approach for addressing security concerns should eclipse anyone else's approach and all other concerns does not mean your approach exists at the peak of the moral high ground. Every time PHP deprecates software it places the burden and the cost of remediation on anyone and everyone who continues to use the software that requires the deprecated items. Those who are zealously security-first generally dismiss those burdens and cost of remediation — because they do not have to be burdened by then nor pay the costs — and so they shift them to everyone, including those who are using functions properly. Those more pragmatic balance that burden and cost with the potential burden and costs that deprecating can impose. And in the case of md5() where public code on GitHub shows almost 1 million uses, that imposed burden and cost is pretty large. But ignoring the burden and cost, is it strongly arguable that deprecating md5() wouldn't even fix the security problems in most cases as those you most want to force to fix things will the ones more likely to just create a polyfill and move on. As many has already stated on this thread. Kudos to Tim Düsterhus for identifying https://www.phptutorial.net/php-tutorial/php-csrf/ and https://www.php-einfach.de/php-tutorial/die-wichtigsten-php-funktionen/ but his takeaway for an action item was less inspiring. He argued those articles support deprecations when it seems to me the more obvious takeaway after finding those articles would be to reach out to those websites — as well as others publishing insecure information — and provide them with updated content to replace the content they are currently publishing with content that is promotes secure practices. Getting those websites updated is likely to have far more positive impact for new PHP developers learning to do things "the right way" then forcing them to update their code where they'll likely just use hash("md5"). Further, rather than shift the burden of remediation to everyone else, why not write a crawler that can automatically and proactively submit PRs to all the code out there using md5(), etc. so that most people only need to accept the PR to update their code, and make is available as a CLI for internal use? I know it is not that simple to remediate, but who do you expect will know how to do that better than those on PHP internals. Certainly not most GitHub repo owners. Besides, the PR could say "Review your code we are proposing the change, and if you are confident that your uses are secure then do not apply this PR. But if you are not sure they are secure then just apply the PR, test it, and then you'll certainly be safer." Rather than just take a low-effort, feel-good action for security theater, if the PHP community REALLY cares about security for its users it would take a pro-active, higher-effort approach to addressing the concern. The WordPress community implemented at least one successful technology-supported "marketing" campaign to move its user base in the past, one of which was the "Serve Happy" campaign to get people to update their version of PHP (how ironic!): https://make.wordpress.org/core/features/servehappy/ Why not create a working group to promote a "SERVE SECURELY" campaign modeled after WordPress's "Serve Happy" campaign, and do your best to help people remediate their security issues? Hell, imagine the free press and industry-wide exposure that such as campaign would provide as a way to educate PHP programmers on the dangers of misusing md5() and other insecure approaches? It is also strongly possible you could even get significant sponsorship for such as campaign to pay for some more developer time to address the problem. It almost certainly could be seen as a feel-good thing for big industry players to support. Frankly, if the pro-deprecation voters in the PHP community are not willing to pursue an initiative that proactively seeks to help users remediate and educate users about security concerns then I would argue *they* do not really care about security of PHP users but instead are only willing to paying lip service to it. #fwiw TLDR;? Use a carrot, not a stick. -Mike

Mike Schinkel

2 years ago
> On Jul 26, 2024, at 9:11 PM, Mike Schinkel <mike@newclarity.net> wrote: > > Kudos to Tim Düsterhus for identifying https://www.phptutorial.net/php-tutorial/php-csrf/ and https://www.php-einfach.de/php-tutorial/die-wichtigsten-php-funktionen/ but his takeaway for an action item was less inspiring. He argued those articles support deprecations when it seems to me the more obvious takeaway after finding those articles would be to reach out to those websites — as well as others publishing insecure information — and provide them with updated content to replace the content they are currently publishing with content that is promotes secure practices. Getting those websites updated is likely to have far more positive impact for new PHP developers learning to do things "the right way" then forcing them to update their code where they'll likely just use hash("md5").
As a quick follow up: https://www.phptutorial.net/contact/ And: https://www.php-einfach.de/author/nils/ https://www.nils-reimers.de/contact/ -Mike

Peter Stalman

2 years ago
On Fri, Jul 26, 2024 at 6:14 PM Mike Schinkel <mike@newclarity.net> wrote:
> Frankly, if the pro-deprecation voters in the PHP community are not > willing to pursue an initiative that proactively seeks to help users > remediate and educate users about security concerns then I would argue > *they* do not really care about security of PHP users but instead are only > willing to paying lip service to it. #fwiw > > TLDR;? Use a carrot, not a stick. > > -Mike >
Thanks Mike, I see you have already made a very similar point to the one I just sent out, but quite a bit more eloquently! The deprecation arguments seem almost academic to me. Thanks, Peter

Tim Düsterhus

2 years ago
Hi On 7/26/24 08:35, Peter Stalman wrote:
> How prevalent is this exactly? PHP 4 ended support in 2008. I think > putting warning labels on these things in the docs is enough, but we can't > go around locking up every kitchen knife just because there are some idiots > out there who read a book from the 50s about the war.
I just Googled "PHP tutorial" and found https://www.phptutorial.net/ as the second search result, which considers itself to be "the modern PHP tutorial". I've clicked at the CSRF section (https://www.phptutorial.net/php-tutorial/php-csrf/) and what do I find:
> $_SESSION['token'] = md5(uniqid(mt_rand(), true));
*Exactly* the md5-uniqid construction that is called out as unsafe in the RFC and used in a security context. Further down on the first page I find https://www.tutorialspoint.com/php/php_mysql_login.htm, which does not even hash the passwords that are stored within the database. At least it's using `mysqli_real_escape_string()`. Then I have the German php-einfach.de, which on https://www.php-einfach.de/php-tutorial/die-wichtigsten-php-funktionen/ ("the most important PHP functions") lists md5() and sha1() as an important function, but does not mention hash() at all. I'm sure I would find quite a few more, but I believe those already support the point I was trying to make.
> And like I said previously, this change isn't what is going to determine if > those people will write good, reliable, secure code. If their learning > insticast can't see past a blog tutorial from 20 years ago, not even to > look up the function in the manual, they will not ever achieve that.
I think you are expecting a little too much from a beginner that is following "the modern PHP tutorial" if you expect them to critically question whether the tutorial is actually good or not. They are likely already struggling with syntax and explaining the difference between "if" and "while". You wouldn't believe how often I've heard the term "if-Schleife" (if loop) in German.
>> I'm positive that even existing projects written by experienced >> developers would benefit from re-checking if their use of MD5 and SHA-1 >> is actually safe instead of assuming that this is the case, when the >> specific functionality has been untouched for the last 10 years. >> > > You can say this about pretty much every software project in existence, > regarding anything. I just don't think it's up to PHP to mandate these > checks. If you want to create a fund for developers to go review their > code on the clock, fine, but don't force it on them. Might as well
A deprecation is not forcing anything. It's an indicator that whatever you are doing might not be the best current practice. You are free to ignore it and in this specific case you are not even at the risk of removal, because the RFC does not propose the removal.
>> Looking back at my own code, I'm seeing places where using SHA-1 is not >> strictly insecure, but where a stronger hash function nevertheless would >> have been more appropriate, if only to simplify code audits. I just used >> sha1(), because it was temptingly convenient compared to hash('sha256', …). >> > > sha1 was the "proper" alternative to md5, until it wasn't. md5
Right, technology advances and security is a moving target. What is the point you are trying to make?
> superceeded crc32, which btw, why isn't that on the hit-list?
CRC32 does not claim to be a cryptographically secure hash algorithm. Its use case is completely different.
> You're using sha256? It's soooo outdated, use sha512 and key it with hmac, > you casual /s
I'm seeing the sarcasm indicator, but I'm compelled to point out that SHA-256 and SHA-512 are both SHA-2. If one is broken, it is likely that the other is as well.
> SHA-1 is a deterministic algorithm, thus it is unable to generate a >> random UID. Whatever this code is doing can most likely be more reliably >> achieved in a different way. > > > ALL hashing functions are deterministic. That's the whole point, and
Yes, that's why I asked how they are using a hash function to get a random result.
> applies to sha256 just the same. You want to be able to hash the same > content and get the same hash. Just the complexity and chance of > collision changes. The reliability and security you are concerned with in > this scenario really depends on what randomness you feed it.
My point was that if you already have randomness then you don't need to pair it with a hash function. You don't gain any randomness by passing it through a hash function. Just convert the randomness to a readable representation using bin2hex, base64_encode or by using Randomizer::getBytesFromString(). Best regards Tim Düsterhus

Rob Landers

2 years ago
On Fri, Jul 26, 2024, at 13:58, Tim Düsterhus wrote:
> Hi > > On 7/26/24 08:35, Peter Stalman wrote: > > How prevalent is this exactly? PHP 4 ended support in 2008. I think > > putting warning labels on these things in the docs is enough, but we can't > > go around locking up every kitchen knife just because there are some idiots > > out there who read a book from the 50s about the war. > > I just Googled "PHP tutorial" and found https://www.phptutorial.net/ as > the second search result, which considers itself to be "the modern PHP > tutorial". > > I've clicked at the CSRF section > (https://www.phptutorial.net/php-tutorial/php-csrf/) and what do I find: > > > $_SESSION['token'] = md5(uniqid(mt_rand(), true)); > > *Exactly* the md5-uniqid construction that is called out as unsafe in > the RFC and used in a security context.
In regards to hashing, this is likely fine; for now. There still isn't an arbitrary pre-image attack on md5 (that I'm aware of). Can you create a random file with a matching hash? Yes, in a few seconds, on modern hardware. But you cannot yet make it have arbitrary contents in our lifetime. The NSA probably has something like this though, but if so, this isn't widely known. That being said, this is just randomly creating a random id without leaking it's internal construction, no different than putting an md5 in a UUID-v8. The real issue here is the use of uniqid() and rand(), making it quite likely (at scale, at least) that a session id will overlap with another session id. — Rob

Tim Düsterhus

2 years ago
HI On 7/26/24 14:50, Rob Landers wrote:
>>> $_SESSION['token'] = md5(uniqid(mt_rand(), true)); >> >> *Exactly* the md5-uniqid construction that is called out as unsafe in >> the RFC and used in a security context. > > In regards to hashing, this is likely fine; for now. There still isn't an arbitrary pre-image attack on md5 (that I'm aware of). Can you create a random file with a matching hash? Yes, in a few seconds, on modern hardware. But you cannot yet make it have arbitrary contents in our lifetime. The NSA probably has something like this though, but if so, this isn't widely known.
Neither collision-, nor pre-image resistance is relevant here. The attack vector is a brute force attack / an attacker guessing the token rather than the token's contents.
> That being said, this is just randomly creating a random id without leaking it's internal construction, no different than putting an md5 in a UUID-v8. The real issue here is the use of uniqid() and rand(), making it quite likely (at scale, at least) that a session id will overlap with another session id.
The point is that it showcases a fundamental misunderstanding of what MD5 (or really any other hash algorithm) does for you. The application of the MD5 does not make the token more random or more unique or whatever positive adjective you would like to use. It would be equally strong (or rather weak) if the output of `uniqid(mt_rand(), true)` was used directly. As per Kerckhoffs's principle, the security of the algorithm must not rely on the attacker not knowing how it's implemented. Given how prevalent constructions like the above are, an attacker could make an educated guess about how it looks like and match their own token against a precomputed table to find out if it matches. Best regards Tim Düsterhus

Rob Landers

2 years ago
On Fri, Jul 26, 2024, at 15:02, Tim Düsterhus wrote:
> HI > > On 7/26/24 14:50, Rob Landers wrote: > >>> $_SESSION['token'] = md5(uniqid(mt_rand(), true)); > >> > >> *Exactly* the md5-uniqid construction that is called out as unsafe in > >> the RFC and used in a security context. > > > > In regards to hashing, this is likely fine; for now. There still isn't an arbitrary pre-image attack on md5 (that I'm aware of). Can you create a random file with a matching hash? Yes, in a few seconds, on modern hardware. But you cannot yet make it have arbitrary contents in our lifetime. The NSA probably has something like this though, but if so, this isn't widely known. > > Neither collision-, nor pre-image resistance is relevant here. The > attack vector is a brute force attack / an attacker guessing the token > rather than the token's contents.
You do realize that GUID and md5 hashes are the same size? One does not simply "guess" a GUID or an md5 hash. gravatar used md5 until a couple of years ago, and had millions? billions? of emails addresses and zero collisions.
> > That being said, this is just randomly creating a random id without leaking it's internal construction, no different than putting an md5 in a UUID-v8. The real issue here is the use of uniqid() and rand(), making it quite likely (at scale, at least) that a session id will overlap with another session id. > > The point is that it showcases a fundamental misunderstanding of what > MD5 (or really any other hash algorithm) does for you. The application > of the MD5 does not make the token more random or more unique or > whatever positive adjective you would like to use. It would be equally > strong (or rather weak) if the output of `uniqid(mt_rand(), true)` was > used directly.
Yes, it does, but probably not how you think. It would be much weaker to leak the internal construction (uniqid(mt_rand(), true)) because then someone could literally guess a working id if they knew when the id was generated (depending on the size of mt_rand, rate limits, etc). By wrapping it in an md5, it is literally unguessable how it is constructed, but the construction is still crap in this case.
> > As per Kerckhoffs's principle, the security of the algorithm must not > rely on the attacker not knowing how it's implemented. Given how > prevalent constructions like the above are, an attacker could make an > educated guess about how it looks like and match their own token against > a precomputed table to find out if it matches.
In this example, an ID is being constructed. If it needs uniqueness, the ID is being constructed incorrectly, but if you could argue that a GUID would fit the bill here, md5 has more "entropy" than a GUIDv4. But due to how the md5 is constructed, it actually has less entropy. So, I think we both can agree that the construction is crap. However, the usage of md5 doesn't matter here. If it really bothers you, craft a GUIDv8 from it. But to Kerckhoffs's principle, that is in regards to encryption ... this is not encryption. — Rob

Nick Lockheart

2 years ago
>
> In regards to hashing, this is likely fine; for now. There still > isn't an arbitrary pre-image attack on md5 (that I'm aware of). Can > you create a random file with a matching hash? Yes, in a few seconds, > on modern hardware. But you cannot yet make it have arbitrary > contents in our lifetime. The NSA probably has something like this > though, but if so, this isn't widely known.
The NSA likely owns "Let's Encrypt" and can therefore MitM every TLS site on the internet.
> If the problem is that the web is full of bad documentation, find or > write some GOOD documentation. Then, work out how best to signpost > users to that documentation. Deprecating md5() and sha1() does > neither.
This. I'm not going to quote everything, but I read through the comments from today and would say this: 1) This seems very much like the people in support of these deprecations are trying to push PHP to enforce *policy* on developers, rather than simply providing tools. 2) PHP should provide good documentation, but should not try to force every user to do something "best practice" by renaming functions. 3) If a websever/host updates the PHP version and the code breaks, the last thing a dev is looking for is "what's the best practice to refactor this code". The dev is thinking, "our site is down, the boss/client is angry, what's the fastest band-aid I can slap on this to get the site up again". Thus: Provide tools, not policy. Provide good documentation.
-- Nick

Rowan Tommins [IMSoP]

2 years ago
On Fri, 26 Jul 2024, at 12:58, Tim Düsterhus wrote:
> I think you are expecting a little too much from a beginner that is > following "the modern PHP tutorial" if you expect them to critically > question whether the tutorial is actually good or not. They are likely > already struggling with syntax and explaining the difference between > "if" and "while". You wouldn't believe how often I've heard the term > "if-Schleife" (if loop) in German.
I think you are expecting a little too much from a beginner if you think they will see the message "md5() is deprecated", and research up to date advice on hashing algorithms, rather than asking ChatGPT how to make the code work, and replacing it with "hash('md5', ...)".
> CRC32 does not claim to be a cryptographically secure hash algorithm. > Its use case is completely different.
As an inexperienced user looking at the PHP manual for hash() and hash_algos(), how would I know that? It's right there in the list, just after something called "adler32".
> I'm seeing the sarcasm indicator, but I'm compelled to point out that > SHA-256 and SHA-512 are both SHA-2. If one is broken, it is likely that > the other is as well.
Again, you know that, but do the users you're trying to help by deprecating sha1()? I'm a reasonably experienced developer, and I have no idea why SHA-512 would exist if it's not in some way "better" than SHA-256. Regards,
-- Rowan Tommins [IMSoP]

Christoph Becker

2 years ago
On 26.07.2024 at 15:13, Rowan Tommins [IMSoP] wrote:
> On Fri, 26 Jul 2024, at 12:58, Tim Düsterhus wrote: > >> CRC32 does not claim to be a cryptographically secure hash algorithm. >> Its use case is completely different. > > As an inexperienced user looking at the PHP manual for hash() and hash_algos(), how would I know that? It's right there in the list, just after something called "adler32".
Well, you are supposed to also check the hash_hmac() documentation, where a changelog entry for 7.2.0 states: | Usage of non-cryptographic hash functions (adler32, crc32, crc32b, | fnv132, fnv1a32, fnv164, fnv1a64, joaat) was disabled. Or maybe we should fix <https://github.com/php/doc-en/issues/3616>. Cheers, Christoph

Tim Düsterhus

2 years ago
Hi On 7/26/24 15:13, Rowan Tommins [IMSoP] wrote:
> On Fri, 26 Jul 2024, at 12:58, Tim Düsterhus wrote: >> I think you are expecting a little too much from a beginner that is >> following "the modern PHP tutorial" if you expect them to critically >> question whether the tutorial is actually good or not. They are likely >> already struggling with syntax and explaining the difference between >> "if" and "while". You wouldn't believe how often I've heard the term >> "if-Schleife" (if loop) in German. > > I think you are expecting a little too much from a beginner if you think they will see the message "md5() is deprecated", and research up to date advice on hashing algorithms, rather than asking ChatGPT how to make the code work, and replacing it with "hash('md5', ...)".
I am not expecting that from a beginner. I am expecting two things: 1. That the beginner switches to a tutorial that does not emit any error messages or warnings, because they realize that the tutorial is not as good as it claims to be. 2. That (1) leads to the outdated tutorials falling out of favor with regards to search engines or alternatively that the outdated tutorials are updated to no longer be outdated.
>> CRC32 does not claim to be a cryptographically secure hash algorithm. >> Its use case is completely different. > > As an inexperienced user looking at the PHP manual for hash() and hash_algos(), how would I know that? It's right there in the list, just after something called "adler32". >
I expect the inexperienced user to look at existing tutorials or code snippets, rather than the reference documentation: If they are inexperienced, they would not even know what to look for in the documentation. That also ties into (1): I hope that the deprecation results in better tutorials / making bad tutorials less attractive. Of course that doesn't mean we shouldn't improve the documentation, and I'm seeing that Christoph and Jim already started doing so.
>> I'm seeing the sarcasm indicator, but I'm compelled to point out that >> SHA-256 and SHA-512 are both SHA-2. If one is broken, it is likely that >> the other is as well. > > Again, you know that, but do the users you're trying to help by deprecating sha1()? I'm a reasonably experienced developer, and I have no idea why SHA-512 would exist if it's not in some way "better" than SHA-256. >
See above. Also: Really any choice from the SHA-2 or SHA-3 family is better than both MD5 and SHA-1 and I would expect users to generally gravitate towards things they have heard about before and you really need to try hard not to have heard about SHA-256 before. Wikipedia is also helpful regarding this topic. Best regards Tim Düsterhus

Peter Stalman

2 years ago
On Fri, Jul 26, 2024, 04:58 Tim Düsterhus <tim@bastelstu.be> wrote:
> > I just Googled "PHP tutorial" and found https://www.phptutorial.net/ as > the second search result, which considers itself to be "the modern PHP > tutorial". > > I've clicked at the CSRF section > (https://www.phptutorial.net/php-tutorial/php-csrf/) and what do I find: > > > $_SESSION['token'] = md5(uniqid(mt_rand(), true)); > > *Exactly* the md5-uniqid construction that is called out as unsafe in > the RFC and used in a security context. > > Further down on the first page I find > https://www.tutorialspoint.com/php/php_mysql_login.htm, which does not > even hash the passwords that are stored within the database. At least > it's using `mysqli_real_escape_string()`. > > Then I have the German php-einfach.de, which on > https://www.php-einfach.de/php-tutorial/die-wichtigsten-php-funktionen/ > ("the most important PHP functions") lists md5() and sha1() as an > important function, but does not mention hash() at all. > > I'm sure I would find quite a few more, but I believe those already > support the point I was trying to make. >
I don't think the examples you provided support the argument for deprecating these functions. If anything, they highlight the real problem: outdated tutorials being prominently featured in search results. As you mentioned, the MySQL login one doesn't even use a hashing function, so deprecating md5 and sha1 functions would do nothing to fix that! And how are these the top results? Are you telling me that the PHP community can't create better websites and SEO than these ancient tutorials? If someone encounters a problem because they can't use the md5() function, they're likely to Google it and find a simple workaround like "just paste this code and it'll work again." mentioned above. That would be just like this deprecation proposal: identifying the wrong solution to the actual problem. The real question is, why aren't there better, more up-to-date resources easily available for someone wanting to learn PHP in 2024? We're the PHP community, we should be leading the web and SEO. Yet most people looking to get into webdev today aren't reaching for PHP. I've seen recent videos where developers are positively surprised by PHP's modern features. But can we blame them for being surprised if these are the top tutorials out there? Deprecating these functions isn't addressing the core issue. The focus should be on making it easy for new learners to access up-to-date tutorials. Thanks, Peter

Matthew Weier O'Phinney

2 years ago
On Fri, Jul 19, 2024 at 12:41 PM Gina P. Banyard <internals@gpb.moe> wrote:
> Hello internals, > > I have opened the vote for the mega deprecation RFC: > https://wiki.php.net/rfc/deprecations_php_8_4 > > Reminder, each vote must be submitted individually. > > > Best regards, > > > Gina P. Banyard >
The section "Deprecate using a single underscore ''_'' as a class name" indicates that probably the primary reason to deprecate it is a potential future conflict in the pattern matching RFC, where it can be used as a wildcard. However, I see no mention of this character as a wildcard anywhere in that RFC. Can somebody clarify?
-- Matthew Weier O'Phinney mweierophinney@gmail.com https://mwop.net/ he/him

Larry Garfield

2 years ago
On Tue, Jul 23, 2024, at 1:42 PM, Matthew Weier O'Phinney wrote:
> On Fri, Jul 19, 2024 at 12:41 PM Gina P. Banyard <internals@gpb.moe> wrote: >> Hello internals, >> >> I have opened the vote for the mega deprecation RFC: >> https://wiki.php.net/rfc/deprecations_php_8_4 >> >> Reminder, each vote must be submitted individually. >> >> >> Best regards, >> >> >> Gina P. Banyard > > > The section "Deprecate using a single underscore ''_'' as a class name" > indicates that probably the primary reason to deprecate it is a > potential future conflict in the pattern matching RFC, where it can be > used as a wildcard. > > However, I see no mention of this character as a wildcard anywhere in that RFC. > > Can somebody clarify?
The pattern matching RFC previously listed _ as a wildcard character. In the discussion a month ago, someone pointed out that `mixed` already serves that exact purpose, so having an extra wildcard was removed. However, a few people indicated a desire to have an explicit wildcard _ anyway, even if it's redundant, as it's a more common and standard approach in other languages. We've indicated that we are open to making that an optional secondary vote in the pattern matching RFC if there's enough interest (it would be trivial), though I haven't bothered to add it to the RFC text yet. Having _ available could also be used in other "wildcard" or "ignore this" cases, like exploding into a list assignment or similar, though I don't believe that has been fully explored. That's the context/background here. Whether that encourages you to vote for or against that section I leave as an exercise for the reader. --Larry Garfield

Christoph Becker

2 years ago
On 23.07.2024 at 16:04, Larry Garfield wrote:
> On Tue, Jul 23, 2024, at 1:42 PM, Matthew Weier O'Phinney wrote: > >> On Fri, Jul 19, 2024 at 12:41 PM Gina P. Banyard <internals@gpb.moe> wrote: >> >>> I have opened the vote for the mega deprecation RFC: >>> https://wiki.php.net/rfc/deprecations_php_8_4 >> >> The section "Deprecate using a single underscore ''_'' as a class name" >> indicates that probably the primary reason to deprecate it is a >> potential future conflict in the pattern matching RFC, where it can be >> used as a wildcard. >> >> However, I see no mention of this character as a wildcard anywhere in that RFC. >> >> Can somebody clarify? > > The pattern matching RFC previously listed _ as a wildcard character. > > In the discussion a month ago, someone pointed out that `mixed` already serves that exact purpose, so having an extra wildcard was removed. > > However, a few people indicated a desire to have an explicit wildcard _ anyway, even if it's redundant, as it's a more common and standard approach in other languages. We've indicated that we are open to making that an optional secondary vote in the pattern matching RFC if there's enough interest (it would be trivial), though I haven't bothered to add it to the RFC text yet. > > Having _ available could also be used in other "wildcard" or "ignore this" cases, like exploding into a list assignment or similar, though I don't believe that has been fully explored. > > That's the context/background here. Whether that encourages you to vote for or against that section I leave as an exercise for the reader.
Well, I wonder how that is supposed to work. Assuming the underscore would be used as wildcard in a class name context, that could only be done after using that character as class name is no longer allowed. So that would have to wait for the next major PHP version (at least). Note that I'm not worried about no longer being able to use an underscore as class name, but rather that this introduces another inconsistency to our indentifiers. Disallowing an underscore as function name is obviously off the table, thanks to gettext. Christoph

Larry Garfield

2 years ago
On Tue, Jul 23, 2024, at 2:41 PM, Christoph M. Becker wrote:
> On 23.07.2024 at 16:04, Larry Garfield wrote: > >> On Tue, Jul 23, 2024, at 1:42 PM, Matthew Weier O'Phinney wrote: >> >>> On Fri, Jul 19, 2024 at 12:41 PM Gina P. Banyard <internals@gpb.moe> wrote: >>> >>>> I have opened the vote for the mega deprecation RFC: >>>> https://wiki.php.net/rfc/deprecations_php_8_4 >>> >>> The section "Deprecate using a single underscore ''_'' as a class name" >>> indicates that probably the primary reason to deprecate it is a >>> potential future conflict in the pattern matching RFC, where it can be >>> used as a wildcard. >>> >>> However, I see no mention of this character as a wildcard anywhere in that RFC. >>> >>> Can somebody clarify? >> >> The pattern matching RFC previously listed _ as a wildcard character. >> >> In the discussion a month ago, someone pointed out that `mixed` already serves that exact purpose, so having an extra wildcard was removed. >> >> However, a few people indicated a desire to have an explicit wildcard _ anyway, even if it's redundant, as it's a more common and standard approach in other languages. We've indicated that we are open to making that an optional secondary vote in the pattern matching RFC if there's enough interest (it would be trivial), though I haven't bothered to add it to the RFC text yet. >> >> Having _ available could also be used in other "wildcard" or "ignore this" cases, like exploding into a list assignment or similar, though I don't believe that has been fully explored. >> >> That's the context/background here. Whether that encourages you to vote for or against that section I leave as an exercise for the reader. > > Well, I wonder how that is supposed to work. Assuming the underscore > would be used as wildcard in a class name context, that could only be > done after using that character as class name is no longer allowed. So > that would have to wait for the next major PHP version (at least). > > Note that I'm not worried about no longer being able to use an > underscore as class name, but rather that this introduces another > inconsistency to our indentifiers. Disallowing an underscore as > function name is obviously off the table, thanks to gettext. > > Christoph
I think someone checked and found no examples of someone using _ as a class name, so the impact of removing it and/or using it for something else would be nearly nil. That may still push _ as a wildcard out to a future version, but I leave that up to others. As I said, I don't have strong feelings either way. --Larry Garfield

Matthew Weier O'Phinney

2 years ago
On Tue, Jul 23, 2024 at 9:06 AM Larry Garfield <larry@garfieldtech.com> wrote:
> On Tue, Jul 23, 2024, at 1:42 PM, Matthew Weier O'Phinney wrote: > > On Fri, Jul 19, 2024 at 12:41 PM Gina P. Banyard <internals@gpb.moe> > wrote: > >> Hello internals, > >> > >> I have opened the vote for the mega deprecation RFC: > >> https://wiki.php.net/rfc/deprecations_php_8_4 > >> > >> Reminder, each vote must be submitted individually. > >> > >> > >> Best regards, > >> > >> > >> Gina P. Banyard > > > > > > The section "Deprecate using a single underscore ''_'' as a class name" > > indicates that probably the primary reason to deprecate it is a > > potential future conflict in the pattern matching RFC, where it can be > > used as a wildcard. > > > > However, I see no mention of this character as a wildcard anywhere in > that RFC. > > > > Can somebody clarify? > > The pattern matching RFC previously listed _ as a wildcard character. > > In the discussion a month ago, someone pointed out that `mixed` already > serves that exact purpose, so having an extra wildcard was removed. > > However, a few people indicated a desire to have an explicit wildcard _ > anyway, even if it's redundant, as it's a more common and standard approach > in other languages. We've indicated that we are open to making that an > optional secondary vote in the pattern matching RFC if there's enough > interest (it would be trivial), though I haven't bothered to add it to the > RFC text yet. > > Having _ available could also be used in other "wildcard" or "ignore this" > cases, like exploding into a list assignment or similar, though I don't > believe that has been fully explored. >
Can you provide examples of what that usage would look like? And the question I have really is, does this actually _require_ using "_", or could another token be used for such matches?
-- Matthew Weier O'Phinney mweierophinney@gmail.com https://mwop.net/ he/him

Larry Garfield

2 years ago
On Tue, Jul 23, 2024, at 4:00 PM, Matthew Weier O'Phinney wrote:
>> However, a few people indicated a desire to have an explicit wildcard _ anyway, even if it's redundant, as it's a more common and standard approach in other languages. We've indicated that we are open to making that an optional secondary vote in the pattern matching RFC if there's enough interest (it would be trivial), though I haven't bothered to add it to the RFC text yet. >> >> Having _ available could also be used in other "wildcard" or "ignore this" cases, like exploding into a list assignment or similar, though I don't believe that has been fully explored. > > Can you provide examples of what that usage would look like? And the > question I have really is, does this actually _require_ using "_", or > could another token be used for such matches?
Hypothetical pattern matching example: $foo is ['a' => int, 'b' => $b, 'c' => mixed]; That would assert that there's 3 keys. "a" may be any integer (but only an integer), "b" can be anything and will be captured to a variable, and "c" must be defined but we don't care what it is. The suggestion is to basically alias _ to "mixed" for pattern purposes: $foo is ['a' => int, 'b' => $b, 'c' => _]; As "there's a var here but I don't care what it is, ignore it" is a common meaning of _ in other languages. But that would need to be disambiguated from a pattern saying "c must be an instance of the class _". Technically any symbol/set of symbols could be used there (as it's just an alias to mixed, which has the exact same effect), but _ is a common choice in other languages. In theory, that could be expanded in the future to something like (note: this hasn't been seriously discussed that I know of, I'm just spitballing randomly): [$a, $b, _] = explode(':', 'foo:bar:baz'); To assign $a = "foo", $b to "bar", and just ignore "baz". Which might cause parser issues if _ is a legal class name, I'm not sure. There's probably other "ignore this" cases we could come up with, but I haven't actually thought about it. Again, whether any of the above is a compelling argument or not I leave as an exercise for the reader. --Larry Garfield

Mike Schinkel

2 years ago
> On Jul 23, 2024, at 12:26 PM, Larry Garfield <larry@garfieldtech.com> wrote: > > Hypothetical pattern matching example: > > $foo is ['a' => int, 'b' => $b, 'c' => mixed]; > > That would assert that there's 3 keys. "a" may be any integer (but only an integer), "b" can be anything and will be captured to a variable, and "c" must be defined but we don't care what it is. > > The suggestion is to basically alias _ to "mixed" for pattern purposes: > > $foo is ['a' => int, 'b' => $b, 'c' => _]; > > As "there's a var here but I don't care what it is, ignore it" is a common meaning of _ in other languages. But that would need to be disambiguated from a pattern saying "c must be an instance of the class _". Technically any symbol/set of symbols could be used there (as it's just an alias to mixed, which has the exact same effect), but _ is a common choice in other languages.
I do not see this use-case as compelling. `mixed` is perfectly sufficient and using `_` for a data types just gives two ways to do the same. Not that multiple ways to do the same thing is necessarily wrong, but I think it needs a better justification than just to save characters. Besides, it has the potential to confuse people as to its exact meaning whereas `mixed` does not. OTOH, if you really want to say characters — albeit not as many — then choose `any`, which is certainly less likely to be confusing and has an analog in Go, TypeScript, and Python, at least. Also, AFAIK, few (no?) other languages actually allow for using `_` for types, they only allow using them for variables. I know that to be the case for Go, and if I understand the docs correctly it is also true for Rust, Zig, Haskell and Swift, with caveats for Rust. - Rust allows underscore for type inference, e.g.: let x: _ = "Hello, world!"; - Also for a Generics' type placeholder, e.g.: let vec: Vec<_> = vec![1, 2, 3]; - But as for Rust pattern matching, the underscore is only used for values, not types. For any other languages, I cannot say.
> In theory, that could be expanded in the future to something like (note: this hasn't been seriously discussed that I know of, I'm just spitballing randomly): > > [$a, $b, _] = explode(':', 'foo:bar:baz');
This is actually where a "blank" variable represented by `_` actually makes a lot of sense. It is also how Go and Zig use them and effectively also how Rust, Haskell, and Swift use them. Unlike for types where we have `mixed`, there is no current globally consistent alternate to using a blank variable in PHP. The only option is to use an arbitrary name that other developers won't know the intention of unless the developer adds comments to the effect. In summary, although I don't have strong feelings about deprecating classes named `_`, I do not think the arguments made for disallowing them actually have any analog in any other languages so I question if there is valid justification for the deprecation. #jmtcw #fwiw -Mike

Gina P. Banyard

2 years ago
On Friday, 19 July 2024 at 19:39, Gina P. Banyard <internals@gpb.moe> wrote:
> Hello internals, > > I have opened the vote for the mega deprecation RFC: > https://wiki.php.net/rfc/deprecations_php_8_4 > > Reminder, each vote must be submitted individually.
2 days late but I have now closed the vote for all the RFC proposals. The ones that have been accepted will be implemented in due course. Best regards, Gina P. Banyard