Unlimited Login Attempts: When Authentication Lacks Abuse Controls

CATEGORY: RESEARCH DATE: 2026-05-15

Research by badjuju - Red Orca

Analysis of an authentication flow that processes consecutive failed login attempts without rate limiting, account lockout, or progressive backoff delays.

Authentication isn’t just about checking whether a password is correct. It’s also about handling abuse.

What happens when a user mistypes their password once isn’t particularly interesting. What happens when someone attempts ten thousand logins in a few seconds is.

While reviewing gtsteffaniak/filebrowser, I found that the authentication flow lacked several common protections against automated attacks. There was no rate limiting, no account lockout mechanism, and no progressive delay for repeated authentication failures.

The issue has since been addressed through GitHub Security Advisory GHSA-r4v7-6wcg-ghj5.

The Finding

The affected endpoint handled authentication requests by accepting a username as a query parameter and a password via request header:

POST /api/auth/login?username=admin&recaptcha=
X-Password: wrong-password

What stood out during testing wasn’t how the endpoint handled successful logins, but how it handled failures.

Every failed authentication attempt was processed exactly the same way. No throttling. No backoff. No lockout. Nothing suggested the application was tracking abusive behavior at all.

During testing, I expected to eventually encounter some form of defensive control, whether that was a 429 Too Many Requests response, increased response latency, or a temporary lockout. After dozens of failed attempts produced identical responses and nearly identical timings, it became clear that no meaningful restrictions were being applied.

To validate this, I ran a simple script that performed 100 consecutive failed authentication attempts while tracking both status codes and response times:

import requests, time
URL = "http://localhost:8080/api/auth/login"
for i in range(1, 101):
    start = time.perf_counter()
    resp = requests.post(
        URL,
        params={
            "username": "admin",
            "recaptcha": ""
        },
        headers={
            "X-Password": "wrong-password"
        }
    )

    duration = time.perf_counter() - start

    if i % 10 == 0 or resp.status_code == 429:
        print(
            f"Attempt {i:3}: "
            f"status={resp.status_code}, "
            f"latency={duration:.4f}s"
        )

The results were exactly what you’d expect from an endpoint with no abuse protections in place:

From the application’s perspective, request number one and request number one hundred were treated exactly the same.

Why This Matters

When people hear “brute force,” they usually think about weak passwords. That’s only part of the problem.

Authentication endpoints are among the most heavily targeted components of any web application. Even when users choose strong passwords, unrestricted login attempts allow attackers to continuously test credentials, perform credential stuffing attacks, or automate password guessing against known accounts.

In this case, the impact became more significant when combined with a separate username enumeration issue. An attacker could first identify valid usernames and then focus authentication attempts exclusively on legitimate accounts.

Beyond credential security, there is also the issue of resource consumption.

Modern password hashing algorithms such as bcrypt and Argon2 are intentionally expensive. Every failed login attempt still requires the server to perform cryptographic work. Without controls to slow or limit requests, an attacker can force a system to continuously spend resources processing authentication requests.

Authentication endpoints shouldn’t become a free CPU benchmark for unauthenticated users.

What Was Missing

In most environments I’d expect to see at least one of the following controls in place:

No single control is a silver bullet, but together they significantly increase the cost of automated attacks while reducing the effectiveness of brute-force and credential-stuffing campaigns. Seeing none of them implemented was what elevated this from a hardening recommendation to a security finding.

Remediation

The issue was addressed by introducing configurable rate-limiting controls for authentication requests.

With rate limiting enabled, repeated authentication attempts can be tracked and restricted before they become abusive. Instead of processing every request indefinitely, the application can begin rejecting requests once defined thresholds are exceeded.

This doesn’t eliminate brute-force attacks entirely, but it changes the economics of the attack. What was previously limited only by network speed becomes constrained by application policy. For authentication systems, that’s exactly the goal.

Final Thoughts

The issue was fixed and publicly disclosed through a GitHub Security Advisory, which is ultimately the most important outcome.

That said, I believe issues like this deserve standardized tracking through the CVE process. My concern isn’t the severity rating or the wording used to describe the issue; it’s visibility. CVEs remain one of the primary mechanisms used by vulnerability databases, enterprise security scanners, SBOM tooling, and dependency tracking platforms to identify affected software.

When an authentication endpoint accepts unlimited login attempts and requires a security fix, I believe users benefit from having that issue tracked through the same channels as other publicly disclosed vulnerabilities.

References


This vulnerability was discovered during independent research and has been reported to the maintainers to ensure a more robust structural fix is implemented.


About

badjuju focuses on application security and vulnerability research, digging into how bugs happen and where things break in real systems.