11

I am using Django remote user authentication in a project. What I am actually using is just django.contrib.auth.RemoteUserBackend without the middleware, and manually calling authenticate after having checked with the backend that the user is legitimate.

Reading the source of the middleware, it seems that it just takes the username from a header in the request and then authenticates the user against the backend passing this username. The remote user backend, in turn, just merrily logs the user in with whatever username was passed. The user has then access to every area that requires a valid login.

Isn't this just a huge security flaw? How is this meant to be used?

In my case I should be safe, since the only call to authenticate comes after a successful remote identity verification, but I am wondering the reason why the middleware was introduced.

Andrea
  • 20,253
  • 23
  • 114
  • 183

3 Answers3

13

Let me turn this around on you: if you think this is a security flaw, then try writing an exploit that sets the REMOTE_USER header in a request to your app and see what happens.

REMOTE_USER dates back to the early days of the web when CGI pages were executed locally as the user you were hitting the web page with. REMOTE_USER is actually the name of a unix environment variable that denotes the active user. As security models for web servers changed, this scheme was preserved for compatibility. Now even IIS supports it to transparently handle Active Directory logins.

All user-passed headers begin with HTTP_. Otherwise, you couldn't trust on any header information, like SERVER_NAME, which would be an enormous mess.

David Horn
  • 1,231
  • 1
  • 10
  • 5
3

Django 'merrily logs the user in' because your webserver has checked that the visitor has valid credentials for that username, and set the header accordingly.

If you trust your webserver (e.g. Apache) to set the REMOTE_USER (or other) header correctly, then it's not a security flaw.

Alasdair
  • 298,606
  • 55
  • 578
  • 516
  • I still do not understand. That is just a header of the request. Why cannot an attacker just set it manually when issuing the request? – Andrea Mar 02 '12 at 09:28
  • Django relies on the webserver to set the header correctly. If the webserver allows the user to set the header manually, then there's a security hole. – Alasdair Mar 02 '12 at 13:19
  • I understand, but the point is that you have to somehow instruct the webserver to remove that header from the request. Headers are for the most part kept unchanged. They are part of the **request**, hence they are user input. I think this should deserve at least a notice. – Andrea Mar 02 '12 at 13:39
  • This isn't my area of expertise. Unless somebody else can offer some insight, I think we're going to go around in circles. Ultimately, if you use a remote authentication method, I feel it's your responsibility to understand how it works, and how to configure it correctly. However, if you think the documentation can be improved, then I recommend you submit a patch. – Alasdair Mar 02 '12 at 14:49
  • a quick look at the source code makes it clear that this middleware and backend does not actually check the password/credentials at all. it just relies on the REMOTE_USER attribute and even goes ahead and creates a new user with that username if one doesn't exist, all without confirming the password is correct. Andrea, I agree with you, not sure why I would ever want a security hole like this in my code. – ecbtln Mar 07 '12 at 05:47
  • The user creation is [documented here](https://docs.djangoproject.com/en/dev/howto/auth-remote-user/#django.contrib.auth.backends.RemoteUserBackend.create_unknown_user). The `RemoteUserBackend` doesn't check the password because the user has already authenticated with the remote authentication source. Where is the security hole if the remote authentication source and webserver are configured correctly? – Alasdair Mar 07 '12 at 10:22
  • I still do not understand your reply. `REMOTE_USER` is a header in the **request**. Why cannot an attacker just send this header along with his request? I guess one can configure the webserver to strip this header before it arrives to Django, but I think users should be made aware of this. – Andrea Mar 16 '12 at 15:18
  • 1
    The default implementation relies on an environment variable `REMOTE_USER` to be set. As I understand it, a user could not forge this, because an HTTP header would be mapped to `HTTP_REMOTE_USER` in Django's [request.Meta](https://docs.djangoproject.com/en/dev/ref/request-response/#django.http.HttpRequest.META) dictionary. If you subclass the middleware and set a custom `header`, then yes, a user could send that `header`. As I've said before, if you are relying on the webserver to do authentication, it's important that the webserver is configured correctly. – Alasdair Mar 16 '12 at 15:32
  • Again, if the documentation can be made clearer, then please submit a patch. Endless comments here do not help anybody. – Alasdair Mar 16 '12 at 15:35
  • 2
    It is not a HTTP header. It's a server-side only variable. – mynameistechno Mar 22 '13 at 18:19
  • 2
    It's WSGI env variable, not an HTTP header. It's an attribute of the *python request object* not an attribute of your HTTP request. No amount of fiddling with the HTTP request will set that var unless you've deliberately set up a server to do it. – Iain Duncan Nov 30 '15 at 23:06
2

You can see the documentation here. A user can't send a request with customer header for REMOTE_USER.

Warning

Be very careful if using a RemoteUserMiddleware subclass with a custom HTTP header. You must be sure that your front-end web server always sets or strips that header based on the appropriate authentication checks, never permitting an end-user to submit a fake (or “spoofed”) header value. Since the HTTP headers X-Auth-User and X-Auth_User (for example) both normalize to the HTTP_X_AUTH_USER key in request.META, you must also check that your web server doesn’t allow a spoofed header using underscores in place of dashes.

This warning doesn’t apply to RemoteUserMiddleware in its default configuration with header = 'REMOTE_USER', since a key that doesn’t start with HTTP_ in request.META can only be set by your WSGI server, not directly from an HTTP request header.

Community
  • 1
  • 1
ramwin
  • 5,803
  • 3
  • 27
  • 29