Opened 3 years ago

Closed 3 years ago

Last modified 3 years ago

#24465 closed Bug (invalid)

Failed logins are recorded as HTTP 200 instead of HTTP 403

Reported by: Mark Litwintschik Owned by: nobody
Component: contrib.admin Version: 1.7
Severity: Normal Keywords:
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no


Attempting to login to the Django admin with an incorrect username and password combination logs the event as an HTTP 200:

[10/Mar/2015 10:24:06] "POST /admin/login/?next=/admin/ HTTP/1.0" 200 2074

I would expect that it would be recorded as a 403.

django.contrib.admin.forms.AdminAuthenticationForm raises a forms.ValidationError if the login is invalid but there is nothing out of the box that will record the response as HTTP 403.

Change History (5)

comment:1 Changed 3 years ago by Erik Romijn

Resolution: invalid
Status: newclosed

I can see your point, but the behaviour is correct and intentional. The 200 response means the same form is re-rendered, now including an error message. The user can edit their input and try again. A 403 response would cause the browser to display a much less friendly error, without offering a reasonable way for a user to correct their error.

Basically, login errors are handled the same way as any other form error, in the typical case: return a 200 response, display the form again, prefilled with any non-secret data the user already entered, and the appropriate error message. The user adjusts their input and submits the form again.

comment:2 Changed 3 years ago by Erik Romijn

For completeness, this type of form handling is documented in:

And if your issue is that you'd like to separately like to record failed logins, you could use:

comment:3 Changed 3 years ago by Mark Litwintschik

My concern is that this gives a chance to run a dictionary attack against the login. If fail2ban is monitoring nginx's logs or django's logs directly the response for a failed login and a correct one look the same.

Thanks for the heads up on django.contrib.auth.signals.user_login_failed, I'll take a look at it.

Last edited 3 years ago by Mark Litwintschik (previous) (diff)

comment:4 Changed 3 years ago by Erik Romijn

There are a few third party packages available that add brute-forcing protection to Django (login) forms: best known are django-axes and django-lockout. These are able to detect exactly this type of login failure and take a certain action after a certain number of attempts in a certain time. I use django-axes myself in many projects.

Alternatively, you could use the user_login_failed signal to write a log message to the log of your Django app, and have fail2ban trigger on those log messages.

comment:5 Changed 3 years ago by Matthew Schinckel

I've found that using a 401 does not give an error page, but still allows you to see in the logs that it was a non-successful login attempt.

Note: See TracTickets for help on using tickets.
Back to Top