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.

Reply via email to