Skip to content

Support PBKDF2 for password hashing - #17316

Merged
michaelklishin merged 9 commits into
mainfrom
loic-password-hashing
Sep 9, 2026
Merged

michaelklishin merged 9 commits into
mainfrom
loic-password-hashing

Conversation

@lhoguin

@lhoguin lhoguin commented Sep 4, 2026

Copy link
Copy Markdown
Contributor

Two things in this PR, a new hashing algorithm, and a management UI improvement. See individual commits.

@lhoguin

lhoguin commented Sep 4, 2026

Copy link
Copy Markdown
Contributor Author

Could make commit messages shorter.

@lhoguin
lhoguin force-pushed the loic-password-hashing branch from a3cb6c7 to 6d985dd Compare September 4, 2026 14:56
@lhoguin

lhoguin commented Sep 4, 2026 •

Copy link
Copy Markdown
Contributor Author

Made the commit messages shorter. Tests were green before that so I expect them to be green again. Ready for review.

@lhoguin
lhoguin marked this pull request as ready for review September 4, 2026 14:56
@mergify

mergify Bot commented Sep 4, 2026

Copy link
Copy Markdown

Tick the box to add this pull request to the merge queue (same as @mergifyio queue).

  • Queue this pull request

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟑 Changes recommended

The PBKDF2 implementation uses an undersized salt and incorrect work factor, while CLI and test integration remain incomplete.

Once you've addressed the issues Copilot identified, you can request another Copilot review.

Pull request overview

Adds PBKDF2-HMAC-SHA256 password hashing and prevents management user endpoints from exposing password hashes.

Changes:

  • Adds PBKDF2 hashing, tests, and configuration documentation
  • Replaces exposed hashes with has_password
  • Updates management UI and definition tests
File summaries
File Description
deps/rabbitmq_management/test/rabbit_mgmt_http_SUITE.erl Updates user API tests
deps/rabbitmq_management/priv/www/js/tmpl/users.ejs Uses has_password in user listings
deps/rabbitmq_management/priv/www/js/tmpl/user.ejs Uses has_password in user details
deps/rabbitmq_management/priv/www/api/index.html Documents PBKDF2 support
deps/rabbitmq_management_agent/src/rabbit_mgmt_format.erl Hides hashes and exposes password presence
deps/rabbit/priv/schema/rabbit.schema Documents the configuration option
deps/rabbit/docs/rabbitmq.conf.example Adds PBKDF2 configuration guidance
deps/rabbit_common/test/unit_password_hashing_SUITE.erl Tests PBKDF2 hashing
deps/rabbit_common/src/rabbit_password_hashing_pbkdf2_sha256.erl Implements PBKDF2-HMAC-SHA256
Review details
  • Files reviewed: 9/9 changed files
  • Comments generated: 4
  • Review effort level: Balanced

πŸ’‘ Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

<a href="https://rabbitmq.com/passwords.html#computing-password-hash">here</a>.
You may also specify the hash function being used by adding the <code>hashing_algorithm</code>
key to the body. Currently recognised algorithms are <code>rabbit_password_hashing_sha256</code>,
key to the body. Currently recognised algorithms are <code>rabbit_password_hashing_sha256</code>

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changing this file is OK but that reference has moved to a separate doc guide.

@michaelklishin

Copy link
Copy Markdown
Collaborator

@lhoguin thank you for picking this long standing issue up, LoΓ―c.

I have pushed a couple of changes that do not need a design or consensus:

  • rabbitmqctl hash_password support for the new algo
  • HTTP API column list updates

Next I'll investigate whether we can make the salt field wider without breaking backwards compatibility.

@michaelklishin

Copy link
Copy Markdown
Collaborator

Also, this PR will break rabbitmqadmin and likely all statically typed HTTP API clients. Due to the GET /api/users key set change, of course.

We can consider this acceptable and proactively make that key optional in the Rust HTTP API client and (in turn) rabbitmqadmin. Or we can consider keeping the field, I'd probably do that if we technically can.

@michaelklishin

Copy link
Copy Markdown
Collaborator

@lhoguin figured a way to make the salt width flexible without breaking compatibility. Storing the module responsible for hashing in the user record was a very wise decision from 2015 or so. We directly benefit from it here.

Let me know if you'd like a different implementation, I don't feel strongly about it but this solution is straightforward and backwards-compatible.

@michaelklishin

Copy link
Copy Markdown
Collaborator

Another change from me: fixed a bug that would have manifested itself with wider salts (in the future) but would be enough to be considered a vulnerability. A bug from the future avoided, how about that.

@michaelklishin

Copy link
Copy Markdown
Collaborator

@lhoguin should we include the number of iterations into the module so that we can safely change it down the road, or do you think that it's so unlikely to change in the future that we should not bother?

I have added a comment for now to make the risk of ?ITERATIONS changes after we ship this module and it gets adopted.

@lhoguin

lhoguin commented Sep 7, 2026

Copy link
Copy Markdown
Contributor Author

About removing hash_password from API: it's not directly related to the new password algorithm so could be dropped. It should be returned for the administrator tag only, so not a huge concern, and the information is also in definitions. It's more of a defense in depth measure because GET /api/users is called when navigating the UI. If we can not send hash_password for GET /api/users, great, if not, we can drop these commits. I would not send if not required to.

The salt width changes look good, thanks!

We cannot change the number of iterations once passwords have been hashed. If at some point the recommendations become to double or triple the iterations, we can introduce a new module. But we are likely to have better hashing algorithm when that happens. Right now there's two better algorithms that would be good to have but that we don't have available in OTP (without NIFs): Argon2id and scrypt. But those algorithms are not FIPS approved while the one in this PR is. So we'll see. In any case this isn't going to change too often so no real need to parameterize.

I will increase the number of iterations to 600K as that's the recommended number today and there's less risk of issues since this module isn't the default.

@lhoguin
lhoguin force-pushed the loic-password-hashing branch 2 times, most recently from 44ae61a to 97f459e Compare September 7, 2026 10:10
lhoguin and others added 9 commits September 7, 2026 12:25
Add rabbit_password_hashing_pbkdf2_sha256, using 210,000 iterations
(OWASP's 2023 minimum), selectable via password_hashing_module.

The default is unchanged because this new module has a high cost
that may not fit deployments with high churn or many connections.
Replace it with a has_password boolean, which is all the
management UI needs.
while at it, remove a factually incorrect comment
that arguably has no value and should be simply
dropped.
and module-controlled.

This is a backwards-compatible way to make
room for salts of, say, 16 bytes for the newer
hashing algos.
This is a painful couple of changes to explain, so bear with me.

There is one bug fixed and one property preserved.

1. Avoid hardcoding a parameter that makes salts longer
   than 36 bytes slip through a case statement, skipping
   the hashing altogether. This is future-proofing but
   if we get it wrong today, we will pay a major price
   in the future with more hashing modules with even
   longer salts
2. To make the failing case take roughly as long as
   succeeding one to prevent username enumeration
   via a timing side channel (measuring how long a login
   attempt took to figure out the non-existent accounts)

These changes address both by

1. Using an array of zeroes of the salt length returned
   by the configured module
2. Use an empty value so that hash comparison for a dummy
   user always fails and causes the slowdown, so a login
   for a user that does not exist costs as much as one
   for an existing user with a wrong password, which is
   what makes enumeration harder, if at all feasible
@lhoguin
lhoguin force-pushed the loic-password-hashing branch from 97f459e to 05c9ca2 Compare September 7, 2026 10:25
@michaelklishin

Copy link
Copy Markdown
Collaborator

Using 600K and treating it as a constant sounds good.

As for the password hash in the API response, if we make it optional depending on the tag, we still have to update rabbitmqadmin. I'll just do that.

@michaelklishin michaelklishin added this to the 4.4.0 milestone Sep 7, 2026
@michaelklishin michaelklishin added the do-not-backport Changes not meant to be backported to release branches label Sep 7, 2026
@michaelklishin michaelklishin changed the title New password hashing algorithm Support PBKDF2 for password hashing Sep 9, 2026
@michaelklishin
michaelklishin merged commit 2f14e18 into main Sep 9, 2026
198 checks passed
@michaelklishin
michaelklishin deleted the loic-password-hashing branch September 9, 2026 00:15
@michaelklishin

Copy link
Copy Markdown
Collaborator

First step towards forward compatibility of rabbitmqadmin v2: michaelklishin/rabbitmq-http-api-rs#122.

@michaelklishin

Copy link
Copy Markdown
Collaborator

And the rabbitmqadmin v2 change: rabbitmq/rabbitmqadmin-ng#184.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

do-not-backport Changes not meant to be backported to release branches

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants