CVE-2026-7820

Improper restriction of excessive authentication attempts (CWE-307) in pgAdmin 4. pgAdmin enforces MAX_LOGIN_ATTEMPTS only inside its custom /authenticate/login view. Flask-Security's default /login view, which is registered automatically by security.init_app() and is reachable on every server, never consulted the User.locked field: pgAdmin's User model relied on Flask-Security's UserMixin.is_locked() (which always returns 'not locked') and Flask-Login's is_active (which only checks the active column, not locked). An attacker who triggered an account lockout via /authenticate/login could therefore obtain a session by re-submitting valid credentials directly to /login, defeating the brute-force-protection control for accounts using the INTERNAL authentication source. The same bypass also means that login attempts via /login are never rate-limited, so an attacker can perform an unbounded online password-guessing attack against INTERNAL accounts regardless of MAX_LOGIN_ATTEMPTS. Fix overrides User.is_active and User.is_locked() so the locked column is enforced on every authentication path. LDAP, OAuth2, Kerberos, and Webserver users are not reachable by this bypass because they have no local password and are rejected by Flask-Security's LoginForm.validate before the locked check; the lockout itself is also internal-only (the /authenticate/login view filters by auth_source=INTERNAL). This issue affects pgAdmin 4: before 9.15.
References
Link Resource
https://github.com/pgadmin-org/pgadmin4/issues/9904 Issue Tracking Patch Vendor Advisory
Configurations

Configuration 1 (hide)

cpe:2.3:a:pgadmin:pgadmin_4:*:*:*:*:*:postgresql:*:*

History

26 Oct 2026, 18:10

Type Values Removed Values Added
Summary
  • (es) Restricción inadecuada de intentos excesivos de autenticación (CWE-307) en pgAdmin 4. pgAdmin aplica MAX_LOGIN_ATTEMPTS solo dentro de su vista personalizada /authenticate/login. La vista /login predeterminada de Flask-Security, que se registra automáticamente por security.init_app() y es accesible en cada servidor, nunca consultó el campo User.locked: el modelo User de pgAdmin dependía de UserMixin.is_locked() de Flask-Security (que siempre devuelve ’not locked’) y de is_active de Flask-Login (que solo verifica la columna ’active’, no ’locked’). Un atacante que activó un bloqueo de cuenta a través de /authenticate/login podría, por lo tanto, obtener una sesión al volver a enviar credenciales válidas directamente a /login, eludiendo el control de protección contra fuerza bruta para cuentas que usan la fuente de autenticación INTERNAL. El mismo bypass también significa que los intentos de inicio de sesión a través de /login nunca tienen límite de tasa, por lo que un atacante puede realizar un ataque de adivinanza de contraseñas en línea ilimitado contra cuentas INTERNAL independientemente de MAX_LOGIN_ATTEMPTS. La corrección anula User.is_active y User.is_locked() para que la columna ’locked’ se aplique en cada ruta de autenticación. Los usuarios de LDAP, OAuth2, Kerberos y Webserver no son alcanzables por este bypass porque no tienen contraseña local y son rechazados por LoginForm.validate de Flask-Security antes de la verificación de ’locked’; el bloqueo en sí también es solo interno (la vista /authenticate/login filtra por auth_source=INTERNAL). Este problema afecta a pgAdmin 4: antes de 9.15.

Information

Published : 2026-05-11 16:17

Updated : 2026-10-26 18:10


NVD link : CVE-2026-7820

Mitre link : CVE-2026-7820

CVE.ORG link : CVE-2026-7820


JSON object : View

Products Affected

pgadmin

  • pgadmin_4
CWE
CWE-307

Improper Restriction of Excessive Authentication Attempts