Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Security: BC break for custom encoders set as migrate_from #42746

Copy link
Copy link
Closed
@TomaszGasior

Description

@TomaszGasior
Issue body actions

Symfony version(s) affected: 5.3.6

Description
I upgraded my app from Symfony 5.2 to 5.3. I am not able to log in to my app. These is an error:

Uncaught PHP Exception TypeError: "Argument 1 passed to Symfony\Component\PasswordHasher\Hasher\PasswordHasherFactory::createHasher() must be of the type array, object given, called in [...]/vendor/symfony/password-hasher/Hasher/PasswordHasherFactory.php on line 146" at [...]/vendor/symfony/password-hasher/Hasher/PasswordHasherFactory.php line 79

How to reproduce
My app is open source: https://github.com/TomaszGasior/RadioLista-v3

On v3.32 tag (with Symfony 5.2) I am able to log in without issues. On v3.33 tag (Sf 5.3) it's impossible to log in to my app.
Please note my app has dev-env specific security encoders settings so you may have remove config/dev/security.yaml or set APP_ENV=prod to really reproduce the issue.

Possible Solution
I found that the issue is caused by BC layer of new password hasher component. My app has it's own password encoder (for legacy sha1 passwords) which is improperly handled by BC layer when it's set as migrate_from option.

Part of security.yaml:

    encoders:
        App\Entity\User:
            algorithm: auto
            cost: 12
            migrate_from: [rl_v1, bcrypt]    # removing this line "hides" the issue
        rl_v1:
            id: App\Security\Encoder\RLv1Encoder

I am able to fix the issue by modifying PasswordHasherFactory.php from this

            foreach ($frompasswordHashers as $name) {
                if ($hasher = $this->passwordHashers[$name] ?? false) {
                    $hasher = $hasher instanceof PasswordHasherInterface ? $hasher : $this->createHasher($hasher, true);
                } else {
                    $hasher = $this->createHasher(['algorithm' => $name], true);
                }

                $hasherChain[] = $hasher;
            }

to this:

            foreach ($frompasswordHashers as $name) {
                if ($hasher = $this->passwordHashers[$name] ?? false) {
                    if (!$hasher instanceof PasswordHasherInterface && $hasher instanceof PasswordEncoderInterface) {
                        $hasher = new PasswordHasherAdapter($hasher);
                    }
                    else {
                        $hasher = $hasher instanceof PasswordHasherInterface ? $hasher : $this->createHasher($hasher, true);
                    }
                } else {
                    $hasher = $this->createHasher(['algorithm' => $name], true);
                }

                $hasherChain[] = $hasher;
            }

Please let me know it makes sense or not, I can create PR. :)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      Morty Proxy This is a proxified and sanitized view of the page, visit original site.