CVE-2026-7820: pgAdmin 4: Account-lockout bypass via Flask-Security default /login view
Improper restriction of excessive authentication attempts (CWE-307) in pgAdmin 4.
pgAdmin enforces MAXLOGINATTEMPTS only inside its custom /authenticate/login view. Flask-Security's default /login view, which is registered automatically by security.initapp() and is reachable on every server, never consulted the User.locked field: pgAdmin's User model relied on Flask-Security's UserMixin.islocked() (which always returns 'not locked') and Flask-Login's isactive (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 MAXLOGINATTEMPTS.
Fix overrides User.isactive and User.islocked() 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 authsource=INTERNAL).
This issue affects pgAdmin 4: before 9.15.
Affected Software
Remediation
Recommended actions to resolve this vulnerability, in priority order.
- Upgrade
Upgrade
pgAdmin 4to a version that resolves this vulnerability.Fixed in 9.15 - Configuration
Apply the fix so locked accounts are enforced on every authentication path; the behavior described fixes that pgAdmin/Flask-Security/Flask-Login default paths otherwise bypass User.locked.
pgAdmin 4 Account lockout enforcement on authentication paths = Ensure locked column is enforced on every authentication path (override User.is_active and User.is_locked() behavior) - Configuration
Update configuration/logic so login attempts are rate-limited on the Flask-Security default /login view; the issue states MAX_LOGIN_ATTEMPTS is only enforced inside pgAdmin’s custom /authenticate/login view, allowing unbounded guessing via /login.
pgAdmin 4 (Flask-Security default /login view) MAX_LOGIN_ATTEMPTS rate limiting = Apply MAX_LOGIN_ATTEMPTS to /login as well as /authenticate/login - Compensating control
Restrict or protect access to the Flask-Security default /login endpoint so it is not reachable for INTERNAL authentication (or otherwise enforce routing so /authenticate/login with auth_source=INTERNAL is used and rate limiting applies).
Event History
Frequently Asked Questions
What is the severity of CVE-2026-7820?
CVE-2026-7820 has a medium severity rating due to the potential for account-lockout bypass.
How do I fix CVE-2026-7820?
To fix CVE-2026-7820, upgrade pgAdmin 4 to the latest version above 9.15 that addresses this vulnerability.
What causes CVE-2026-7820?
CVE-2026-7820 is caused by improper restriction of excessive authentication attempts in the Flask-Security default /login view.
Which versions of pgAdmin 4 are affected by CVE-2026-7820?
CVE-2026-7820 affects pgAdmin 4 versions below 9.15.
What could happen if CVE-2026-7820 is exploited?
If CVE-2026-7820 is exploited, unauthorized users may bypass account lockouts, allowing repeated login attempts.