Skip to content

Email resend bypasses retry delay in 2FA challenge flow #847

@dknauss

Description

@dknauss

Summary

The email provider handles resend in pre_process_authentication(), but Two_Factor_Core::process_provider() applies is_user_rate_limited() only afterward. As a result, resend requests are not subject to the same retry-delay gate as verification attempts.

Why this matters

For email-based 2FA, a user who has already hit the plugin's retry delay after invalid verification attempts can still trigger Resend Code. Each resend generates a fresh token via generate_and_email_token(), replacing the one that an attacker may have been trying to guess. This effectively resets the brute-force window while the rate limit is still active — the delay blocks verification attempts but not the token rotation that undermines it.

This is an inference from current source, not a confirmed exploit. It looks like an auth-flow / abuse-hardening gap rather than a full auth bypass.

Verified code path

  1. Two_Factor_Core::process_provider() calls $provider->pre_process_authentication( $user ) before it checks is_user_rate_limited( $user ).
  2. Two_Factor_Email::pre_process_authentication() resends whenever two-factor-email-code-resend is present.
  3. The rate-limit check runs only later in process_provider(), so it does not gate the resend branch.

Current behavior from source

  • Resend is handled as provider pre-processing.
  • Rate limiting is applied only to the subsequent verification path.
  • I did not find a dedicated resend throttle in the email provider path.

Potential impact

  • Repeated resend requests can generate additional email traffic even when verification attempts are temporarily delayed.
  • Integrators embedding the provider UI inside another challenge flow may see resend behavior that does not match the plugin's normal retry messaging.

Suggested fix

Either:

  1. Move the rate-limit check ahead of provider pre-processing for resend-capable flows, or
  2. add a dedicated resend throttle in Two_Factor_Email::pre_process_authentication().

Related context

  • This seems adjacent to the broader throttling discussion in Throttle second factor attempts #477, but I could not find a report specifically about the email resend path surviving the existing retry-delay check.

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

Status

In progress

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions