Take the 2-minute tour ×
Information Security Stack Exchange is a question and answer site for Information security professionals. It's 100% free, no registration required.

I have recently updated a website from Django 1.0 to 1.3. One of the changes introduced was automatic protection against CSRF attacks. For the most part, this works great, but I have a problem with clients that for some reason do not accept cookies at all. While it is OK to refuse such clients to register or log in, they still should be able to use e.g. a contact form. Django refuses all POST requests that do not have a CSRF cookie, so I have dozens of CSRF failures every day that are not an attack, but legitimate clients that do not accept cookies.

As this is not acceptable, my plan is to modify Django's behavior (through extension of the CSRF middleware) in such a way, that it lets through POST requests that do not have any cookies at all. My reasoning behind this is that CSRF attacks makes use of the session cookie to forge a request. But there is no session cookie attached to the request anyway, so no protection against CSRF is needed in this case.

So, is my reasoning sound or am I about to tear a huge security hole into my website?

share|improve this question
    
What is business impact of CSRF attack? Someone can trade on stock market? Add comment to blog? When you know what is the impact of the attack you can decide how/if you need to deal with it. –  AaronS Nov 7 '11 at 11:37
    
It's a community website revolving around cooking. So nothing "critical", but I'd like to keep it as secure as possible anyway –  Benjamin Wohlwend Nov 7 '11 at 11:42

4 Answers 4

I don't know of any devastating problem with your plan. However, let me spell out my reasoning and the potential risks, so you can form your own opinion about the level of risk associated with removing CSRF defenses.

Let me start with a question: Are you saying that your server is going to send an email anytime it receives a POST request, with no session cookie, no authentication, no authorization, from anyone in the world? Anyone in the world who sends you that POST request will cause the server to send an email, one email per POST request? If so, this means someone mischievous can cause your server to send tons of emails, spamming the poor recipient. Whether that's a serious problem or not, I don't know. You'll have to decide for yourself based on your own risk tolerance.

In general, any time a POST request can trigger some side effect on your server, or can cause your server to take some sensitive action, without requiring authentication, you might want to think carefully to make sure you're not opening yourself up to spam/denial-of-service attacks.

If you remove the CSRF protection, then you increase the risk. It means that an attacker can craft a malicious ad which, when viewed, causes the viewer's browser to automatically send a POST request to your server, triggering the side effect. This is a very cheap way that an attacker can generate lots of POST requests to your server, in a way that does not give any easy way to block the flood of malicious requests (since each request will be coming from a different IP address) or track down the attacker. CSRF tokens make this attack harder: now the attacker has to send the POST requests from his own machine, and can't exploit ads to use others as a patsy.

The added risk is pretty modest. Moreover, if you're running a low-value low-profile site, maybe no one will bother attacking you, so maybe this isn't worth worrying about. I just wanted to point out the risk, even if it turns out to be negligible in your particular situation.

share|improve this answer
    
Thanks for your input. The contact form I mentioned does contain a captcha, so a simple POST request will fail (captcha's are in no way unbreakable, I know, but it keeps away the script kiddies for the most part). Also, I'm not saying that I want to remove CSRF wholesale, just for POSTs that do not contain any session info anyway. I was under the impression that CSRF protection is mostly geared towards thwarting "session stealing", so why use it if there is no session in the first place, right? But I do now see that CSRF also helps mitigating bot attacks in general (although only slightly) –  Benjamin Wohlwend Nov 8 '11 at 10:41
    
@piquadrat, your analysis makes sense to me. I've edited my answer to tone down the level of concern and point out that the added risk is probably pretty modest. –  D.W. Nov 8 '11 at 21:42

I started to add a comment on Hendrik Brummermann's answer but quickly ran out of space, hence....

I do not understand why an absence of CSRF protection would affect logging of an IP address. And even if it did, in the absence of any session management, what relevance an IP address has to managing the integrity of a transaction?

Also, in order to avoid replays, the token would need to vary between transactions - so the server would need to generate new values for each operation and remember them between requests - implying a session - implying a cookie!

What's missing from the original question is details of the threat model - particularly in the absence of any session, what is it you are trying to protect?

Assuming that replay attacks are not a big issue, then you could just include both a random value (R) and the hash of that random value + salt (f(R+X)) as hidden fields. Embedding a timestamp in R reduces the window for any replay attack (when you add the machinery to check it).

A stateless solution could be implemented by using information supplied in each request, e.g. using the user agent and accept- headers and (say) the first 16 bits of the client IP address AND a secret salt.

Note that I've regularly pointed out the silliness of using IP address information for authentication - and a client IP address can change (e.g. when connecting via load-balanced proxies) however IME, the only time where the most significant bits of the IP address change is when the client is deliberately trying to obscure their identity via a service such as TOR - in which case you probably don't want their POSTs anyway.

There are other ways of making this user identifier more unique - e.g. negotiated SSL properties, the current cookie set (even if it is empty!),

See also panopticlick for more discussion on client fingerprinting - but do bear in mind that there is likely to be a lot of overlap between users whom don't accept cookies and users whom won't run your javascript.

Obviously whatever reference data you use for the check, if you want to implement this via Django, then you'll need to override both the token generator and the checker (CsrfViewMiddleware).

share|improve this answer
4  
"I do not understand why an absence of CSRF protection would affect logging of an IP address." In an CSRF-attack the logging contains the IP-address of the relay-victim, not the attacker. So taking legal actions against the owner of that IP-address (e. g. for anonymous posting of spam, denial of service attacks, insults, copyright violations, ...) may lead to the wrong person. –  Hendrik Brummermann Nov 7 '11 at 13:00
    
The basic property of an replay attack is that the attacker is able to record the original request. CSRF attacker are, however, not able to read any information from the attacked side in the context of the victim. Otherwise you cross the boarder to XSS attacks, which cannot be prevented with a CSRF token anyway. –  Hendrik Brummermann Nov 7 '11 at 13:05
1  
IP-addresses are basically the only way of identification an anonymous user that can be used by the authorities to track down a person with a bit of luck. All other tricks of cookie alternatives and browser fingerprinting will only allow you to links different request from the same source. But going to the police with this information is not going to help. –  Hendrik Brummermann Nov 7 '11 at 13:09
    
@Hendrik Brummermann: "In an CSRF-attack the logging contains the IP-address of the relay-victim, not the attacker" - no that's not a CSRF attack, that's a CSS or MITM attack –  symcbean Nov 7 '11 at 16:09
1  
@symcbean, I think you may be a bit confused about how CSRF attacks work. Hendrik is right. –  D.W. Nov 8 '11 at 8:18

Django refuses all POST requests

Only with the csrf mechanism enabled. Django issues csrf cookies in two cases:

  1. If you're using the csrf middleware in settings.py. Middleware applies to all requests as per the WSGI spec.
  2. If you decorate individual views with @csrf_protect, without the middleware

In the case of 1, all views are protected via this mechanism and a comparison of {% csrf_token %} (an input field in the form containing the csrf token) is compared against the cookie sent.

You have two options to disable this, therefore:

  1. Site wide - remove the middleware and your POST requests will start working again.
  2. Through careful use of @csrf_exempt you can opt certain views (POST urls) out of the csrf protection mechanism.

So is not using csrf protection a risk? It depends on what your POST request is. Have a read of the description of csrf from OWASP - basically the idea is that you construct a request and convince a user to execute it; when they do, their data is modified in ways they didn't mean to happen. The idea behind the token comparison is that only the issuing web page will have the valid token, meaning a malicious constructed request should be rejected.

As such, the answer to this is yes, it most likely is a risk. For most web based applications and sites, you do want this kind of protection.

my plan is to modify Django's behavior (through extension of the CSRF middleware) in such a way, that it lets through POST requests that do not have any cookies at all.

For the above reasons I would advise against that. I think you mean to extend from the existing framework (which you don't need to do), but I just want to cover the other case other users might consider, i.e. patching the django framework itself to do this. In which case... don't, because unless each site sits in its own virtualenv you'll be extending this vulnerability to all sites using that code, now and until you update your django install.

share|improve this answer
    
For the record: the plan was to write a middleware that inherits from CsrfViewMiddleware and overrides process_view, checking if request.COOKIES is empty. If empty, accept the request. If not, let process_view of the superclass decide. –  Benjamin Wohlwend Nov 7 '11 at 12:03
    
@piquadrat I thought as much... still, I felt I really ought to cover the other case, just in case someone decided to do it...! –  user2213 Nov 7 '11 at 12:38

Removing CSRF protection completely means that you cannot trust logging information to show the real source of a request (e. g. IP-address). This may be okay in your case.

An alternative is to add some random information to a hidden field.

Then concatenate this information with a secret string, that is only known to the server. The hash can be used as a stateless CSRF token. Depending on your use case, it may be a good idea to add a timestamp to the hidden field and hash, then check that it is not older than half an hour for example.

To verify the token, just calculate the hash again based on the hidden field and secret.

share|improve this answer
    
See comments/answer –  symcbean Nov 7 '11 at 12:35
1  
Surely that doesn't prevent CSRF, as you can just have the user pass that too... –  Kristian Glass Jul 8 '12 at 14:44

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.