Skip to content

Fix bcrypt password verification and enforce bcrypt for all new password storage#439

Open
Copilot wants to merge 3 commits into
masterfrom
copilot/fix-bcrypt-salt-verification
Open

Fix bcrypt password verification and enforce bcrypt for all new password storage#439
Copilot wants to merge 3 commits into
masterfrom
copilot/fix-bcrypt-salt-verification

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented May 13, 2026

verifyPassword() routed to password_verify() only when $salt === "bcrypt", but several write paths stored bcrypt hashes while leaving salt as an MD5 string — causing login to silently fall through to SHA1 comparison and always fail.

Changes

app/helper/security.php

  • Added isBcryptHash() to detect bcrypt hashes by prefix ($2y$, $2b$, $2a$)
  • verifyPassword() now uses bcrypt path when the hash is bcrypt, regardless of salt value
// Before: only bcrypt-verified when salt === "bcrypt"
if ($salt === "bcrypt") {
    return password_verify($password, $hash);
}
return hash_equals($this->hash($password, $salt), $hash); // always ran for MD5 salt, even against $2y$ hashes

// After: auto-detects bcrypt hashes by prefix
if ($salt === "bcrypt" || $this->isBcryptHash($hash)) {
    return password_verify($password, $hash);
}

app/controller/user.php, app/controller/admin.php, app/controller/index.php

  • All password write paths (user password change, admin user create/edit, password reset, forced reset) were calling hash($password, $salt) with a non-null salt, hitting the SHA1 path instead of bcrypt
  • Fixed to call hash($password) (no salt) and unpack the returned ["salt" => "bcrypt", "hash" => "..."] array
  • Temporary passwords in admin keep salt = null to preserve forced-reset-on-login behaviour; the bcrypt hash is still correctly verified via prefix detection

install.php

  • Initial admin password created during fresh installation was also using the legacy SHA1 path; now uses hash() without a salt so new installs store a bcrypt hash from the start

tests/stringTest.php

  • Added testVerifyBcryptPasswordWithMismatchedSalt() covering the exact failure case: bcrypt hash stored alongside an MD5 salt

Copilot AI linked an issue May 13, 2026 that may be closed by this pull request
…ll new passwords

Agent-Logs-Url: https://github.com/Alanaktion/phproject/sessions/3f1b38d8-6ce2-4863-beaa-8ad14ae1e6f7

Co-authored-by: Alanaktion <236490+Alanaktion@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix bcrypt login issue with legacy salt verification Fix bcrypt password verification and enforce bcrypt for all new password storage May 13, 2026
Copilot AI requested a review from Alanaktion May 13, 2026 19:04
@Citydampf
Copy link
Copy Markdown

looks good

@Alanaktion Alanaktion marked this pull request as ready for review May 13, 2026 22:50
Copilot AI review requested due to automatic review settings May 13, 2026 22:50
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates password verification and storage paths so bcrypt hashes are verified even when legacy salt metadata is inconsistent, and several password update flows now generate bcrypt hashes directly.

Changes:

  • Added bcrypt hash prefix detection in Helper\Security::verifyPassword().
  • Updated account, admin, password reset, and forced-reset password write paths to use bcrypt hash output.
  • Added a regression test for bcrypt verification when the stored salt is legacy-looking.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
app/helper/security.php Detects bcrypt hashes by prefix during password verification.
app/controller/user.php Stores bcrypt output for user-initiated password changes.
app/controller/index.php Stores bcrypt output for reset and forced-reset flows.
app/controller/admin.php Stores bcrypt output for admin-set temporary/permanent passwords.
tests/stringTest.php Adds regression coverage for mismatched bcrypt hash and salt metadata.
Comments suppressed due to low confidence (1)

app/controller/index.php:300

  • Mandatory: this forced-reset path also writes the full bcrypt hash into user.password, which is still declared as char(40) in the database schemas. The stored hash can be truncated or rejected, leaving users unable to complete the forced reset/login flow until the schema is updated.
            $hashResult = $security->hash($f3->get("POST.password1"));
            $user->salt = $hashResult["salt"];
            $user->password = $hashResult["hash"];

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread app/controller/user.php
Comment thread app/controller/index.php
Comment thread app/controller/admin.php
Comment thread app/helper/security.php
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

new bcrypt login is always more legacy

4 participants