views:

295

answers:

3

I have a Django app. When logged in as an admin user, I want to be able to pass a secret parameter in the URL and have the whole site behave as if I were another user.

Let's say I have the URL /my-profile/ which shows the currently logged in user's profile. I want to be able to do something like /my-profile/?__user_id=123 and have the underlying view believe that I am actually the user with ID 123 (thus render that user's profile).

Why do I want that?

Simply because it's much easier to reproduce certain bugs that only appear in a single user's account.

My questions:

1) What would be the easiest way to implement something like this?

2) Is there any security concern I should have in mind when doing this? Note that I (obviously) only want to have this feature for admin users, and our admin users have full access to the source code, database, etc. anyway, so it's not really a "backdoor"; it just makes it easier to access a user's account.

Thank you!

A: 

Set up so you have two different host names to the same server. If you are doing it locally, you can connect with 127.0.0.1, or localhost, for example. Your browser will see this as three different sites, and you can be logged in with different users. The same works for your site.

So in addition to www.mysite.com you can set up test.mysite.com, and log in with the user there. I often set up sites (with Plone) so I have both www.mysite.com and admin.mysite.com, and only allow access to the admin pages from there, meaning I can log in to the normal site with the username that has the problems.

Lennart Regebro
This doesn't answer my question. I don't want to login with different users, since I don't know the other users' passwords. I just want to *impersonate* other users (being an admin).
ionut bizau
@ibz: No, you don't. That would be like the biggest security hole ever, it's a very bad idea. If you really have problem with one specific user (and not with a specific security group) then reset that users password, and let the users reset it back later.
Lennart Regebro
How exactly is that a "security hole"?
ionut bizau
To be honest, I think I both answered that in a comment to the question, and I also think it's pretty self-evident. Sorry.
Lennart Regebro
@ibz: Knowing the admin password means ALL user accounts are compromised. Whereas knowing just a user password compromises only one account.
S.Lott
@S.Lott: Very good point. Thanks!
ionut bizau
+4  A: 

I solved this with a simple middleware. It also handles redirects (that is, the GET parameter is preserved during a redirect). Here it is:

class ImpersonateMiddleware(object):
    def process_request(self, request):
        if request.user.is_superuser and "__impersonate" in request.GET:
            request.user = models.User.objects.get(id=int(request.GET["__impersonate"]))

    def process_response(self, request, response):
        if request.user.is_superuser and "__impersonate" in request.GET:
            if isinstance(response, http.HttpResponseRedirect):
                location = response["Location"]
                if "?" in location:
                    location += "&"
                else:
                    location += "?"
                location += "__impersonate=%s" % request.GET["__impersonate"]
                response["Location"] = location
        return response
ionut bizau
If you can easily turn on and off this middleware (from filesystem configuration of course) then this is a good compromise, as you can leave it off except for when you *really* need it.
Lennart Regebro
There is no reason to he so paranoid about this feature. An admin can delete a user. An admin can edit a user. An admin can kill the site. Logging in as a them is no less secure. As long as the superadmin login is secure so is the "Su" feature. If there is a concern about the admin, the fact that a session was su'd can be logged such that the events are traceable.
Alex Neth
I should add that I regularly include this feature. It's extraordinarily useful. I would use some very explicit and visible logging if this was a banking site and severEly limit the privilege to super admins, but it's fine even there given appropriate safeguards.
Alex Neth
I use a similar middleware, but a custom header instead. X-Masquerade-As: <username>. This then sets the user attribute on the request, but also sets the superuser as the 'real_user', which is then used in logging.
Matthew Schinckel
+2  A: 

i don't see how that is a security hole any more than using su - someuser as root on a a unix machine. root or an django-admin with root/admin access to the database can fake anything if he/she wants to. the risk is only in the django-admin account being cracked at which point the cracker could hide tracks by becoming another user and then faking actions as the user.

yes, it may be called a backdoor, but as ibz says, admins have access to the database anyways. being able to make changes to the database in that light is also a backdoor.

greetings, eMBee

eMBee
Well, Unix security is pretty broken in principle. The difference is that after 40 years of unix bugfixing it's pretty hard to crack the su command and execute su unless you are supposed to. Thousands of people have tried to break it, and thousands of people has fixed the problems. This is hardly the case for ibz Django middleware.If you want to be secure, you *have* to be paranoid. :)
Lennart Regebro
But, once you are an su-capable user (under unix) you can do anything. Same with having the is_superuser flag set. As long as the impersonation middleware checks this flag, then it is as secure as doing something via the admin.
Matthew Schinckel