Feature Request: Optional Generic Authentication Failure Responses to Prevent Username Enumeration

Hello,

First, thank you for the continued work on Pritunl. We use it in a business environment and have been very happy with the product.

During a recent PCI DSS external penetration test, one of the findings was that the /auth/session endpoint returns different authentication responses depending on whether the supplied username exists.

Current behavior

If an invalid username is supplied:


HTTP/1.1 401 Unauthorized

{
    "error": "auth_invalid_username",
    "error_msg": "Authentication username is not valid."
}

If a valid username is supplied with an incorrect password:


HTTP/1.1 401 Unauthorized

{
    "error": "auth_invalid",
    "error_msg": "Authentication credentials are invalid."
}

Because the responses differ, an attacker can determine whether a username exists without needing to know the password. This behavior is commonly flagged during PCI DSS penetration tests as username enumeration.

Code location

While investigating, I traced the behavior to:


pritunl/handlers/auth.py

Specifically:


admin = auth.get_by_username(username)

if not admin:
    return _auth_plugin(username, password, remote_addr)

which ultimately returns:


{
    "error": AUTH_INVALID_USERNAME,
    "error_msg": AUTH_INVALID_USERNAME_MSG,
}

whereas an existing user with an invalid password returns:


{
    "error": AUTH_INVALID,
    "error_msg": AUTH_INVALID_MSG,
}

Feature Request

Would it be possible to add an optional configuration setting to return a generic authentication failure for all failed login attempts?

For example:


generic_auth_errors = true

or similar.

When enabled, both cases would return something like:


HTTP/1.1 401 Unauthorized

{
    "error": "auth_invalid",
    "error_msg": "Invalid username or password."
}

while still logging the detailed failure reason internally (invalid username, invalid password, MFA failure, etc.).

Why this would help

This would align with security guidance such as:

  • OWASP Authentication Cheat Sheet
  • OWASP ASVS
  • PCI DSS penetration testing expectations

Many organizations using Pritunl in regulated environments (PCI DSS, SOC 2, ISO 27001, HIPAA, etc.) would benefit from an option to suppress username enumeration without requiring a reverse proxy or WAF to rewrite responses.

This would preserve the current behavior by default while allowing administrators to enable generic authentication responses when required for compliance.

Thank you for considering this enhancement.

It would take more than that to resolve the issue. Every code path would need to be accounted for with equal timing or it would be very easy to check for usernames by comparing the response time. The time it takes to lookup a valid user compared to one that doesn’t exist can be calculated. There is also RADIUS and plugin support for logins, all of these are different code paths that could offset the response time. A random sleep can be used but this still results in timing attacks that are theoretically possible. If this were done it would result in a continued stream of reported CVEs.

I don’t think this would impact any security compliance because it is only for the administrator accounts. All user accounts are handled by the single sign-on provider and almost all of those have simple methods to check for usernames like attempting a password recovery. If it is a concern use a random 64 char string as the administrator username.

There is already Yubikey support for administrators which provides a very secure login. But I also just added a new local two-factor authentication method to resolve any concerns about the security of administrator accounts.

The new method requires running a command on the server for administrator logins. This will prompt the administrator to run a command on login and require full SSH access to complete an administrator login. After testing this will be included in the next release.