Adding signing (and signed cookies) to Django core

View: New views
20 Messages — Rating Filter:   Alert me  
< Prev | 1 - 2 | Next >

Adding signing (and signed cookies) to Django core

by Simon Willison-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


As mentioned in the thread about cookie-based notifications, at the
DjangoCon Sprints I raised the subject of adding signing (and signed
cookies) to Django core.

I've found myself using signing more and more over time, and I think
it's a concept which is common enough to deserve inclusion in Django -
if anything, its use should be actively encouraged by the framework.

It's also something that's hard to do correctly. At the sprints Armin
pointed out that I should be using hmac, not straight sha1, for
generating signatures (something Django itself gets wrong in the few
places that implement signing already). Having a cryptographer-
approved implementation will save a lot of people from making the same
mistakes.

Signed cookies
==============

On top of signing (which I imagine would live in django.utils) I'd
like to add a signed cookie implementation. Signed cookies are useful
for all sorts of things - most importantly, they can be used in place
of sessions in many places, which improves performance (and overall
scalability) by removing the need to access a persistent session
backend on every hit. Set the user's username in a signed cookie and
you can display "Logged in as X" messages on every page without any
persistence layer calls at all.

I think signed cookies should either be a separate API from
response.set_cookie or should be provided as an additional argument to
that method. I'm not a fan of signing using middleware (as seen in
http://code.google.com/p/django-signedcookies/ ) since that approach
signs everything - some cookies, such as those used by Google
Analytics, need to remain unsigned.

So the API could either be:

    response.set_signed_cookie(key, value)

Or...

    response.set_cookie(key, value, signed=True)

(I prefer the latter option)

Proposed signing implementation
===============================

I'd be happy to donate my signing code from django-openid to the
cause, which was written to be usable entirely separately from the
rest of the django-openid codebase:

http://github.com/simonw/django-openid/blob/master/django_openid/signed.py
http://github.com/simonw/django-openid/blob/master/django_openid/tests/signing_tests.py

This offers two APIs: sign/unsign and dumps/loads. sign and unsign
generate and append signatures to bytestrings and confirm that they
have not been tampered with. dumps and loads can be used to create
signed pickles of arbitrary Python objects.

Here's what the API would look like with this library:

>>> from django.utils import signed
>>> signed.sign('hello')
'hello.9asVJn9dfv6qLJ_BYObzF7mmH8c'

The signature is a URL-safe base64 encoded digest of the hmac/sha1. I
used base64 rather than .hexdigest() for space reasons - base64
digests are 27 characters, hexadecimal digests are 40. When you're
including signatures in cookies and URLs (especially account recovery
URLs sent out in plain text, 80 character wide e-mails) every byte
counts.

>>> signed.unsign('hello.9asVJn9dfv6qLJ_BYObzF7mmH8c')
'hello'
>>> signed.unsign('hello.badsignature')
Traceback (most recent call last):
...
BadSignature: Signature failed: badsignature

BadSignature is a subclass of ValueError, meaning lazy developers
(like myself) can do the following rather than importing the exception
itself:

try:
    value = signed.unsign(signed_value)
except ValueError:
    return tamper_error_view(request)

>>> signed.dumps({"a": "foo"})
'KGRwMApTJ2EnCnAxClMnZm9vJwpwMgpzLg.mYepoYkzWwXRmsCTVJm3Mb0HHz4'
>>> signed.loads(_)
{'a': 'foo'}

Again, the pickle is URL-safe base64 encoded to take up less valuable
cookie space and generally make it easier to pass around on the Web. A
nice thing about URL-safe base64 is that it uses 64 out of the 65 URL-
safe characters (by URL-safe I mean characters that are left unchanged
by Python's urllib.urlencode function) - the remaining character is
the period, which I use to separate the pickle from the signature.

signed.dumps takes a couple of extra optional arguments. The first is
compress=True (default is False) which zlib compresses the pickle if
doing so will save any space:

>>> import this # to get an object worth compressing
...
>>> len(signed.dumps(this.s))
1207
>>> len(signed.dumps(this.s, compress=True))
637

By default, all signatures use Django's SECRET_KEY. If you want to
sign with a different key, you can pass it as an argument to the
various functions:

>>> signed.sign('hello', key='sekrit')
'hello.o6MKehoOfZ2b2FU84wzibW6IWxI'
>>> signed.unsign(_, key='sekrit')
'hello'

The dumps and loads methods also take a key argument, as well as an
additional optional extra_key argument for if you want to generate
different signatures for different parts of your application (useful
for the extra paranoid):

>>> signed.dumps('hello', extra_key='ultra')
'UydoZWxsbycKcDAKLg.1XYDpILo5xqSwImfa3WuJJT4RPo'
>>> signed.loads(_, extra_key='ultra')
'hello'

We'd want to get a proper cryptographer to give this the once-over
before adding it to core, but I'm generally happy with the API. It
could be argued that it's over kill and just sticking signed.sign and
signed.unsign in would be enough, but I'm pretty keen on the
convenience of dumps and loads.

Thinking about it further, an additional API that just gives you the
signature without including the original value would mean it could be
used for hashing passwords as well.

Potential uses
==============

Lots of stuff:

- Signed cookies (obviously)
- Generating CSRF tokens
- Secure /logout/ and /change-language/ links
- Securing /login/?next=/some/path/
- Securing hidden fields in form wizards
- Recover-your-account links in e-mails

We already use signing in a few places in Django core (mainly sessions
and form wizards), currently using md5 without hmac - sha1/hmac would
be an instant improvement.

SECRET_KEY considerations
=========================

One thing that worries me slightly about increasing the amount of
signing going on in Django is that it elevates the importance of the
SECRET_KEY. I'm currently ignorant of best practices regarding
protecting this kind of shared secret, but the steps we take (***ing
it out from the debug pages and otherwise ignoring it) could almost
certainly be improved.

One thing that's particularly interesting to me is what happens when
you change your secret. If you're changing your secret because it's
leaked then obviously you want stuff signed with the old secret to
become invalid immediately, but I can imagine some users wanting to
rotate their secret keys on a continual basis for added security
against brute force attacks.

If you're rotating your secret, invalidating all of your users signed
cookies etc is a bit of an annoyance. It might be worth supporting two
secrets - the current SECRET_KEY and an optional OLD_SECRET_KEY - with
unsigning operations falling back on the old key if the current key
fails. This would allow users to deploy a new secret while keeping the
old one valid for a week or so, upgrading any tokens that use the old
key in the process. This suggestion is inspired by Amazon's recent
announcement of a similar feature for handling web service access
credentials:

http://aws.typepad.com/aws/2009/09/aws-access-credential-rotation.html

This is probably all too much complication, but it's something that's
been nagging at me since I started increasing my dependence on the
SECRET_KEY setting.

So... what do people think? Is this a feature suitable for Django
(obviously I think so)? Is this as simple as getting a cryptographer's
input and dropping signed.py in to django.utils or are there other
design factors we should consider?

Cheers,

Simon
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to django-developers@...
To unsubscribe from this group, send email to django-developers+unsubscribe@...
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en
-~----------~----~----~----~------~----~------~--~---


Re: Adding signing (and signed cookies) to Django core

by tmcnulty1982 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message

+1 for signed cookies.  Your API looks reasonable and I'd agree that set_cookie(..., signed=True) fits better with the rest of the API as well.

What about some sanity checking to make sure that, if SECRET_KEY is used, it is, at the very least, a non-empty string?

On Thu, Sep 24, 2009 at 1:18 PM, Simon Willison <simon@...> wrote:

As mentioned in the thread about cookie-based notifications, at the
DjangoCon Sprints I raised the subject of adding signing (and signed
cookies) to Django core.

I've found myself using signing more and more over time, and I think
it's a concept which is common enough to deserve inclusion in Django -
if anything, its use should be actively encouraged by the framework.

It's also something that's hard to do correctly. At the sprints Armin
pointed out that I should be using hmac, not straight sha1, for
generating signatures (something Django itself gets wrong in the few
places that implement signing already). Having a cryptographer-
approved implementation will save a lot of people from making the same
mistakes.

Signed cookies
==============

On top of signing (which I imagine would live in django.utils) I'd
like to add a signed cookie implementation. Signed cookies are useful
for all sorts of things - most importantly, they can be used in place
of sessions in many places, which improves performance (and overall
scalability) by removing the need to access a persistent session
backend on every hit. Set the user's username in a signed cookie and
you can display "Logged in as X" messages on every page without any
persistence layer calls at all.

I think signed cookies should either be a separate API from
response.set_cookie or should be provided as an additional argument to
that method. I'm not a fan of signing using middleware (as seen in
http://code.google.com/p/django-signedcookies/ ) since that approach
signs everything - some cookies, such as those used by Google
Analytics, need to remain unsigned.

So the API could either be:

   response.set_signed_cookie(key, value)

Or...

   response.set_cookie(key, value, signed=True)

(I prefer the latter option)

Proposed signing implementation
===============================

I'd be happy to donate my signing code from django-openid to the
cause, which was written to be usable entirely separately from the
rest of the django-openid codebase:

http://github.com/simonw/django-openid/blob/master/django_openid/signed.py
http://github.com/simonw/django-openid/blob/master/django_openid/tests/signing_tests.py

This offers two APIs: sign/unsign and dumps/loads. sign and unsign
generate and append signatures to bytestrings and confirm that they
have not been tampered with. dumps and loads can be used to create
signed pickles of arbitrary Python objects.

Here's what the API would look like with this library:

>>> from django.utils import signed
>>> signed.sign('hello')
'hello.9asVJn9dfv6qLJ_BYObzF7mmH8c'

The signature is a URL-safe base64 encoded digest of the hmac/sha1. I
used base64 rather than .hexdigest() for space reasons - base64
digests are 27 characters, hexadecimal digests are 40. When you're
including signatures in cookies and URLs (especially account recovery
URLs sent out in plain text, 80 character wide e-mails) every byte
counts.

>>> signed.unsign('hello.9asVJn9dfv6qLJ_BYObzF7mmH8c')
'hello'
>>> signed.unsign('hello.badsignature')
Traceback (most recent call last):
...
BadSignature: Signature failed: badsignature

BadSignature is a subclass of ValueError, meaning lazy developers
(like myself) can do the following rather than importing the exception
itself:

try:
   value = signed.unsign(signed_value)
except ValueError:
   return tamper_error_view(request)

>>> signed.dumps({"a": "foo"})
'KGRwMApTJ2EnCnAxClMnZm9vJwpwMgpzLg.mYepoYkzWwXRmsCTVJm3Mb0HHz4'
>>> signed.loads(_)
{'a': 'foo'}

Again, the pickle is URL-safe base64 encoded to take up less valuable
cookie space and generally make it easier to pass around on the Web. A
nice thing about URL-safe base64 is that it uses 64 out of the 65 URL-
safe characters (by URL-safe I mean characters that are left unchanged
by Python's urllib.urlencode function) - the remaining character is
the period, which I use to separate the pickle from the signature.

signed.dumps takes a couple of extra optional arguments. The first is
compress=True (default is False) which zlib compresses the pickle if
doing so will save any space:

>>> import this # to get an object worth compressing
...
>>> len(signed.dumps(this.s))
1207
>>> len(signed.dumps(this.s, compress=True))
637

By default, all signatures use Django's SECRET_KEY. If you want to
sign with a different key, you can pass it as an argument to the
various functions:

>>> signed.sign('hello', key='sekrit')
'hello.o6MKehoOfZ2b2FU84wzibW6IWxI'
>>> signed.unsign(_, key='sekrit')
'hello'

The dumps and loads methods also take a key argument, as well as an
additional optional extra_key argument for if you want to generate
different signatures for different parts of your application (useful
for the extra paranoid):

>>> signed.dumps('hello', extra_key='ultra')
'UydoZWxsbycKcDAKLg.1XYDpILo5xqSwImfa3WuJJT4RPo'
>>> signed.loads(_, extra_key='ultra')
'hello'

We'd want to get a proper cryptographer to give this the once-over
before adding it to core, but I'm generally happy with the API. It
could be argued that it's over kill and just sticking signed.sign and
signed.unsign in would be enough, but I'm pretty keen on the
convenience of dumps and loads.

Thinking about it further, an additional API that just gives you the
signature without including the original value would mean it could be
used for hashing passwords as well.

Potential uses
==============

Lots of stuff:

- Signed cookies (obviously)
- Generating CSRF tokens
- Secure /logout/ and /change-language/ links
- Securing /login/?next=/some/path/
- Securing hidden fields in form wizards
- Recover-your-account links in e-mails

We already use signing in a few places in Django core (mainly sessions
and form wizards), currently using md5 without hmac - sha1/hmac would
be an instant improvement.

SECRET_KEY considerations
=========================

One thing that worries me slightly about increasing the amount of
signing going on in Django is that it elevates the importance of the
SECRET_KEY. I'm currently ignorant of best practices regarding
protecting this kind of shared secret, but the steps we take (***ing
it out from the debug pages and otherwise ignoring it) could almost
certainly be improved.

One thing that's particularly interesting to me is what happens when
you change your secret. If you're changing your secret because it's
leaked then obviously you want stuff signed with the old secret to
become invalid immediately, but I can imagine some users wanting to
rotate their secret keys on a continual basis for added security
against brute force attacks.

If you're rotating your secret, invalidating all of your users signed
cookies etc is a bit of an annoyance. It might be worth supporting two
secrets - the current SECRET_KEY and an optional OLD_SECRET_KEY - with
unsigning operations falling back on the old key if the current key
fails. This would allow users to deploy a new secret while keeping the
old one valid for a week or so, upgrading any tokens that use the old
key in the process. This suggestion is inspired by Amazon's recent
announcement of a similar feature for handling web service access
credentials:

http://aws.typepad.com/aws/2009/09/aws-access-credential-rotation.html

This is probably all too much complication, but it's something that's
been nagging at me since I started increasing my dependence on the
SECRET_KEY setting.

So... what do people think? Is this a feature suitable for Django
(obviously I think so)? Is this as simple as getting a cryptographer's
input and dropping signed.py in to django.utils or are there other
design factors we should consider?

Cheers,

Simon



--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to django-developers@...
To unsubscribe from this group, send email to django-developers+unsubscribe@...
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en
-~----------~----~----~----~------~----~------~--~---


Re: Adding signing (and signed cookies) to Django core

by Marty Alchin :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


I'm obviously going to weigh in here, having authored the signed
cookies app you mentioned below, but be aware I'm not a cryptographer,
nor do I have any personal use for signed cookies at all. I can
appreciate their value, but I'm not even using my own app in anything,
so if there are problems with it, I haven't experienced them
first-hand. I won't comment on everything, because I certainly trust
your thoughts on the matter. (Your comment on the Django book is what
prompted me to write the app in the first place!) I do have a couple
thoughts, though. So, anything I don't comment on, I agree with.

On Thu, Sep 24, 2009 at 1:18 PM, Simon Willison <simon@...> wrote:
> It's also something that's hard to do correctly. At the sprints Armin
> pointed out that I should be using hmac, not straight sha1, for
> generating signatures (something Django itself gets wrong in the few
> places that implement signing already). Having a cryptographer-
> approved implementation will save a lot of people from making the same
> mistakes.

I have no idea how hmac differs from straight sha1, but this was
raised in a django-signedcookies issue as well, and i've since
integrated it. I'm with you on this; if somebody who knows better
recommends something, I'm inclined to listen.

> I think signed cookies should either be a separate API from
> response.set_cookie or should be provided as an additional argument to
> that method. I'm not a fan of signing using middleware (as seen in
> http://code.google.com/p/django-signedcookies/ ) since that approach
> signs everything - some cookies, such as those used by Google
> Analytics, need to remain unsigned.

I admit, I hadn't considered third-party cookies when I put it
together as a decorator. Client-side cookie access is likely
problematic as well, but that'll always be questionable anyway. You
can't validate a cookie in the client without divulging your secret
key and you can't just ignore the signature, because that defeats the
whole purpose. My app also provides a decorator, which might help in
some rare situations, but most of the time things like analytics
cookies will be in a base template and will always be included in
whatever view happens to have the decorator. I'm very surprised that
in all this time, nobody submitted a bug about the analytics problem.

> So the API could either be:
>
>    response.set_signed_cookie(key, value)
>
> Or...
>
>    response.set_cookie(key, value, signed=True)
>
> (I prefer the latter option)

I prefer the latter as well, for an added reason. I'd personally like
to invest some time in seeing if there are any other interesting
pitfalls in set_cookie() based on it deferring to Python's
SimpleCookie implementation. When writing Pro Django, I realized that
SimpleCookie expects everything to be strings, so #6657 came up with
secure=False resulting in a secure cookie after all. I don't know if
there are other such issues, but it might be worth looking at in
detail if we already have to add in signed cookie support.

And before anyone asks, no I don't think tying the signing behavior
into secure=True would be a good idea. Secure is meant to tell the
browser it should only send the cookie back over a secure channel,
such as HTTPS. While people who need secure cookies may also want
signed cookies, they're two separate ideas that I don't think would do
well mixed together. Of course, that leaves us with a potential
response.set_cookie(key, value, secure=True, signed=True), but I think
it's worth it to be explicit.

But I do have one other concern with either of these APIs that is at
least worth discussing: the disparity between setting a signed cookie
and retrieving one.

response.set_cookie(key, value, signed=True)
value = signed.unsign(request.COOKIES[key])

Mainly, unsigning a cookie requires importing and directly using the
signing module anyway, so the benefit of having set_cookie() handle it
transparently is fairly weak (unless, of course, I'm missing something
obvious). I'd rather just see the signing code made available and
well-documented, so that at least the change in code when adding
signed cookies is equivalent on both sides.

Another option would be to have request.COOKIES be a custom
dictionary, with an extra .get_unsigned(key) method that would work
like .get(), but validates the signature along the way. That behavior
can't be added straight to __getitem__() though, because that has no
way of knowing whether the cookie was signed to begin with.

These are the types of issues that led me to just implement it as a
middleware, so the API could be as dead simple as possible. With these
new issues in mind, I don't think dead simplicity is really an option,
so I'd rather fall back to being explicit.

>>>> signed.unsign('hello.9asVJn9dfv6qLJ_BYObzF7mmH8c')
> 'hello'
>>>> signed.unsign('hello.badsignature')
> Traceback (most recent call last):
> ...
> BadSignature: Signature failed: badsignature
>
> BadSignature is a subclass of ValueError, meaning lazy developers
> (like myself) can do the following rather than importing the exception
> itself:
>
> try:
>    value = signed.unsign(signed_value)
> except ValueError:
>    return tamper_error_view(request)

I'm not a big fan of this, personally. Yes, it does make things
slightly easier, by not requiring a separate import, but we already
have django.core.exceptions.SuspiciousOperation exists for a reason,
and a missing or invalid signature would certainly qualify, in my
eyes. Yeah, I suppose we could perhaps make that a subclass of
ValueError or TypeError, but I would worry about people wrapping it up
into something else that might cause problems.

try:
    value = signed.unsign(signed_value).decode('utf-8')
except ValueError:
    # Whoops! UnicodeDecodeError winds up here as well!

I don't know how likely this is to happen, since the only examples I
could come up with offhand are:
 * Unicode errors, which could possibly be handled by automatic
Unicode translating in the signing code
 * Using int() for things like user IDs, in which case a non-integer
would be evidence of tampering anyway.

Of course, you could just as easily argue that people shouldn't be
doing that, or if they do, they should import BadSignature (or
whatever) and give it its own except block ahead of ValueError so the
two can be distinguished. But of course, that expects a non-lazy
programmer, and if we're trying to make it easier for lazy
programmers, I don't think the behavior of non-lazy ones matters much.

> Potential uses
> ==============
>
> Lots of stuff:
>
> - Signed cookies (obviously)
> - Generating CSRF tokens
> - Secure /logout/ and /change-language/ links
> - Securing /login/?next=/some/path/
> - Securing hidden fields in form wizards
> - Recover-your-account links in e-mails

I think this is a big win for including signing as a separate piece
from signed cookies, because you have a much bigger list of use cases,
which can make it easier for people to understand the value. One of
the most common questions I got on my app was, "why would I want to
sign my cookies?" I think expanding the available uses signing will
help answer that question.

> So... what do people think? Is this a feature suitable for Django
> (obviously I think so)? Is this as simple as getting a cryptographer's
> input and dropping signed.py in to django.utils or are there other
> design factors we should consider?

All in all, I understand and appreciate the benefits of signing
things, and I'd like that behavior to be made available reliably and
easily, and it looks like that would require them being in core. I'm a
little worried about ease of use, though, because it seems like most
of the ways to make things easy can also make things either more
confusing or less flexible. I'm a little scattered on the moment, but
I think it's definitely worth refining into something that can go into
core.

-Gul

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to django-developers@...
To unsubscribe from this group, send email to django-developers+unsubscribe@...
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en
-~----------~----~----~----~------~----~------~--~---


Re: Adding signing (and signed cookies) to Django core

by Simon Willison-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


On Sep 24, 7:37 pm, Marty Alchin <gulop...@...> wrote:
> Another option would be to have request.COOKIES be a custom
> dictionary, with an extra .get_unsigned(key) method that would work
> like .get(), but validates the signature along the way. That behavior
> can't be added straight to __getitem__() though, because that has no
> way of knowing whether the cookie was signed to begin with.

Hmm... I hadn't considered that. I was thinking that the unsigning
could be transparent, so by the time you access request.COOKIES['key']
the value had already been unsigned (and if the signature failed the
cookie key just wouldn't be set at all, as if the cookie never
existed). But as you point out, this doesn't work because you can't
tell if the cookie was signed or not in the first place.

We could fix this with a naming convention of some sort:

response.set_cookie('key', 'value', sign=True)
- results in a Set-Cookie: key__Xsigned=value header

But that's pretty ugly. Not sure what to do about this one -
request.unsign_cookie('key') might be an option, as at least that
reflects the set_cookie / sign / unsign API a bit. Not ideal by a long
shot though.

> eyes. Yeah, I suppose we could perhaps make that a subclass of
> ValueError or TypeError, but I would worry about people wrapping it up
> into something else that might cause problems.
>
> try:
>     value = signed.unsign(signed_value).decode('utf-8')
> except ValueError:
>     # Whoops! UnicodeDecodeError winds up here as well!

That's a great argument against subclassing ValueError - I hadn't
considered the unicode case. You're right, if anything it should
subclass SuspiciousOperation instead.

Cheers,

Simon

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to django-developers@...
To unsubscribe from this group, send email to django-developers+unsubscribe@...
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en
-~----------~----~----~----~------~----~------~--~---


Re: Adding signing (and signed cookies) to Django core

by Marty Alchin :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


On Thu, Sep 24, 2009 at 2:54 PM, Simon Willison <simon@...> wrote:
> Hmm... I hadn't considered that. I was thinking that the unsigning
> could be transparent, so by the time you access request.COOKIES['key']
> the value had already been unsigned (and if the signature failed the
> cookie key just wouldn't be set at all, as if the cookie never
> existed). But as you point out, this doesn't work because you can't
> tell if the cookie was signed or not in the first place.

The behavior you mention here is exactly what django-signedcookies
does, but it can only do so because it can blindly assume that all
cookies are signed, which as you pointed out, causes other problems.

> We could fix this with a naming convention of some sort:
>
> response.set_cookie('key', 'value', sign=True)
> - results in a Set-Cookie: key__Xsigned=value header

That seems pretty ugly on the surface, but it does confine the
ugliness to somewhere most people won't bother to look. One potential
problem is if someone wants to use __Xsigned in the name of an
unsigned cookie, but a namespace clash like that should be extremely
rare in practice.

Also, does the name of a cookie factor into the cookie length limits?
My reading of RFC 2109 says yes, but it'd be worth verifying, since it
would cut down on the usable value space. With your compressed base64
stuff, that's not as big of a problem, but still something to look
into.

> request.unsign_cookie('key') might be an option, as at least that
> reflects the set_cookie / sign / unsign API a bit. Not ideal by a long
> shot though.

>> try:
>>     value = signed.unsign(signed_value).decode('utf-8')
>> except ValueError:
>>     # Whoops! UnicodeDecodeError winds up here as well!
>
> That's a great argument against subclassing ValueError - I hadn't
> considered the unicode case. You're right, if anything it should
> subclass SuspiciousOperation instead.

I don't know if it's completely anti-ValueError, because a ValueError
subclass does still make some sense semantically, and since you can
catch more than one exception type in a try block, it's perfectly
functional. It's just that when lazy people blindly catch ValueError
without checking for something more specific as well, things can
break.

So it really just comes down to whether we expect people to be
thorough or lazy. Hrm. Yeah, I guess that answers it. :)

-Gul

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to django-developers@...
To unsubscribe from this group, send email to django-developers+unsubscribe@...
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en
-~----------~----~----~----~------~----~------~--~---


Re: Adding signing (and signed cookies) to Django core

by Benjamin Slavin-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


The lack of parallelism in terms of the interface is my biggest hangup here.

I do think that this should find it's way into trunk, as signed
cookies are important in the use cases you mention and are really easy
to get wrong... and getting it wrong can be dangerous.

I'm not going to get into the dumps/loads bit right now because
there's enough to tackle on signed cookies.


On Thu, Sep 24, 2009 at 2:54 PM, Simon Willison <simon@...> wrote:

> Hmm... I hadn't considered that. I was thinking that the unsigning
> could be transparent, so by the time you access request.COOKIES['key']
> the value had already been unsigned (and if the signature failed the
> cookie key just wouldn't be set at all, as if the cookie never
> existed). But as you point out, this doesn't work because you can't
> tell if the cookie was signed or not in the first place.
>
> We could fix this with a naming convention of some sort:
>
> response.set_cookie('key', 'value', sign=True)
> - results in a Set-Cookie: key__Xsigned=value header

Unfortunately, this approach won't work.

A malicious client can just send "key" rather than "key__Xsigned" and
you'll never know that the cookie hasn't gone through validation.
This also means that you can't look for cookie values that match a
specific format (ex: body.signature) because a malicious client could
just drop the signature portion.

As always, we can't trust the client. :-(

This means that unsigning can *never* be fully transparent.  We need a
symmetric specification of the fact that a given cookie should,
indeed, be signed.

> But that's pretty ugly. Not sure what to do about this one -
> request.unsign_cookie('key') might be an option, as at least that
> reflects the set_cookie / sign / unsign API a bit. Not ideal by a long
> shot though.

I'm not sure what the best solution is, but here are some of the
options I've considered:

1) request.unsign_cookie('foo') -- This breaks the parallelism with
existing cookies.  Sometimes we'll be doing request.COOKIES['foo'] and
sometimes we'll be doing request.unsign_cookie('foo').

2) A decorator for views -- @unsign_cookies("foo", "bar") -- This
doesn't allow any sort of fall-back (you can't customize what to do if
a given cookie is improperly signed)

3) COOKIES as an intelligent object -- We can overload .get so we're
doing something like request.COOKIES.get('foo', signed=True) -- I
think this has the best chance at an interface that keeps a consistent
feel. It's completely backward compatible, though it breaks the
traditional expectation of what you can do via the `get` method on a
dictionary.


Best,
 - Ben

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to django-developers@...
To unsubscribe from this group, send email to django-developers+unsubscribe@...
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en
-~----------~----~----~----~------~----~------~--~---


Re: Adding signing (and signed cookies) to Django core

by Marty Alchin :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


> Also, does the name of a cookie factor into the cookie length limits?
> My reading of RFC 2109 says yes, but it'd be worth verifying, since it
> would cut down on the usable value space. With your compressed base64
> stuff, that's not as big of a problem, but still something to look
> into.

Also, just to throw this out there for the sake of compleness: could
the signature be stored under a separate name, rather than being
bundled with the original cookie itself?

Set-Cookie: key=value
Set-Cookie: key__Xsignature=signature_string

It seems like this could address a couple issues at once.

* There's a clear distinction between signed and unsigned cookies, so
request.COOKIES can be populated with only valid cookies

* The key/value pair remains unchanged, so things like Google
Analytics can happily ignore the signature if it was applied
unnecessarily (middleware is back on the table!)

Since there may be an upper limit on the number of allowed cookies,
though, doubling the number of cookies could present some very real
problems. RFC 2109 recommends allowing at least 20 cookies per domain
name, and it looks like at least Microsoft takes that to be a
maximum,[1] so it could present very real problems (middleware is back
off the table!).

I'm not sure how many cookies people use on a regular basis, and this
would only be for explicitly signed cookies, so maybe it'd be okay,
but it's flirting dangerously close to being completely unworkable.
Worse yet, it doesn't look like there's any predictable way to know
which cookies would get lost in the event of having too many, so this
may end up causing some very weird application errors if things go
wrong.

At least now it's been recorded for future reference. (Hello, future
me, looking up information on why we did things the way we did! Do we
have flying cars yet?)

-Gul

[1] http://support.microsoft.com/kb/306070

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to django-developers@...
To unsubscribe from this group, send email to django-developers+unsubscribe@...
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en
-~----------~----~----~----~------~----~------~--~---


Re: Adding signing (and signed cookies) to Django core

by Simon Willison-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


On Sep 24, 8:22 pm, Benjamin Slavin <benjamin.sla...@...> wrote:
> Unfortunately, this approach won't work.
>
> A malicious client can just send "key" rather than "key__Xsigned" and
> you'll never know that the cookie hasn't gone through validation.
> This also means that you can't look for cookie values that match a
> specific format (ex: body.signature) because a malicious client could
> just drop the signature portion.
>
> As always, we can't trust the client. :-(

Good point - that rules that approach out.

> 1) request.unsign_cookie('foo') -- This breaks the parallelism with
> existing cookies.  Sometimes we'll be doing request.COOKIES['foo'] and
> sometimes we'll be doing request.unsign_cookie('foo').

If we were going to do that, it would make sense to NOT have set_cookie
(... sign=True) as the API for setting one. We could achieve
parallelism with something like this:

response.sign_cookie('key', 'value')
...
value = request.unsign_cookie('key')

You can still read request.COOKIES directly, but you'll get the raw,
signed value. That API doesn't look too ugly to me.

> 2) A decorator for views -- @unsign_cookies("foo", "bar") -- This
> doesn't allow any sort of fall-back (you can't customize what to do if
> a given cookie is improperly signed)

If a cookie is improperly signed I think you silently discard it, as
if it was never set. If we had logging this could always be logged as
well... we could fire a signal if we really think people might want to
further customise it.

> 3) COOKIES as an intelligent object -- We can overload .get so we're
> doing something like request.COOKIES.get('foo', signed=True) -- I
> think this has the best chance at an interface that keeps a consistent
> feel. It's completely backward compatible, though it breaks the
> traditional expectation of what you can do via the `get` method on a
> dictionary.

This isn't so bad, since we already have a precedent for this in
request.POST.get_list('foo'). request.COOKIES.get_signed(key) might be
OK.

At the moment I think my preference is for response.sign_cookie and
request.unsign_cookie, though I'm a bit worried that "unsign cookie"
doesn't obviously mean "get the cookie, check if the signature is OK
and return the value if it is". I like that unsign_cookie maps to the
low level signed.unsign API, but it might well be that most users
never use the low level signing API and the cookie stuff is the only
bit of it they ever see.

Cheers,

Simon

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to django-developers@...
To unsubscribe from this group, send email to django-developers+unsubscribe@...
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en
-~----------~----~----~----~------~----~------~--~---


Re: Adding signing (and signed cookies) to Django core

by Marty Alchin :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


On Thu, Sep 24, 2009 at 3:22 PM, Benjamin Slavin
<benjamin.slavin@...> wrote:

>> response.set_cookie('key', 'value', sign=True)
>> - results in a Set-Cookie: key__Xsigned=value header
>
> Unfortunately, this approach won't work.
>
> A malicious client can just send "key" rather than "key__Xsigned" and
> you'll never know that the cookie hasn't gone through validation.
> This also means that you can't look for cookie values that match a
> specific format (ex: body.signature) because a malicious client could
> just drop the signature portion.
>
> As always, we can't trust the client. :-(

And you've just added another reason my followup email is invalid,
though I sent that before reading your response. (Take that, future
me!)

> 3) COOKIES as an intelligent object -- We can overload .get so we're
> doing something like request.COOKIES.get('foo', signed=True) -- I
> think this has the best chance at an interface that keeps a consistent
> feel. It's completely backward compatible, though it breaks the
> traditional expectation of what you can do via the `get` method on a
> dictionary.

I was wondering about this option as well, after I mentioned adding a
request.COOKIES.get_unsigned() method. I actually like this idea a
lot, personally. As you mention, it's backward compatible, and I'm not
sure it completely breaks the traditional expectation. After all,
aren't subclasses expected to customize the behavior of their parents?
It doesn't change any existing behavior, but rather just adds
something extra.

The one downside to using get() directly, as opposed to an altogether
new method, is that get() doesn't raise a KeyError when a value
doesn't exist. That means if anyone's wrapping request.COOKIES[key] in
a try block and catching KeyError, changing to the new code is more
than just a one-liner. I'm personally okay with this, but it's
definitely worth noting.

-Gul

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to django-developers@...
To unsubscribe from this group, send email to django-developers+unsubscribe@...
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en
-~----------~----~----~----~------~----~------~--~---


Re: Adding signing (and signed cookies) to Django core

by Johannes Dollinger :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


How about Signer class?

        signer = Signer(key=...)
        assert signer.unsign(signer.sign(value)) == value

This way you wouldn't have to pass around key, extra_key, and  
potential further arguments but a single Signer instance. Plus, you  
could easyly overwrite hashing, concatenation, and serialization as  
well as add functionality transparently, eg.:

        sig = TimestampedSignatureFactory(ttl=3600) # sig.unsign() will raise  
SigntureExpired after 3600 seconds

> 1) request.unsign_cookie('foo') -- This breaks the parallelism with
> existing cookies.  Sometimes we'll be doing request.COOKIES['foo'] and
> sometimes we'll be doing request.unsign_cookie('foo').
>
> 2) A decorator for views -- @unsign_cookies("foo", "bar") -- This
> doesn't allow any sort of fall-back (you can't customize what to do if
> a given cookie is improperly signed)
>
> 3) COOKIES as an intelligent object -- We can overload .get so we're
> doing something like request.COOKIES.get('foo', signed=True) -- I
> think this has the best chance at an interface that keeps a consistent
> feel. It's completely backward compatible, though it breaks the
> traditional expectation of what you can do via the `get` method on a
> dictionary.

4) A signed.Cookies object:

        signed_cookies = signed.Cookies(request, key=...)
        signed_cookies.set_cookie(key, value, **kwargs)
        value = signed_cookies[key]

or

        signed_cookies = signer.get_cookies(request)
        ...

__
Johannes





--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to django-developers@...
To unsubscribe from this group, send email to django-developers+unsubscribe@...
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en
-~----------~----~----~----~------~----~------~--~---


Re: Adding signing (and signed cookies) to Django core

by SmileyChris :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


+1 on the concept of a signing module.

On Sep 25, 7:48 am, Marty Alchin <gulop...@...> wrote:

> The one downside to using get() directly, as opposed to an altogether
> new method, is that get() doesn't raise a KeyError when a value
> doesn't exist. That means if anyone's wrapping request.COOKIES[key] in
> a try block and catching KeyError, changing to the new code is more
> than just a one-liner.

Adding my coat of paint to the shed...

Rather than a "request.unsign_cookie" method, provide a
"request.SIGNED_COOKIES" property (or perhaps alteratively,
request.COOKIES.signed if the interface was useful enough to use
across request.GET/POST too) containing a lazy dict-like object which
only retrieves (correctly) signed cookies.
This way you are using a similar interface, but it's obvious in code
that you're only interested in signed ones.

For example:

# raises KeyError
value = request.SIGNED_COOKIES['bad-cookie']

# value == None
value = request.SIGNED_COOKIES.get('bad-cookie')

Personally, I don't see much point in specifically reporting on
incorrectly signed cookies - imo they should just be treated as if
they never existed. If someone really cared, they can look in
request.COOKIES to see if the cookie was in there but not in
SIGNED_COOKIES.
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to django-developers@...
To unsubscribe from this group, send email to django-developers+unsubscribe@...
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en
-~----------~----~----~----~------~----~------~--~---


Re: Adding signing (and signed cookies) to Django core

by Ian Lewis-3 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


On Fri, Sep 25, 2009 at 6:33 AM, Chris Beaven <smileychris@...> wrote:

>
> +1 on the concept of a signing module.
>
> On Sep 25, 7:48 am, Marty Alchin <gulop...@...> wrote:
>
>> The one downside to using get() directly, as opposed to an altogether
>> new method, is that get() doesn't raise a KeyError when a value
>> doesn't exist. That means if anyone's wrapping request.COOKIES[key] in
>> a try block and catching KeyError, changing to the new code is more
>> than just a one-liner.
>
> Adding my coat of paint to the shed...
>
> Rather than a "request.unsign_cookie" method, provide a
> "request.SIGNED_COOKIES" property (or perhaps alteratively,
> request.COOKIES.signed if the interface was useful enough to use
> across request.GET/POST too) containing a lazy dict-like object which
> only retrieves (correctly) signed cookies.
> This way you are using a similar interface, but it's obvious in code
> that you're only interested in signed ones.
>
> For example:
>
> # raises KeyError
> value = request.SIGNED_COOKIES['bad-cookie']
>
> # value == None
> value = request.SIGNED_COOKIES.get('bad-cookie')
>
> Personally, I don't see much point in specifically reporting on
> incorrectly signed cookies - imo they should just be treated as if
> they never existed. If someone really cared, they can look in
> request.COOKIES to see if the cookie was in there but not in
> SIGNED_COOKIES.

The problem is that you don't know which cookies are signed and which
aren't for the reasons posted earlier. So you don't know which cookies
to put in SIGNED_COOKIES and which to put in COOKIES unless accessing
COOKIES gives you raw values of ALL cookies and SIGNED_COOKIES
attempts to unsign ALL cookies. That seems really clunky.

You have to sign and unsign the cookies yourself in code which makes
the sign_cookie/unsign_cookie API make sense.

Ian

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to django-developers@...
To unsubscribe from this group, send email to django-developers+unsubscribe@...
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en
-~----------~----~----~----~------~----~------~--~---


Re: Adding signing (and signed cookies) to Django core

by Ian Lewis-3 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


On Fri, Sep 25, 2009 at 4:46 AM, Simon Willison <simon@...> wrote:
> This isn't so bad, since we already have a precedent for this in
> request.POST.get_list('foo'). request.COOKIES.get_signed(key) might be
> OK.

request.COOKIES.get_signed(key) makes the most sense to me since it's
clear you are dealing with cookies and as you said it looks something
like the request.POST object. Though it's a bit more verbose than
request.unsign_cookie(key), accessing cookies directly through the
request object looks wierd and unsign_cookie could be ambiguous.

Ian

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to django-developers@...
To unsubscribe from this group, send email to django-developers+unsubscribe@...
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en
-~----------~----~----~----~------~----~------~--~---


Re: Adding signing (and signed cookies) to Django core

by SmileyChris :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


On Sep 25, 1:56 pm, Ian Lewis <ianmle...@...> wrote:
> [...] unless accessing
> COOKIES gives you raw values of ALL cookies and SIGNED_COOKIES
> attempts to unsign ALL cookies. That seems really clunky.

Yes, all cookies would stay in COOKIES.
SIGNED_COOKIES would be a lazy dict-like object, not a plain
dictionary. It'd only unsign all cookies if you iterated its keys or
values.

But I just realised it doesn't cover the case for using a different
signing key (or extra signing data though).

I think I now prefer Simon's request.COOKIES.get_signed(key) method
(which could handle different/extra signing data).
I really don't like the term "unsigning" (and therefore don't like the
method name unsign_cookie) - it doesn't explain what is happening
clearly.
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to django-developers@...
To unsubscribe from this group, send email to django-developers+unsubscribe@...
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en
-~----------~----~----~----~------~----~------~--~---


Re: Adding signing (and signed cookies) to Django core

by Russell Keith-Magee-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


On Fri, Sep 25, 2009 at 1:18 AM, Simon Willison <simon@...> wrote:
>
> As mentioned in the thread about cookie-based notifications, at the
> DjangoCon Sprints I raised the subject of adding signing (and signed
> cookies) to Django core.
>
> I've found myself using signing more and more over time, and I think
> it's a concept which is common enough to deserve inclusion in Django -
> if anything, its use should be actively encouraged by the framework.

Put me down as +1 in favor of adding support for signed cookies in some form.

As for the exact form that the API will take - I don't have any
particularly strong opinions at this point, and there are plenty of
big brains weighing in, so I will stay out of the discussion and let
the community evolve the idea.

By way of greasing the wheels towards trunk: if the outcome of this
mailing list thread was a wiki page that digested all the ideas,
concerns and issues into a single page, it will make the final
approval process much easier. Luke Plant's wiki page on the proposed
CSRF changes [1] is a good model to follow here - I wasn't involved in
the early stages of that discussion, but thanks to that wiki page, I
was able to come up to speed very quickly and see why certain ideas
were rejected.

[1] http://code.djangoproject.com/wiki/CsrfProtection

Yours,
Russ Magee %-)

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to django-developers@...
To unsubscribe from this group, send email to django-developers+unsubscribe@...
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en
-~----------~----~----~----~------~----~------~--~---


Re: Adding signing (and signed cookies) to Django core

by Eric Florenzano :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


A big +1 on signed cookies, and I like the direction the discussion is
going.

Also, I hope this doesn't derail this discussion, but I hope after
signed cookies are added, auth can be made to optionally use signed
cookies instead of sessions.

Thanks,
Eric Florenzano
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to django-developers@...
To unsubscribe from this group, send email to django-developers+unsubscribe@...
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en
-~----------~----~----~----~------~----~------~--~---


Re: Adding signing (and signed cookies) to Django core

by Simon Willison-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


On Sep 25, 4:48 am, Russell Keith-Magee <freakboy3...@...>
wrote:
> By way of greasing the wheels towards trunk: if the outcome of this
> mailing list thread was a wiki page that digested all the ideas,
> concerns and issues into a single page, it will make the final
> approval process much easier.

Great idea: http://code.djangoproject.com/wiki/Signing

I've attempted to summarise the discussion so far. I've also added a
couple of notes about potential improvements to the proposed
django.utils.signed API, in particular a note that having low-level
support for signatures that have a timestamp baked in and hence can
expire might be a good idea.

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to django-developers@...
To unsubscribe from this group, send email to django-developers+unsubscribe@...
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en
-~----------~----~----~----~------~----~------~--~---


Re: Adding signing (and signed cookies) to Django core

by Luke Plant-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


On Thursday 24 September 2009 18:18:56 Simon Willison wrote:

> SECRET_KEY considerations
> =========================

Can I add some other things I've been worrying about while we're on
the topic?

In other web apps (I think Wordpress?), there have been problems
associated with use of secret keys when the same key is used for
different purposes throughout the application.

Suppose one part of an app signs an e-mail address for the purpose of
an account confirmation link sent in an e-mail.  The user won't be
able to forge the link unless they know HMAC(SECRET_KEY, email).

However, suppose another part of the website allows a user to set
their e-mail address (merely for convenience), and stores it in a
signed cookie.  That means an attacker can now easily get hold of
HMAC(SECRET_KEY, email), and forge the link.

There are many places in Django that use SECRET_KEY.  I'm not
currently aware of any vulnerability, because in most cases the
attacker has only *limited* control over manipulating the message that
is being signed.  But I may have missed some, and without some
systematic method, it would be easy for one place to open up
vulnerabilities for all the others.

So I propose:

 - we review all the Django code involving md5/sha1
 - we switch to HMAC where appropriate
 - we add unique prefixes to the SECRET_KEY for every different
   place it is used.  So for the e-mail confirmation link, we use
   HMAC("email-confirmation" + SECRET_KEY, message)
 - also add the ability to do SECRET_KEY rotation, as Simon
   suggested.  This suggests we want a utility wrapper around hmac
   that looks like hmac(unique_key_prefix, key, message) and handles
   all the above details for us.

The main difficulty is the way this could break compatibility with
existing signed messages, especially persistent ones like those stored
in password files etc.

Luke

--
"Smoking cures weight problems...eventually..." (Steven Wright)

Luke Plant || http://lukeplant.me.uk/

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to django-developers@...
To unsubscribe from this group, send email to django-developers+unsubscribe@...
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en
-~----------~----~----~----~------~----~------~--~---


Re: Adding signing (and signed cookies) to Django core

by Dan Watson :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


On Sep 24, 3:46 pm, Simon Willison <si...@...> wrote:

> > 1) request.unsign_cookie('foo') -- This breaks the parallelism with
> > existing cookies.  Sometimes we'll be doing request.COOKIES['foo'] and
> > sometimes we'll be doing request.unsign_cookie('foo').
>
> If we were going to do that, it would make sense to NOT have set_cookie
> (... sign=True) as the API for setting one. We could achieve
> parallelism with something like this:
>
> response.sign_cookie('key', 'value')
> ...
> value = request.unsign_cookie('key')
>
> You can still read request.COOKIES directly, but you'll get the raw,
> signed value. That API doesn't look too ugly to me.

What about having something like:

request.get_cookie(key, signed=True)

that would fall back to returning request.COOKIES[key] when
signed=False? It creates a second way to access request cookies, but
introduces parallelism with the response.set_cookie method.

Dan
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to django-developers@...
To unsubscribe from this group, send email to django-developers+unsubscribe@...
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en
-~----------~----~----~----~------~----~------~--~---


Re: Adding signing (and signed cookies) to Django core

by Simon Willison-2 :: Rate this Message:

Reply to Author | View Threaded | Show Only this Message


On Sep 25, 12:02 pm, Luke Plant <L.Plant...@...> wrote:

> In other web apps (I think Wordpress?), there have been problems
> associated with use of secret keys when the same key is used for
> different purposes throughout the application.
> ...
>  - we add unique prefixes to the SECRET_KEY for every different
>    place it is used.  So for the e-mail confirmation link, we use
>    HMAC("email-confirmation" + SECRET_KEY, message)
>  - also add the ability to do SECRET_KEY rotation, as Simon
>    suggested.  This suggests we want a utility wrapper around hmac
>    that looks like hmac(unique_key_prefix, key, message) and handles
>    all the above details for us.

I share your concern. In the signed.py module I address your first
point by allowing an additional "extra_key" parameter:

>>> signed.dumps('hello', extra_key='ultra')
'UydoZWxsbycKcDAKLg.1XYDpILo5xqSwImfa3WuJJT4RPo'
>>> signed.loads(_, extra_key='ultra')
'hello'

However, this is available on dumps and loads but not on sign and
unsign - we should definitely implement it at those points instead.

I'm going to ask on some web security mailing list about best practice
for rotating keys. Do you have any further information on the
WordPress problems?

Cheers,

Simon
--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups "Django developers" group.
To post to this group, send email to django-developers@...
To unsubscribe from this group, send email to django-developers+unsubscribe@...
For more options, visit this group at http://groups.google.com/group/django-developers?hl=en
-~----------~----~----~----~------~----~------~--~---

< Prev | 1 - 2 | Next >