Adding signing (and signed cookies) to Django core
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
Re: Adding signing (and signed cookies) to Django core
+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 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' > >>> sign
Re: Adding signing (and signed cookies) to Django core
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 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 develop
Re: Adding signing (and signed cookies) to Django core
On Sep 24, 7:37 pm, Marty Alchin 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@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 -~--~~~~--~~--~--~---
Re: Adding signing (and signed cookies) to Django core
On Thu, Sep 24, 2009 at 2:54 PM, Simon Willison 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@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 -~--~~~~--~~--~--~---
Re: Adding signing (and signed cookies) to Django core
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 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@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 -~--~~~~--~~--~--~---
Re: Adding signing (and signed cookies) to Django core
> 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@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 -~--~~~~--~~--~--~---
Re: Adding signing (and signed cookies) to Django core
On Sep 24, 8:22 pm, Benjamin Slavin 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@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 -~--~~~~--~~--~--~---
Re: Adding signing (and signed cookies) to Django core
On Thu, Sep 24, 2009 at 3:22 PM, 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@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 -~--~~~~--~~--~--~---
Re: Adding signing (and signed cookies) to Django core
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@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 -~--~~~~--~~--~--~---
Re: Adding signing (and signed cookies) to Django core
+1 on the concept of a signing module. On Sep 25, 7:48 am, Marty Alchin 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@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 -~--~~~~--~~--~--~---
Re: Adding signing (and signed cookies) to Django core
On Fri, Sep 25, 2009 at 4:46 AM, Simon Willison 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@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 -~--~~~~--~~--~--~---
Re: Adding signing (and signed cookies) to Django core
On Fri, Sep 25, 2009 at 6:33 AM, Chris Beaven wrote: > > +1 on the concept of a signing module. > > On Sep 25, 7:48 am, Marty Alchin 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@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 -~--~~~~--~~--~--~---
Re: Adding signing (and signed cookies) to Django core
On Sep 25, 1:56 pm, Ian Lewis 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@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 -~--~~~~--~~--~--~---
How to customize field to get the correct python value?
Hi there, I have a question on customize the db field. For this field, I want to save integers in db, but want them to be my custom class when they are used in Python code. In Django docs, it's said that I should set the __metaclass__ to be models.SubfieldBase to make to_python() work. I tried it and succeeded when all data of one db row are fetched. But I then found that if I use query method like values(), values_list () or only(), the data of my custom field will not be converted into my desired type but the raw db integer. Then I tried Django's own DateField, it works OK also in such scenario. So I think there must be something I missed. Could you help on this question? I cannot find more materials to move on, so I'm very eager to get your help. Many thanks! -- http://j-lite.net --~--~-~--~~~---~--~~ 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@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 -~--~~~~--~~--~--~---
Re: How to customize field to get the correct python value?
On Fri, Sep 25, 2009 at 10:40 AM, Jay wrote: > > Hi there, > > I have a question on customize the db field. For this field, I want to > save integers in db, but want them to be my custom class when they are > used in Python code. In Django docs, it's said that I should set the > __metaclass__ to be models.SubfieldBase to make to_python() work. I > tried it and succeeded when all data of one db row are fetched. > > But I then found that if I use query method like values(), values_list > () or only(), the data of my custom field will not be converted into > my desired type but the raw db integer. Then I tried Django's own > DateField, it works OK also in such scenario. So I think there must be > something I missed. > > Could you help on this question? I cannot find more materials to move > on, so I'm very eager to get your help. Many thanks! Two points: 1) Please don't cross post messages to multiple mailing lists. Find the right forum, and ask once. 2) Django-developers is not the right place. This mailing list is for discussing the development of Django itself, not answering general user queries. Django-users is the list you want. 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@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 -~--~~~~--~~--~--~---
Re: Adding signing (and signed cookies) to Django core
On Fri, Sep 25, 2009 at 1:18 AM, Simon Willison 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@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 -~--~~~~--~~--~--~---
Re: Adding signing (and signed cookies) to Django core
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@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 -~--~~~~--~~--~--~---