Django - How to do CSFR on public pages? Or, better yet, how should it be used period? - django

After reading this: http://docs.djangoproject.com/en/dev/ref/contrib/csrf/#how-to-use-it
I came to the conclusion that it is not valid to use this except for when you trust the person who is using the page which enlists it. Is this correct?
I guess I don't really understand when it's safe to use this because of this statement:
This should not be done for POST forms
that target external URLs, since that
would cause the CSRF token to be
leaked, leading to a vulnerability.
The reason it's confusing is that; to me an "external URL" would be page on that isn't part of my domain (ie, I own www.example.com and put a form that posts to www.spamfoo.com. This obviously can't be the case since people wouldn't use Django for generating forms that post to other people's websites, but how could it be true that you can't use CSRF protection on public forms (like a login form)?

With apologies to not understanding the specific source of your confusion, I'll say that the question you should be asking is when NOT to use CSRF protection. You've already called out this case from the docs:
This should not be done for POST forms
that target external URLs, since that
would cause the CSRF token to be
leaked, leading to a vulnerability.
If you are posting a form to your domain, you'll want CSRF protection enabled by default, unless you have a specific reason to disable it (which should be more rare than not).

Related

Django - using captcha on third party applications

My Django site is vulnerable to spam, so I would like to include captcha protection. The problem is that a lot of components are from third party sources, and don't include captcha verification. While i could modify their views, that would effect the code's portability/upgradability, so I would rather not. I can only modify their templates.
I have solution, but it is a bit messy, and before I start implementing, I want to see if anyone has a better one.
My solution is as follows:
1) Redirect all Post forms on the site to targert a "captcha bounce" app that will programatically reconstruct their post request, check if the captcha is correct, add a verification token to the post, then redirect back to the original post target.
2) Implement a new type of middleware that will check all post requests. If the post request is not targeting the "captcha bounce" url, the middleware will return an error unless the verification token is present.
As i said before, this seems needlessly messy. Does anyone have a better idea?
It depends on app message but:
you can usually wrap their views into your views and add extra processing
you can extend their forms to add your captcha fields
you can add template-tag that is extra javascript check and just add it to your templates.
Ok, I figured it out. This should be doable just by using middleware. Just give it a list of views that it should check for captcha correctness. In the case of a wrong response, it cuts the post out of the request and/or activates some sort of error variable. Much simpler.

django + varnish caching not being stored due to csrf tokens

I have a login form on my homepage. This is causing a csrf token to be set and this is meaning that my page is not being stored in the varnish cache.
How should I get around this?
You have two main courses: either give up using varnish to cache the page, or give up having a login form on the page. The CSRF token (which you don't want to lose) will prevent you from successfully caching the page in varnish; even if you take it into account, you're going to end up with a copy of the page for every single person visiting, which defeats the purpose of using varnish.
A solution halfway between the two is to cache the page without login form in varnish, and then insert the login form using Javascript. The main page will be cached, and you should be able to make the login form (pulled via AJAX) fast enough to not cause you problems. Another possible solution would involve putting the login form in an IFRAME, although that complicates matters yet further since you'd have to have something in the response to logging in which worked with Javascript in the main page (that included the IFRAME in the first place) to reload it suitably.

Django: Login from page outside django

Maybe it's a stupid question, but I'm trying to login to my django app using a form that is outside django. My guess is that I could send a POST request to /login, but that would fail because of the csrf token.
Maybe I'm missing some kind of theoretical background, but I would like to know what's the correct way to achieve this.
Background info:
The django authentication is working fine IF you use the django login forms. What I'd like to do is to use an external static html form (on an apache outside django), to post to django directly so when I redirect to my django server, I don't have to login.
CSRF exists to prevent exactly this. Although you no doubt have good intentions, there's no technical difference between this and a hacker trying to steal access to your site via a real CSRF attack.
Sounds like you need a single-signon service like CAS: http://code.google.com/p/django-cas/
(but it's possible overkill)

Django request paths

I've been working through an issue with my django project. The issue is I've got one project, which will retrieve data for users of different clients. I need to know 'from where' a viewer is coming from (request.path [my original solution]). I've been looking at a number of different options which sound close to what I want to do, but I'm not sure what the best option is, not having done this before.
My first option was to add a url in the urls.py with a 'tag' or 'keyword' then look for that tag/keyword in the request.path, which I'd add as a session key. Then go onto get the data.
Something else I started looking at was the sites framework. After reading through the documentation, I'm still confused how sites actually works, so I'm not sure if this is the right option.
Another solution talked about using middleware, this came up in connection with the research into using the sites framework.
And then yet another talked about doing this in apache.
Could some one help point me in the right direction?
Cheers,
T
If you need to know from which URL came your user to your currrent page you should check the REFERER http header, available in request.META.get('HTTP_REFERER').
See http://docs.djangoproject.com/en/1.2/ref/request-response/#ref-request-response for more informations.
Be careful though, the referer meta is not mandatory and could be missing due to private browsing or direct access to the page from the URL bar.
It's not completely clear from your question, but if you're asking for the URL that the user was on before coming to the current page, you probably want request.META['HTTP_REFERRER'].
Edit after comment
That would be a very bad idea. Global variables are not safe given that you potentially have multiple requests being processed at the same time. The referrer is already available from the request, which can be accessed in all views and templates, so I don't know what else a middleware would give you.

Django Admin - Re-authentication?

I'm in a bit of a dilemma at the moment regarding Django's admin backend. The default authentication system allows already logged-in users that have staff privileges to access the admin site, however it just lets them straight in.
This doesn't feel “right” to me, and I'm wondering if it would be difficult to at least require a re-authentication of that same session in order to get into the backend.
Preferably though, it'd be good if the frontend sessions could be separated from the backend ones (though still using the same user objects), this would allow a clean separation of both parts of the site. Would this perhaps require two separate authentication backends? Would something like this be difficult to achieve?
Here's an idea: run the admin app on a different domain to the frontend. The cookies won't be valid in the other domain, so the user will have to log in again. All you'd need would be a separate Apache vhost and a basic settings.py that just has contrib.admin in INSTALLED_APPS.
You could probably implement a middleware that asks for authentication when accessing the admin site from a referer not in the admin site. It could log the person out and make them log back in, but even that wouldn't be necessary. Just require another password entry, and redirect them if it fails. It might involve setting a session variable, is_admin_authenticated or something.