Introduce kernel parameters allowing to specify password hashing algorithm and options

Created on 14 June 2025, 4 months ago

Problem/Motivation

The constructor of the PhpPassword hashing class accepts two optional parameters for the algorithm and hashing options. These parameters were chosen to match the PHP password_hash() parameters.

However, the function signature changed a couple of times before and after the original issue went in ( πŸ“Œ Replace custom password hashing library with PHP password_hash() Fixed ). Especially the $algo paramater only became nullable with PHP 8.0. For lower versions it had to be specified explicitly. At the time when the original issue went in, it was unpractical to expose the $algorithm constructor parameter as a kernel parameter. Simply because the default is a PHP constant, and that directly encoded the default algorithm as a string.

Since the $algo parameter is nullable nowadays, it is possible to expose the password hashing algorithm as a kernel parameter (null by default). This makes it easier for contrib and custom code to switch it to something else.

Steps to reproduce

Proposed resolution

Modify the constructor of PhpPassword to make the $algorithm parameter nullable, parameter references to the @password@ service definition and add two new kernel parameters:

  password.algorithm: ~
  password.options: []

Remaining tasks

User interface changes

Introduced terminology

API changes

Data model changes

Release notes snippet

✨ Feature request
Status

Active

Version

11.0 πŸ”₯

Component

base system

Created by

πŸ‡¨πŸ‡­Switzerland znerol

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Merge Requests

Comments & Activities

  • Issue created by @znerol
  • πŸ‡¨πŸ‡­Switzerland znerol
  • Pipeline finished with Failed
    4 months ago
    #522378
  • πŸ‡¨πŸ‡­Switzerland znerol
  • πŸ‡¨πŸ‡­Switzerland znerol
  • Pipeline finished with Failed
    4 months ago
    #522420
  • Pipeline finished with Failed
    4 months ago
    #522421
  • πŸ‡¨πŸ‡­Switzerland znerol

    Postponing on #3530235

  • πŸ‡«πŸ‡·France andypost

    CI images has both now

  • πŸ‡«πŸ‡·France andypost

    constructor only changes looks ok but it require container rebuild so needs update hook to request it, otherwise looks ready

  • πŸ‡¨πŸ‡­Switzerland znerol

    Thanks! Added an update hook.

  • Pipeline finished with Success
    4 months ago
    Total: 673s
    #524011
  • πŸ‡«πŸ‡·France andypost

    Thank you! CR is ready and the code too

  • πŸ‡¬πŸ‡§United Kingdom longwave UK

    Looks good to me, added a question about the update hook though.

  • πŸ‡¨πŸ‡­Switzerland znerol
  • Pipeline finished with Success
    4 months ago
    Total: 835s
    #541920
  • πŸ‡ΊπŸ‡ΈUnited States benjifisher Boston area

    Thanks for working on this issue. As I said on πŸ“Œ Properly handle that PHP bcrypt passwords are truncated to 72 bytes Active ,

    • I think this issue is the right way to go. Let's make it easy to use something other than bcrypt.
    • I think we should implement hook_requirements. We could do that as part of this issue or on #3536662.

    @longwave mentioned #2951046 to me. I am adding that as a related issue. Once that issue gets in, we can use something like !php/const PASSWORD_ARGON2ID in services.yml.

  • The Needs Review Queue Bot β†’ tested this issue. It no longer applies to Drupal core. Therefore, this issue status is now "Needs work".

    This does not mean that the patch necessarily needs to be re-rolled or the MR rebased. Read the Issue Summary, the issue tags and the latest discussion here to determine what needs to be done.

    Consult the Drupal Contributor Guide β†’ to find step-by-step guides for working with issues.

  • πŸ‡¨πŸ‡­Switzerland znerol
  • Pipeline finished with Success
    3 months ago
    Total: 670s
    #565852
  • πŸ‡«πŸ‡·France andypost

    I think requirements exposition of available hashes should be done in πŸ“Œ Properly handle that PHP bcrypt passwords are truncated to 72 bytes Active

    This issue exactly about to add support of argon and make it configurable, also soduim open doors to fast encrypted storage

  • πŸ‡¦πŸ‡ΊAustralia larowlan πŸ‡¦πŸ‡ΊπŸ.au GMT+10

    Left a minor question on the MR, fine to self RTBC

  • Pipeline finished with Success
    3 months ago
    #567599
  • πŸ‡¨πŸ‡­Switzerland znerol
    % ./vendor/bin/drush updb
     -------- ------------------ ------------- ------------------------------------
      Module   Update ID          Type          Description
     -------- ------------------ ------------- ------------------------------------
      system   password_kernel_   post-update   Rebuild the container after adding
               parameters                       new kernel parameters.
     -------- ------------------ ------------- ------------------------------------
    
    
     β”Œ Do you wish to run the specified pending updates? ───────────┐
     β”‚ Yes                                                          β”‚
     β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
    
    >  [notice] Update started: system_post_update_password_kernel_parameters
    >  [notice] Update completed: system_post_update_password_kernel_parameters
     [success] Finished performing updates.
    
  • πŸ‡ΊπŸ‡ΈUnited States drumm NY, US
  • πŸ‡ΊπŸ‡ΈUnited States neclimdul Houston, TX
  • πŸ‡ͺπŸ‡ͺEstonia ram4nd Tallinn
  • πŸ‡ΊπŸ‡ΈUnited States xjm
  • πŸ‡¦πŸ‡ΊAustralia larowlan πŸ‡¦πŸ‡ΊπŸ.au GMT+10

    @catch mentioned on slack

    don't think we need the empty post update on 3530186 because the default deployment identifier is based on \Drupal::version() and that's used in the container cache key. Doesn't really matter except it's less updates to remove when the time comes later. Mentioning in here because it went around a couple of times in the issue already

    Sorry for the runaround @znerol - can we remove that?

    We discussed this on the security team triage call in relation to πŸ“Œ Properly handle that PHP bcrypt passwords are truncated to 72 bytes Active - in particular that issue's reintroduction of the legacy phpass hashing for users with passwords longer than 72 bytes where the bcypt algorithm is in use.

    We decided we would instead prefer to move forward with this issue rather than what is effectively backwards with the reuse of phpass.

    We decided we would prefer to have the hook_requirements from that issue added here that:

    1. Shows a warning if the site only has bcrypt hashing available - detailing the 72 char/null byte risk with links to PHP.net regarding argon2
    2. Shows an error if the site has argon2 available but it isn't the default hashing algorithm

    I've manually tested that changing the hashing algorithm with existing users successfully rehashes their password in the new algorithm when they next login.

    I will mark the other issue as postponed on this one in the meantime.

    We also decided to re-purpose the php_password contrib module as a BC layer as the changes in this MR can't be backported.

    So this issue here will fix 11.3/10.6 and the contrib module will decorate the core service to bring this functionality to stable branches. The contrib module will have a forward compatibility layer that will be a no-op if it detects the container param added here - which would indicate the module is no longer needed as the site has updated to a version with this feature.

    With this issue and the contrib module ready, we will likely issue a PSA

    Crediting those involved in the discussion.

  • πŸ‡«πŸ‡·France andypost

    Removed post update hook

  • Pipeline finished with Success
    3 months ago
    Total: 735s
    #568474
  • πŸ‡¬πŸ‡§United Kingdom catch

    I think this needs:

    We decided we would prefer to have the hook_requirements from that issue added here that:

    Shows a warning if the site only has bcrypt hashing available - detailing the 72 char/null byte risk with links to PHP.net regarding argon2
    Shows an error if the site has argon2 available but it isn't the default hashing algorithm

    from #24.

  • πŸ‡¦πŸ‡ΊAustralia larowlan πŸ‡¦πŸ‡ΊπŸ.au GMT+10

    forwards compatibility layer is ready for review here https://git.drupalcode.org/project/php_password/-/merge_requests/6/diffs

  • πŸ‡¨πŸ‡­Switzerland znerol

    Added the requirement checks.

    Should we add a config and a proper UI to make these requirement checks more actionable? Maybe an additional Password fieldset in admin/config/people/accounts?

  • Pipeline finished with Failed
    3 months ago
    Total: 765s
    #570034
  • Pipeline finished with Success
    3 months ago
    Total: 433s
    #570061
  • πŸ‡¨πŸ‡­Switzerland znerol
  • πŸ‡«πŸ‡·France andypost

    I think transition to new hash should be done in follow-up, moreover old passwords makes no sense when Argon2 available

  • Status changed to Needs review 2 months ago
  • πŸ‡ΊπŸ‡ΈUnited States benjifisher Boston area

    @andypost:

    What transition do you mean?

    Passwords should be rehashed automatically when users log in with their passwords. The Q&A for the Password Compatiblity module β†’ apply to this situation, too.

    From #24 here:

    I've manually tested that changing the hashing algorithm with existing users successfully rehashes their password in the new algorithm when they next login.

    @znerol:

    Should we add a config and a proper UI to make these requirement checks more actionable?

    I have been thinking that this is something that would be managed through settings.yml, not the UI. In fact, I like the idea of relying on something in the filesystem. Otherwise, XSS can be exploited to change the password hashing algorithm.

    If you still want to provide a configuration form, then let's have a followup issue for further discussion.

  • πŸ‡¨πŸ‡­Switzerland znerol

    By the time this ships, every single site which upgrades will generate a requirements warning. The requirements warning points to a CR which contains example code for a service provider class. The example code for the service provider class is there because I think we shouldn't instruct people to manually add a container parameter for this. The reason I think so is that it is safer to use the \PASSWORD_* constants instead of let people attempt to hard code the exact password algorithm into a yml file by hand.

    Regarding the UI: This could be just a password_argon2 module with its own requirements check and a service provider which adds the password.algoritm: argon2id container parameter.

  • πŸ‡ΊπŸ‡ΈUnited States benjifisher Boston area

    How is it easier to use the \PASSWORD_* constants? Using drush php:

    > password_algos()
    = [
        "2y",
        "argon2i",
        "argon2id",
      ]
    

    I think we should let people set a hashing algorithm in services.yml.

    Have we considered what to do if the service parameter is invalid? Do we fall back to PASSWORD_DEFAULT? Is there a requirements check (for the status page)?

  • πŸ‡¨πŸ‡­Switzerland znerol

    In the MR. There is a requirements check for unknown / misspelled password algorithms.

    When an algorithm is configured which isn't available on the system, attempting to create new password hash will result in a fatal:

    Fatal error: Uncaught ValueError: password_hash(): Argument #2 ($algo) must be a valid password hashing algorithm in Command line code on line 1

    Verifying existing hashes still works (assuming the hashing algorithm is available on the system).

  • Pipeline finished with Failed
    2 months ago
    Total: 127s
    #573608
  • Pipeline finished with Success
    2 months ago
    Total: 412s
    #573624
  • Pipeline finished with Failed
    about 2 months ago
    Total: 197s
    #586691
  • πŸ‡¦πŸ‡ΊAustralia larowlan πŸ‡¦πŸ‡ΊπŸ.au GMT+10

    I updated the change record to add an example of setting the parameters via a services.yml file
    Fixed a couple of typos in the MR.
    Going to try to simplify the hook_requirements a bit

  • πŸ‡¦πŸ‡ΊAustralia larowlan πŸ‡¦πŸ‡ΊπŸ.au GMT+10

    Moved the requirements checking logic to a new private method which enabled me to reduce the cyclic complexity/nesting.

    As this was just moving things around I think I'm still ok to mark this as RTBC. So doing so.

  • πŸ‡¦πŸ‡ΊAustralia larowlan πŸ‡¦πŸ‡ΊπŸ.au GMT+10
  • Pipeline finished with Success
    about 2 months ago
    #586698
  • πŸ‡¦πŸ‡ΊAustralia larowlan πŸ‡¦πŸ‡ΊπŸ.au GMT+10

    https://www.drupal.org/project/php_password/releases/3.0.0 β†’ has been released with the forward compat layer

  • πŸ‡«πŸ‡·France andypost

    +1 RTBC as no code change just move and s/know/known test change

  • πŸ‡¬πŸ‡§United Kingdom catch

    With the change record, is password.options really required? It looks like it defaults to empty array anyway and could be omitted.

    Also should some documentation be going into default.services.yml? The issue summary mentions this but there's no changes in the MR.

  • πŸ‡¦πŸ‡ΊAustralia larowlan πŸ‡¦πŸ‡ΊπŸ.au GMT+10

    Updated the CR to make it clear that password.options is optional.
    Added docs to core.services.yml and default.services.yml

  • Pipeline finished with Success
    about 2 months ago
    Total: 734s
    #587496
  • πŸ‡³πŸ‡ΏNew Zealand quietone

    The link in the message is to a change record. Should that be to a documentation page instead?

  • πŸ‡¬πŸ‡§United Kingdom catch

    I think we should consider removing the hook_requirements() from here altogether, and sorting that out in πŸ“Œ Properly handle that PHP bcrypt passwords are truncated to 72 bytes Active where we have multiple options that might not require it (at least on all sites, we still might end up with something similar on some sites).

  • πŸ‡³πŸ‡ΏNew Zealand quietone

    I hadn't read the other issue, πŸ“Œ Properly handle that PHP bcrypt passwords are truncated to 72 bytes Active , until now. I like the suggestion in #43.

  • πŸ‡¨πŸ‡­Switzerland znerol

    @catch that would basically reverse the outcome from the security team triage call (#24). I'd rather try to go into a direction which doesn't send us running around in circles.

  • Pipeline finished with Success
    about 2 months ago
    Total: 488s
    #589674
  • πŸ‡¨πŸ‡­Switzerland znerol

    Added a draft MR with an Password Argon2 module. This is implementing the idea from #32.

  • Pipeline finished with Failed
    about 2 months ago
    Total: 250s
    #589733
  • Pipeline finished with Success
    about 2 months ago
    Total: 643s
    #589739
Production build 0.71.5 2024