Current situation: PHP2Shell. Two critical PHP vulnerabilities (CVSS 9.2) allow malicious code without login. Patches since 21 October 2026.What to do now →

Protecting admin logins against brute force: brute-force protection for every login

Automated password attempts hit every reachable login page. How to slow attackers down with rate limiting and Fail2ban, protect accounts with a second factor and store passwords correctly.

Last updated 6 min readGuides

In short: Effective brute-force protection for a login consists of several layers: two-factor login for all admins, a brake on login attempts in the web server, automatic blocks at network level and secure password storage in the code. Where possible, the admin area is additionally reachable only from known networks.

How brute-force attacks on logins work

Every publicly reachable login page is attacked automatically sooner or later. This affects CMS logins, shop backends, hosting panels, webmail and SSH. Attackers do not need to know who owns the website; their tools scan the internet for known login addresses.

Three variants are common. In classic brute force, many passwords are tried against one account. In password spraying, one common password is tested against many accounts to get around lockouts. In credential stuffing, credentials are used that were stolen from other services.

For management and owners, the situation is easily summed up: a taken-over admin account is as dangerous as a security vulnerability in the server. Whoever is admin can install extensions, change code and export data.

Such attacks can be recognised by a cluster of failed logins, by POST requests to the login address from many different IP addresses and by login attempts with user names such as “admin” or names from the legal notice. A noticeably higher server load at unusual times can also point to it, because every login costs compute time for the password check.

Brute-force protection at the login: the most effective measures

Not every measure suits every system. The following order has proved its worth, because the first points combine the most effect with little effort:

  • Two-factor login for all accounts with admin rights. Even a guessed password is then not enough. Details in the guide Two-factor login for admin access.
  • Unique, long passwords from a password manager, no shared accounts.
  • Rate limiting in the web server, so that only a few attempts per minute are possible per address.
  • Automatic blocks of repeatedly conspicuous addresses at network level, for example with Fail2ban.
  • Shrink the admin area: access only from the company network or via VPN, switch off interfaces that are not needed.
  • Uniform error messages that do not reveal whether the user name exists.
  • Monitoring of failed logins and notification of anomalies.

Rate limiting in the web server

With Nginx, the number of requests to a login address can be limited per IP address. The example shows a WordPress login page; adjust the path and PHP hand-over to your system.

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

# in the server block
location = /wp-login.php {
    limit_req zone=login burst=3 nodelay;
    limit_req_status 429;
    # include your server's usual PHP hand-over here
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/run/php/php-fpm.sock;
}

On Apache, restricting the admin area to known addresses offers simple, effective protection:

<Location "/admin">
    Require ip 192.0.2.0/24
</Location>

The address 192.0.2.0/24 is a placeholder from the documentation range; insert your company network or your VPN's address. If your website sits behind a proxy or CDN, the server must know the visitor's real address, otherwise the rule does not work as intended.

Fail2ban and blocks at network level

Fail2ban reads log files and blocks addresses that repeatedly stand out in the firewall for a set time. For Nginx rate limiting, Fail2ban already ships a suitable filter:

# /etc/fail2ban/jail.local
[nginx-limit-req]
enabled  = true
port     = http,https
logpath  = /var/log/nginx/error.log
maxretry = 10
findtime = 10m
bantime  = 1h

[sshd]
enabled  = true
maxretry = 5
bantime  = 1h

After the restart, check with fail2ban-client status that the jails are active. Take care to exclude your own fixed addresses via ignoreip so that you do not lock yourself out. Dedicated filters are possible for individual CMS login pages, but they must match the actual log format and be tested beforehand.

If a CDN or proxy sits in front of the server, Fail2ban sees only its addresses without further settings. A block would then hit all visitors. In this case the web server must take the real visitor address from the header of the upstream service, and blocks are better set directly at the CDN.

Blocks at network level are a tool against mass attacks, not against targeted ones. Distributed attacks from very many addresses with few attempts per address undercut any threshold. That is why the second factor remains the most important layer, and Fail2ban mainly reduces load and log noise.

Storing passwords correctly in your own software

Anyone programming their own logins should never store passwords in plain text or with fast hash functions such as MD5 or SHA-1. With password_hash(), PHP provides a secure solution that manages salt and algorithm automatically.

<?php
declare(strict_types=1);

// On registration or password change
$hash = password_hash($passwort, PASSWORD_DEFAULT);

// On login
if (password_verify($eingabe, $hashAusDatenbank)) {
    if (password_needs_rehash($hashAusDatenbank, PASSWORD_DEFAULT)) {
        $neu = password_hash($eingabe, PASSWORD_DEFAULT);
        // store $neu in the database
    }
    // Login successful, renew the session ID
} else {
    usleep(random_int(200000, 400000)); // uniform response time
    // output a uniform error message
}

password_needs_rehash() ensures that older hashes are automatically converted to the current method at the next login. Log failed attempts with time, account and address so that patterns stand out. Notes on evaluation are in the guide Analysing web server logs after an attack.

Special features of CMS, shops and panels

Besides the visible login page, many systems have further routes through which credentials are checked. These side entrances are often forgotten in protection.

  • WordPress: The xmlrpc.php interface also accepts logins. If it is not needed, for example for older apps, block it in the web server. Also check whether user names are visible via author pages or the REST interface, and use names for admins that do not appear publicly.
  • Shops: Besides the admin area, customer accounts are a target, especially for credential stuffing. Rate limiting, notifications of new logins and an optional second factor for customers help here.
  • Hosting panels and webmail: These interfaces are often reachable worldwide and protect the whole server. They should be reachable only from known networks or via VPN.
  • SSH: Login with a key instead of a password makes classic guessing ineffective.

Which protective functions your system provides itself is in the vendor's documentation. Third-party extensions should be actively maintained, otherwise they become a risk themselves. For WordPress operators, the page WordPress and PHP2Shell summarises the current priorities.

How does PHP SOS help?

  • We record all reachable login pages on your servers, from the CMS to the hosting panel, and assess their protection.
  • We set up rate limiting, Fail2ban and access restrictions as part of server hardening and test them.
  • We accompany the introduction of two-factor login for admins and document emergency access.
  • With security monitoring, we keep failed logins and new admin accounts continuously in view.

Frequently asked questions

How do I recognise a brute-force attack?

Typical are many failed logins in a short time, often against the same login page and with changing user names. In the access log this shows up as a cluster of POST requests to the login address, frequently from many different IP addresses.

Should an account be locked after several failed attempts?

Caution: hard lockouts can be used by attackers to lock legitimate users out. Better are increasing delays, time-limited blocks per IP address and a notification to the account holder.

Does it help to rename the login address?

It reduces automated noise in the logs but is not real protection. Anyone who knows or finds the address can keep attacking. Effective are two-factor login, rate limiting and restricting the admin area.

What is credential stuffing?

Attackers try combinations of email and password that were stolen from other services. It works when users reuse the same password. Unique passwords and a second factor remove the basis of this method.

Sources

Immediate contact

Every hour counts. Write to us now.

Briefly describe your system, PHP version and observation. We will get back to you, clarify the situation and work remotely until the incident is closed and documented.

Immediate help