Make WordPress Core

Opened 7 years ago

Closed 7 years ago

Last modified 6 years ago

#39499 closed enhancement (wontfix)

Migrate Password Hashing from 8192 rounds of salted MD5 to Argon2i v1.3

Reported by: paragoninitiativeenterprises's profile paragoninitiativeenterprises Owned by:
Milestone: Priority: normal
Severity: normal Version: 4.8
Component: Security Keywords:
Focuses: Cc:

Description

This is a sibling ticket to #39309 to greatly enhance the security of WordPress blogs the whole world over. It is not a duplicate of #21022 as that pertains to bcrypt.

Currently, WordPress uses 8192 rounds of Salted MD5 for password hashing: https://paragonie.com/blog/2016/08/on-insecurity-popular-open-source-php-cms-platforms#wordpress-password-storage

Salted MD5 is a weaker version of PBKDF2-MD5 with a low number of iterations and no GPU resistance. Migrating to bcrypt has been proposed (see #21022 for details), but bcrypt support in PHP < 5.3.7 isn't consistent; nor is it secure.

Proposal: Switch to Argon2i for Password Hashing

Argon2i is available in libsodium, accessible via the crypto_pwhash API.

Paragon Initiative Enterprises is currently developing a pure-PHP polyfill for the libsodium extension (currently available in PECL) compatible with PHP 5.2.4+. Once it's complete, stable, and independently audited by third party security experts, WordPress should consider adopting this library to facilitate a long-overdue security enhancement.

Our library is available on Github: https://github.com/paragonie/sodium_compat

Migrating Legacy Hashes

We recommend the process outlined here: https://paragonie.com/blog/2016/02/how-safely-store-password-in-2016#legacy-hashes

In more WP-specific terms: On the next automatic update, immediately re-hash all users' password hashes with Argon2i. Store the old salt (not the full hash) alongside the Argon2i hash, and provide some way of identifying legacy hashes.

When a user authenticates:

  • Is this a legacy hash?
    • Pre-hash with Phpass (in portable mode, as per WordPress today)
    • Use the $P$ hash as the password for crypto_pwhash_str_verify()
    • If the password validates, calculate a new crypto_pwhash_str() of the user's given password and store that in the database. Clear the old salt and any legacy password indicators.
  • Otherwise, just use crypto_pwhash_str_verify()

WordPress should absolutely NOT consider an "opportunistic upgrade" strategy. Otherwise, you're no better than Yahoo: https://nakedsecurity.sophos.com/2016/12/15/yahoo-breach-ive-closed-my-account-because-it-uses-md5-to-hash-my-password/

Change History (7)

This ticket was mentioned in Slack in #core-passwords by paragonie. View the logs.


7 years ago

#2 @mmaunder
7 years ago

Moving to a GPU resistant hashing algorithm would be a huge improvement for WP. So as a general idea I fully support this and I think many others do too.

Argon2 is a relatively new algorithm. Are any other widely used projects using it yet? Would WP be the early adopter here?

This provides some benchmarks: https://github.com/P-H-C/phc-winner-argon2

I'm interested in what a real-world configuration/usage of Argon2 would look like that would be WP hosting environment friendly. I would say that a few reasonable assumptions are:

You will only have 1 CPU core available.
You should not use more than 10MB of memory.
Hashing should not take longer than 0.5 seconds or it affects the user experience.

Is it possible to use Argon2 within these constraints and still be GPU resistant?

Last question: Can you talk about your choice of Argon2i over Argon2d? Keep in mind your audience includes non-infosec and non-crypto people.

Thanks for starting the conversation Scott!!

#3 @paragoninitiativeenterprises
7 years ago

Are any other widely used projects using it yet?

Not many that I haven't had a hand in.

Would WP be the early adopter here?

No, the early adopter would be Airship: https://paragonie.com/project/airship

You will only have 1 CPU core available.
You should not use more than 10MB of memory.
Hashing should not take longer than 0.5 seconds or it affects the user experience.

Well, I was going to shoot for at least (16 MB of memory, 0.125 seconds) on a reasonable hardware configuration.

Libsodium's CRYPTO_PWHASH_*_INTERACTIVE constants define 32 MB of memory usage and 4 rounds on 1 core.

We must always use at least 3 rounds.

If most setups can get away with the _INTERACTIVE configuration, I'd recommend that. But as an insurance/stopgap, an early adoption at a lower memory threshold would be better than sticking with MD5 forever.

#4 @paragoninitiativeenterprises
7 years ago

Last question: Can you talk about your choice of Argon2i over Argon2d? Keep in mind your audience includes non-infosec and non-crypto people.

To be clear: This was the libsodium choice, not one of my own design.

Argon2d is great for offline devices (e.g. generating your Bitcoin private key from a password and salt). Argon2i is better for interactive use (i.e. password validation in a web application).

Formally: Argon2d uses memory-dependent addressing to make it harder to crack on a GPU. Argon2i uses memory-independent addressing, to resist timing attacks.

With Argon2d: Which bytes are used in the next step depend entirely on the output of the current step. It's expensive to attempt to reproduce the execution path without using a ton of memory.

With Argon2i: The time it takes to hash a password doesn't vary depending on the input. It doesn't leak information through side-channels.

Cache-timing side-channels matter on software that communicates over the Internet. That makes Argon2i a more conservative choice.

Version 0, edited 7 years ago by paragoninitiativeenterprises (next)

#5 @paragoninitiativeenterprises
7 years ago

  • Resolution set to wontfix
  • Status changed from new to closed

Upon closer analysis, this may be a doomed prospect.

Argon2i via \Sodium\crypto_pwhash() (with the *_INTERACTIVE constants) takes about 100ms to calculate on my machine (and requires 32 MB of memory).

My PHP implementation is already taking several seconds with the same memory/iteration parameters. Libsodium itself won't allow memory values below 32 MB, so weakening security is not possible for compatibility.

I'll update this ticket if I can get the performance reasonable, but a better idea might be just to polyfill bcrypt so PHP 5.2.4 - 5.3.7 can use password_compat.

#6 @netweb
7 years ago

  • Milestone Awaiting Review deleted

#7 @my1xt
6 years ago

Sorry for Blasting a comment on this old bug, but I wanted to drop a few words on this.

Actually There are a few things that can be updated and a few things making this proposal way out of proportion for a general-purpose thing like WP.

The Good thing is Argon2i isnt the only usable way of Argon2 by now, but we also have argon2id which is a lot more resistant to tradeoffs and wouldnt need multiple rounds but can apparently survive with just one, making this a lot less of an issue.

but still I think this idea is probably doomed for a about 1 and a half (argon2i) 2 and a half years (argon2id).

The problem with the approach planned would be to use Sodium, which obviously has the problem of, well, using sodium, or rather a PHP extension, which probably isnt widely deployed in general. that would lead to people use older WP versions which all have their own problems and kill auto update, because an update would totally kill off their sites if we would make sodium a requirement.

Instead going to ax off PHP<5.3.7 and going for bcrypt would probably be the best way to start and after that starting to move everything towards only supported versions of PHP, which, 2 years after PHP7.2 for argon2i and 2 years after 7.3 for Argon2id would mean all versions supported by PHP would have those hashes available in password_hash without even relying on a core extension, that might not be enabled.

although even that will probably take a while since there are still 2.9-10.4% of WP Users on a version which cant do bcrypt properly (2.9 are on 5.2 which definitely isnt going to workm while 7.5 are on 5.3, but without stats about the patch version we wouldnt have accurate numbers).

Maybe the numbers shift a bit again when PHP5 as a whole gets dropped at the end of this year.

Note: See TracTickets for help on using tickets.