On Mon, Sep 6, 2010 at 8:25 PM, Luke Plant <l.plant...@cantab.net> wrote: > Almost everything in this discussion and all your misconceptions are > covered here: > > http://code.djangoproject.com/wiki/CsrfProtection
I've read it before joining the discussion. > For HTTP (not HTTPS), MitM attacks are out of scope for our CSRF > protection, because a MitM sees everything in the clear and will easily > be able to defeat any defences we put up. Your suggested alterations do > nothing to stop that. I wasn't targetting HTTP at all. But I can't agree that there's nothing we can do. We can encode TTL into the CSRF cookie¹ to make sure being able to eavesdrop at one point in time does not grant you permanent access to the server. > In the context of HTTPS, the Referer header check does indeed add a > necessary and effective protection. The nature of a CSRF attack is that > the attacker has to gets the *user's* browser to make the request, not > the attacker's. Plain CSRF is not interesting to me, requiring any non-trivial token is enough to prevent blind CSRF attacks. I am more interested in preventing an attack that consists of eavesdropping a cookie sent over HTTP and using it to forge a POST using a secure connection. > The curl command line says nothing at all - I'm well > aware that the attacker can do that from their own machine if they want > to, but it won't do them any good! I could not understand how you > thought that showing a curl command line is at all relevant. See above, I'm concerned about the case when the attacker is able to perform a replay attack. If you validate CSRF against the client's IP (and possibly against a TTL field¹), you effectively prevent that type of attack. If the CSRF tokens were stored in the database (much like sessions are), you'd gain additional security of all tokens expiring after one use (or TTL reached). > A MitM cannot tamper with requests that are sent over SSL. The only > exception to this is that if HTTP -> HTTPS POST requests are allowed, > the MitM can indeed generate any request they like and get the user's > browser to send it, with potentially damaging consequences. The MitM can also trick the user into POSTing same data over HTTP and forge a HTTPS request of his own. That's why I propose using a separate, secure CSRF cookie (using a different name) for HTTPS requests. Even if you trick the user into sending data over HTTP, you will not gain access to the secure token. Any views that require HTTPS will not be exploited no matter what the attacker sends to the browser. > To stop > this we, we simply refuse HTTP -> HTTPS POST requests. In the context of > an HTTPS request from a browser, we can indeed trust the Referer header > since no-one can tamper with it in transit, and the MitM will not be > able to convincingly send the user's browser a page that looks like it > comes from https://example.com. I believe you can use mere cookies to prevent this (see above) and keep the site working for people who don't and can't send Referer headers. > Please distinguish between the HTTP/HTTPS context if you've still got > questions. As I said, I am only concerned about using HTTP as an attack vector to access HTTPS. ¹ Consider the following example. It was based on the code I wrote to handle the new Facebook API so it might be a bit of an overkill but it shows it's possible. A simpler solution would be to return (TTL + ':' + md5(token + user_ip + secret_key + TTL)). def encode_cookie(token): values = { 'TTL': time.time() + 60*60, 'token': token, } payload = simplejson.dumps(values) digest = hmac.new(settings.SECRET_KEY, payload, hashlib.sha256).digest() encoded_payload = base64.b64encode(payload) encoded_digest = base64.b64encode(digest) return '%s.%s' % (encoded_digest, encoded_payload) def decode_cookie(value): encoded_sig, encoded_payload = map(str, value.split('.', 1)) sig = base64.b64decode(encoded_sig) payload = base64.b64decode(encoded_payload) data = simplejson.loads(payload) digest = hmac.new(settings.SECRET_KEY, payload, hashlib.sha256).digest() if str(digest) != sig: return None if data.get('TTL', 0) < time.time(): return None return data.get('token', None) -- Patryk Zawadzki -- You received this message because you are subscribed to the Google Groups "Django developers" group. To post to this group, send email to django-develop...@googlegroups.com. To unsubscribe from this group, send email to django-developers+unsubscr...@googlegroups.com. For more options, visit this group at http://groups.google.com/group/django-developers?hl=en.