Type something to search...
What is credential stuffing and how to defend against it?

What is credential stuffing and how to defend against it?

Credential stuffing is an automated attack where criminals take usernames and passwords leaked from one website's data breach and try them on many other websites, betting that people reuse the same password everywhere. Unlike brute force, the attacker isn't guessing; they're using real credentials that already worked somewhere. Because so many people reuse passwords, even a small success rate can give attackers access to a large number of accounts.

If your website has a login form, whether it's a WordPress admin, a WooCommerce customer account, a membership area, or a custom app, credential stuffing is almost certainly being attempted against it right now. This article explains how the attack works, why it's harder to spot than traditional brute force, how to tell if you're being targeted, and the layered defenses that protect both your admin accounts and your users.

What Is Credential Stuffing?

Every year, large data breaches expose huge numbers of email addresses and passwords. Those lists are collected, combined, sold, and shared in criminal forums. Credential stuffing is the practice of feeding those lists into automated tools that try each pair against another site's login page.

The attack relies on one simple human habit: password reuse. If someone used jane@example.com with the password Summer2024! on a breached forum, and they use the same combination on your online store, the attacker is in.

Credential Stuffing vs. Brute Force Attacks

These two attacks are often confused, but they work differently:

  • Brute force: The attacker tries many passwords against one account, such as guessing thousands of passwords for the admin user.
  • Password spraying: The attacker tries a few very common passwords against many accounts.
  • Credential stuffing: The attacker tries one known password per account, using real leaked pairs, across many accounts.

That difference matters for defense. A brute-force attack generates many failures on a single account, which is easy to spot and block. Credential stuffing might try each account only once, often from a different IP address each time, so per-account lockouts barely trigger.

How Does a Credential Stuffing Attack Work?

A typical attack follows a predictable pattern:

  1. Acquire credentials: The attacker obtains a "combo list" of email and password pairs from past breaches.

  2. Configure a tool: They use automation software or a custom script that knows how to submit your login form, including any hidden fields or tokens.

  3. Distribute the traffic: Requests are spread across residential proxies, botnets, or cloud servers so that each IP address sends only a handful of attempts.

  4. Test at scale: The tool submits credentials and records which ones succeed, often checking for a redirect or a specific word on the logged-in page.

  5. Monetize the hits: Working accounts are used directly, such as placing orders with stored cards or redeeming loyalty points, or sold to other criminals.

Why Attackers Use Credential Stuffing

It's cheap, automated, and effective. Leaked credential lists are widely available, the tools are easy to run, and the attacker doesn't need any vulnerability in your software. From their point of view, even a success rate well under one percent is profitable when they're testing millions of pairs.

What Can Attackers Do With Stuffed Accounts?

The damage depends on what the account can do:

  • Admin accounts: Full control of your website, including installing malicious plugins, injecting spam, or stealing customer data.
  • Customer accounts: Stored addresses, order history, saved payment methods, gift card balances, and loyalty points.
  • Membership accounts: Access to paid content, which can be resold.
  • Email or SaaS accounts: A foothold for further phishing and fraud.

Even when the attacker doesn't succeed, a large credential stuffing campaign can slow your server, inflate hosting bills, and flood your logs.

Signs Your Site Is Being Targeted

Credential stuffing tries to look like normal traffic, but it leaves clues:

  1. A spike in failed logins: Especially failed logins for many different usernames or email addresses, rather than one account.

  2. Many logins for accounts that don't exist: Leaked lists contain emails that were never registered on your site.

  3. Unusual geography or networks: Login attempts from countries or hosting providers you don't normally see.

  4. Higher login success from new devices: A cluster of successful logins followed by password changes, address changes, or orders shipped to new addresses.

  5. Customer complaints: Users reporting orders they didn't place or changed account details.

  6. Unusual XML-RPC or REST API traffic: On WordPress, attackers sometimes target xmlrpc.php or other authentication endpoints instead of the normal login page.

You can check login activity with a security or activity log plugin such as Wordfence, Solid Security, or WP Activity Log, or by looking at your server's access logs for repeated POST requests to wp-login.php:

# Count POST requests to wp-login.php by IP in an Nginx or Apache access log
grep "POST /wp-login.php" /var/log/nginx/access.log \
  | awk '{print $1}' | sort | uniq -c | sort -rn | head -20

A long list of IPs each making a small number of attempts is a classic credential stuffing pattern.

How to Defend Against Credential Stuffing

No single control stops credential stuffing on its own. The strongest approach layers several defenses, so an attacker has to beat all of them.

1. Require Multi-Factor Authentication

Multi-factor authentication (MFA) is the single most effective defense. A leaked password is useless if the attacker also needs a code from an authenticator app, a passkey, or a hardware key.

At a minimum, require it for every administrator, editor, and shop manager account. For customer accounts, offer it and encourage it, especially for accounts with stored payment methods. Popular options for WordPress include Wordfence Login Security, the Two Factor plugin, and the MFA features built into Solid Security.

2. Support Passkeys

Passkeys replace passwords with a cryptographic key stored on the user's device or password manager. Since there's no reusable password to leak, credential stuffing simply doesn't apply. Several WordPress plugins now add passkey login, and many identity providers support them if you use single sign-on.

3. Block Known Breached Passwords

Stop users from choosing passwords that are already in breach lists. The Have I Been Pwned Pwned Passwords service offers a free range API that uses k-anonymity: you send only the first five characters of the password's SHA-1 hash, so the actual password never leaves your server.

Here's a WordPress example that checks new passwords during registration, profile updates, and password resets. Place it in a custom plugin or your child theme's functions.php:

<?php
/**
 * Reject passwords found in the Have I Been Pwned breach corpus.
 */
function sajjad_password_is_pwned( $password ) {
    $hash   = strtoupper( sha1( $password ) );
    $prefix = substr( $hash, 0, 5 );
    $suffix = substr( $hash, 5 );

    $response = wp_remote_get(
        'https://api.pwnedpasswords.com/range/' . $prefix,
        array(
            'timeout' => 5,
            'headers' => array( 'Add-Padding' => 'true' ),
        )
    );

    // Fail open if the API is unreachable so users aren't locked out.
    if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) {
        return false;
    }

    $lines = explode( "\n", wp_remote_retrieve_body( $response ) );
    foreach ( $lines as $line ) {
        $parts = explode( ':', trim( $line ) );
        if ( isset( $parts[1] ) && $parts[0] === $suffix && (int) $parts[1] > 0 ) {
            return true;
        }
    }
    return false;
}

// Profile updates and new users created in the admin.
add_action( 'user_profile_update_errors', 'sajjad_check_pwned_profile', 10, 3 );
function sajjad_check_pwned_profile( $errors, $update, $user ) {
    if ( ! empty( $_POST['pass1'] ) ) {
        $password = wp_unslash( $_POST['pass1'] ); // Do not sanitize passwords; they are hashed, not output.
        if ( sajjad_password_is_pwned( $password ) ) {
            $errors->add( 'pwned_password', __( 'This password has appeared in a data breach. Please choose a different one.' ) );
        }
    }
}

// Password reset form.
add_action( 'validate_password_reset', 'sajjad_check_pwned_reset', 10, 2 );
function sajjad_check_pwned_reset( $errors, $user ) {
    if ( ! empty( $_POST['pass1'] ) ) {
        $password = wp_unslash( $_POST['pass1'] );
        if ( sajjad_password_is_pwned( $password ) ) {
            $errors->add( 'pwned_password', __( 'This password has appeared in a data breach. Please choose a different one.' ) );
        }
    }
}

This follows current password guidance from bodies like NIST, which recommends checking new passwords against lists of known compromised passwords rather than forcing arbitrary complexity rules. WooCommerce and membership plugins use their own registration forms, so check their hooks if you want the same check there.

4. Rate Limit by More Than IP Address

Traditional lockouts by IP don't work well against distributed attacks, but rate limiting still raises the attacker's cost. Combine several signals:

  • Per-IP limits: Slow down any single IP making many login attempts.
  • Per-account limits: Temporarily lock or challenge an account after several failed attempts, regardless of IP.
  • Global limits: Alert or challenge everyone when total failed logins spike far above normal.

At the web server level, Nginx can throttle login requests before they reach PHP:

# In the http block
limit_req_zone $binary_remote_addr zone=wplogin:10m rate=5r/m;

# In the server block
location = /wp-login.php {
    limit_req zone=wplogin burst=5 nodelay;
    limit_req_status 429;

    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}

Adjust the PHP-FPM socket path to match your server. Test carefully after changes, and reload Nginx with sudo nginx -t && sudo systemctl reload nginx.

5. Add Bot Detection and Challenges

Most credential stuffing is automated, so making automation harder helps a lot. Options include:

  • CAPTCHA alternatives: Cloudflare Turnstile, hCaptcha, or Google reCAPTCHA on login, registration, and password reset forms.
  • Web application firewalls: Cloudflare, Sucuri, and similar services use reputation data and behavior analysis to block known bad bots.
  • Challenge only when suspicious: Show a challenge after a failed attempt or when a request looks unusual, so legitimate users aren't annoyed every time.

6. Protect Every Login Endpoint

Attackers go wherever the defenses are weakest. On WordPress, authentication can happen through:

  • wp-login.php, the standard login form.
  • xmlrpc.php, which supports authenticated methods and can be abused for login attempts.
  • The REST API, when application passwords are enabled.
  • WooCommerce's My Account login form.

Make sure your rate limiting, bot protection, and monitoring cover all of these, and disable any you don't use.

7. Use Generic Error Messages

Don't tell attackers which part of the login failed. A message like "Incorrect email or password" is better than "No account found for that email," which confirms which emails are registered. WordPress core reveals whether a username exists by default; you can make the message generic with a filter:

add_filter( 'login_errors', 'sajjad_generic_login_error' );
function sajjad_generic_login_error( $error ) {
    return __( 'Incorrect username or password.' );
}

Keep in mind this also hides helpful messages from legitimate users, so it's a trade-off between usability and privacy.

8. Detect Suspicious Successful Logins

Blocking failed logins is only half the job. Also watch for successful logins that look wrong:

  • A login from a new country immediately followed by an email or password change.
  • Many different accounts logging in from the same IP address.
  • A saved shipping address changed just before an order.

Notify users by email when their password, email address, or payment method changes, and when they log in from a new device. That gives real account owners a chance to react quickly.

9. Encourage Password Managers

Credential stuffing only works because of password reuse. Encourage your team and users to use a password manager so every site gets a unique password. For your own admin team, make it a policy rather than a suggestion.

Responding to a Credential Stuffing Attack

If you discover an active or successful attack:

  • Increase protections immediately: Turn on stricter rate limits, bot challenges, or temporarily require a challenge for all logins.

  • Identify affected accounts: Look for accounts with successful logins from suspicious IPs or unusual activity afterwards.

  • Force password resets: Reset passwords for affected accounts and invalidate their sessions. With WP-CLI you can destroy a user's sessions:

# Log a specific user out of all sessions
wp user session destroy 42 --all
  • Reverse fraudulent changes: Restore changed email addresses, shipping details, or payment methods, and cancel suspicious orders.

  • Notify users: Explain what happened, that their password was likely reused from another breach, and encourage unique passwords and MFA.

  • Review legal obligations: Depending on where you and your users are, unauthorized access to personal data may trigger notification requirements.


FAQ: Credential Stuffing

Brute force guesses many passwords for one account. Credential stuffing tries real username and password pairs leaked from other breaches, usually one attempt per account, across many accounts.

Because many people reuse the same password on multiple sites. When one site is breached, those credentials work on other sites where the same person has an account.

It helps but isn't enough on its own. Credential stuffing is usually spread across many IP addresses with few attempts each, so you also need MFA, bot detection, and breached password checks.

Yes, when you use the range API. Only the first five characters of the password's SHA-1 hash are sent, so the password itself is never shared with the service.

Yes. Passkeys don't use a reusable password, so there's nothing for an attacker to steal from one site and replay on another.

Look for spikes in failed logins across many different usernames, logins for accounts that don't exist, and attempts coming from many IPs. Security and activity log plugins make this easy to see.

Force resets for accounts that show suspicious activity. For a large attack, a site-wide reset may be justified, but pair it with clear communication so users understand why.


Conclusion

Credential stuffing doesn't exploit a bug in your website. It exploits the fact that people reuse passwords, and it uses leaked credentials from other breaches to walk in through your front door. Because the traffic is distributed and each account may only be tried once, traditional lockouts alone won't stop it.

The most reliable protection is layered: require MFA for privileged accounts and offer it to everyone else, support passkeys where you can, block breached passwords, rate limit across IPs and accounts, add bot detection to every login endpoint, and watch for suspicious successful logins. Put those in place, and the leaked password lists circulating online lose most of their value against your site.

Tags :
Share :

Related Posts

What are the best WordPress security plugins?

What are the best WordPress security plugins?

The best WordPress security plugins for most sites are Wordfence, Sucuri Security, Solid Security, MalCare, All-In-One Security (AIOS), Patchstack, a

Dive Deeper
What are the most common website security threats?

What are the most common website security threats?

The most common website security threats are vulnerable or outdated software, weak and stolen passwords, malware infections, injection attacks like S

Dive Deeper
How does GDPR affect website security?

How does GDPR affect website security?

GDPR affects website security by turning it from a good habit into a legal obligation. If your website collects personal data from people in the EU (

Dive Deeper