VYPR
High severityOSV Advisory· Published Dec 10, 2025· Updated Dec 10, 2025

Filament's multi-factor authentication (app) recovery codes can be used multiple times

CVE-2025-67507

Description

Filament is a collection of full-stack components for accelerated Laravel development. Versions 4.0.0 through 4.3.0 contain a flaw in the handling of recovery codes for app-based multi-factor authentication, allowing the same recovery code to be reused indefinitely. This issue does not affect email-based MFA. It also only applies when recovery codes are enabled. This issue is fixed in version 4.3.1.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Filament versions 4.0.0-4.3.0 allow indefinite reuse of app-based MFA recovery codes, weakening security; fixed in 4.3.1.

Vulnerability

Overview

Filament, a full-stack component framework for Laravel, versions 4.0.0 through 4.3.0 contain a flaw in the handling of recovery codes for app-based multi-factor authentication (MFA). The recovery codes are not invalidated after a single use, allowing the same code to be reused indefinitely [1][4]. This issue does not affect email-based MFA and only applies when recovery codes are enabled [1].

Exploitation

An attacker who gains access to both a user's password and their recovery codes can repeatedly complete the MFA challenge without needing the user's app-based second factor [4]. The attack requires the attacker to have obtained the recovery codes (e.g., through a data breach or phishing) and the user's password. No additional authentication is needed after the first successful use of a recovery code [2].

Impact

The intended security of MFA is weakened because recovery codes become a static, long-term bypass method rather than a one-time-use fallback [4]. This effectively reduces the MFA to a single-factor password if recovery codes are compromised, undermining the protection that MFA is designed to provide.

Mitigation

The issue is fixed in Filament version 4.3.1 [1]. The fix, visible in commit 87ff60ad9b6e16d4e14ee36a220b8917dd7b0815, ensures that after a recovery code is verified, it is removed from the user's stored codes, preventing reuse [2]. Users should upgrade to version 4.3.1 or later to remediate the vulnerability.

AI Insight generated on May 19, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
filament/filamentPackagist
>= 4.0.0, < 4.3.14.3.1

Affected products

2
  • Filamentphp/FilamentOSV2 versions
    v4.0.0, v4.0.1, v4.0.10, …+ 1 more
    • (no CPE)range: v4.0.0, v4.0.1, v4.0.10, …
    • (no CPE)range: >=4.0.0, <=4.3.0

Patches

1
87ff60ad9b6e

fix: Multi-factor recovery code reuse (#18672)

https://github.com/filamentphp/filamentDan HarrinDec 9, 2025via ghsa
2 files changed · +71 2
  • packages/panels/src/Auth/MultiFactor/App/AppAuthentication.php+13 2 modified
    @@ -176,13 +176,24 @@ public function verifyRecoveryCode(string $recoveryCode, ?HasAppAuthenticationRe
         {
             $user ??= Filament::auth()->user();
     
    +        $remainingCodes = [];
    +        $isValid = false;
    +
             foreach ($this->getRecoveryCodes($user) as $hashedRecoveryCode) { /** @phpstan-ignore-line */
                 if (Hash::check($recoveryCode, $hashedRecoveryCode)) {
    -                return true;
    +                $isValid = true;
    +
    +                continue;
                 }
    +
    +            $remainingCodes[] = $hashedRecoveryCode;
    +        }
    +
    +        if ($isValid) {
    +            $user->saveAppAuthenticationRecoveryCodes($remainingCodes);
             }
     
    -        return false;
    +        return $isValid;
         }
     
         /**
    
  • tests/src/Panels/Auth/MultiFactor/App/AppAuthenticationChallengeTest.php+58 0 modified
    @@ -343,3 +343,61 @@
     
         $this->assertGuest();
     });
    +
    +it('will not allow a recovery code to be used more than once', function (): void {
    +    $appAuthentication = Arr::first(Filament::getCurrentOrDefaultPanel()->getMultiFactorAuthenticationProviders());
    +
    +    $userToAuthenticate = User::factory()
    +        ->hasAppAuthentication($recoveryCodes = $appAuthentication->generateRecoveryCodes())
    +        ->create();
    +
    +    $recoveryCodeToUse = Arr::first($recoveryCodes);
    +
    +    livewire(Login::class)
    +        ->fillForm([
    +            'email' => $userToAuthenticate->email,
    +            'password' => 'password',
    +        ])
    +        ->call('authenticate')
    +        ->assertNotSet('userUndertakingMultiFactorAuthentication', null)
    +        ->assertNoRedirect()
    +        ->callAction(TestAction::make('useRecoveryCode')
    +            ->schemaComponent("{$appAuthentication->getId()}.code", schema: 'multiFactorChallengeForm'))
    +        ->fillForm([
    +            $appAuthentication->getId() => [
    +                'recoveryCode' => $recoveryCodeToUse,
    +            ],
    +        ], 'multiFactorChallengeForm')
    +        ->call('authenticate')
    +        ->assertHasNoErrors()
    +        ->assertRedirect(Filament::getUrl());
    +
    +    $this->assertAuthenticatedAs($userToAuthenticate);
    +
    +    auth()->logout();
    +
    +    $this->assertGuest();
    +
    +    livewire(Login::class)
    +        ->fillForm([
    +            'email' => $userToAuthenticate->email,
    +            'password' => 'password',
    +        ])
    +        ->call('authenticate')
    +        ->assertNotSet('userUndertakingMultiFactorAuthentication', null)
    +        ->assertNoRedirect()
    +        ->callAction(TestAction::make('useRecoveryCode')
    +            ->schemaComponent("{$appAuthentication->getId()}.code", schema: 'multiFactorChallengeForm'))
    +        ->fillForm([
    +            $appAuthentication->getId() => [
    +                'recoveryCode' => $recoveryCodeToUse,
    +            ],
    +        ], 'multiFactorChallengeForm')
    +        ->call('authenticate')
    +        ->assertHasFormErrors([
    +            "{$appAuthentication->getId()}.recoveryCode",
    +        ], 'multiFactorChallengeForm')
    +        ->assertNoRedirect();
    +
    +    $this->assertGuest();
    +});
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

4

News mentions

0

No linked articles in our index yet.