summaryrefslogtreecommitdiff
path: root/docs
diff options
context:
space:
mode:
authorJannis Leidel <jannis@leidel.info>2011-05-21 14:41:14 +0000
committerJannis Leidel <jannis@leidel.info>2011-05-21 14:41:14 +0000
commitf60d42846365b2bf2f1c9bc7a3007c303122a20b (patch)
tree1fd63d707db2e1ab86dabe762041e1b5cf6a4587 /docs
parent15793309e16dcdf5de17594eaef1962a7c35ce31 (diff)
Fixed #12417 -- Added signing functionality, including signing cookies. Many thanks to Simon, Stephan, Paul and everyone else involved.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@16253 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'docs')
-rw-r--r--docs/index.txt1
-rw-r--r--docs/ref/request-response.txt48
-rw-r--r--docs/ref/settings.txt13
-rw-r--r--docs/releases/1.4.txt9
-rw-r--r--docs/topics/index.txt1
-rw-r--r--docs/topics/signing.txt135
6 files changed, 207 insertions, 0 deletions
diff --git a/docs/index.txt b/docs/index.txt
index 9135d32019..8b4ae53bc2 100644
--- a/docs/index.txt
+++ b/docs/index.txt
@@ -171,6 +171,7 @@ Other batteries included
* :doc:`Comments <ref/contrib/comments/index>` | :doc:`Moderation <ref/contrib/comments/moderation>` | :doc:`Custom comments <ref/contrib/comments/custom>`
* :doc:`Content types <ref/contrib/contenttypes>`
* :doc:`Cross Site Request Forgery protection <ref/contrib/csrf>`
+ * :doc:`Cryptographic signing <topics/signing>`
* :doc:`Databrowse <ref/contrib/databrowse>`
* :doc:`E-mail (sending) <topics/email>`
* :doc:`Flatpages <ref/contrib/flatpages>`
diff --git a/docs/ref/request-response.txt b/docs/ref/request-response.txt
index 6281120d15..d4ff40a746 100644
--- a/docs/ref/request-response.txt
+++ b/docs/ref/request-response.txt
@@ -240,6 +240,43 @@ Methods
Example: ``"http://example.com/music/bands/the_beatles/?print=true"``
+.. method:: HttpRequest.get_signed_cookie(key, default=RAISE_ERROR, salt='', max_age=None)
+
+ .. versionadded:: 1.4
+
+ Returns a cookie value for a signed cookie, or raises a
+ :class:`~django.core.signing.BadSignature` exception if the signature is
+ no longer valid. If you provide the ``default`` argument the exception
+ will be suppressed and that default value will be returned instead.
+
+ The optional ``salt`` argument can be used to provide extra protection
+ against brute force attacks on your secret key. If supplied, the
+ ``max_age`` argument will be checked against the signed timestamp
+ attached to the cookie value to ensure the cookie is not older than
+ ``max_age`` seconds.
+
+ For example::
+
+ >>> request.get_signed_cookie('name')
+ 'Tony'
+ >>> request.get_signed_cookie('name', salt='name-salt')
+ 'Tony' # assuming cookie was set using the same salt
+ >>> request.get_signed_cookie('non-existing-cookie')
+ ...
+ KeyError: 'non-existing-cookie'
+ >>> request.get_signed_cookie('non-existing-cookie', False)
+ False
+ >>> request.get_signed_cookie('cookie-that-was-tampered-with')
+ ...
+ BadSignature: ...
+ >>> request.get_signed_cookie('name', max_age=60)
+ ...
+ SignatureExpired: Signature age 1677.3839159 > 60 seconds
+ >>> request.get_signed_cookie('name', False, max_age=60)
+ False
+
+ See :doc:`cryptographic signing </topics/signing>` for more information.
+
.. method:: HttpRequest.is_secure()
Returns ``True`` if the request is secure; that is, if it was made with
@@ -618,6 +655,17 @@ Methods
.. _`cookie Morsel`: http://docs.python.org/library/cookie.html#Cookie.Morsel
.. _HTTPOnly: http://www.owasp.org/index.php/HTTPOnly
+.. method:: HttpResponse.set_signed_cookie(key, value='', salt='', max_age=None, expires=None, path='/', domain=None, secure=None, httponly=False)
+
+ .. versionadded:: 1.4
+
+ Like :meth:`~HttpResponse.set_cookie()`, but
+ :doc:`cryptographic signing </topics/signing>` the cookie before setting
+ it. Use in conjunction with :meth:`HttpRequest.get_signed_cookie`.
+ You can use the optional ``salt`` argument for added key strength, but
+ you will need to remember to pass it to the corresponding
+ :meth:`HttpRequest.get_signed_cookie` call.
+
.. method:: HttpResponse.delete_cookie(key, path='/', domain=None)
Deletes the cookie with the given key. Fails silently if the key doesn't
diff --git a/docs/ref/settings.txt b/docs/ref/settings.txt
index f5f1226f21..4716fabea7 100644
--- a/docs/ref/settings.txt
+++ b/docs/ref/settings.txt
@@ -1647,6 +1647,19 @@ See :tfilter:`allowed date format strings <date>`.
See also ``DATE_FORMAT`` and ``SHORT_DATETIME_FORMAT``.
+.. setting:: SIGNING_BACKEND
+
+SIGNING_BACKEND
+---------------
+
+.. versionadded:: 1.4
+
+Default: 'django.core.signing.TimestampSigner'
+
+The backend used for signing cookies and other data.
+
+See also the :doc:`/topics/signing` documentation.
+
.. setting:: SITE_ID
SITE_ID
diff --git a/docs/releases/1.4.txt b/docs/releases/1.4.txt
index 496a4c93d2..a579e96f93 100644
--- a/docs/releases/1.4.txt
+++ b/docs/releases/1.4.txt
@@ -46,6 +46,15 @@ not custom filters. This has been rectified with a simple API previously
known as "FilterSpec" which was used internally. For more details, see the
documentation for :attr:`~django.contrib.admin.ModelAdmin.list_filter`.
+Tools for cryptographic signing
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Django 1.4 adds both a low-level API for signing values and a high-level API
+for setting and reading signed cookies, one of the most common uses of
+signing in Web applications.
+
+See :doc:`cryptographic signing </topics/signing>` docs for more information.
+
``reverse_lazy``
~~~~~~~~~~~~~~~~
diff --git a/docs/topics/index.txt b/docs/topics/index.txt
index 49a03befb1..84f9e9f688 100644
--- a/docs/topics/index.txt
+++ b/docs/topics/index.txt
@@ -18,6 +18,7 @@ Introductions to all the key parts of Django you'll need to know:
auth
cache
conditional-view-processing
+ signing
email
i18n/index
logging
diff --git a/docs/topics/signing.txt b/docs/topics/signing.txt
new file mode 100644
index 0000000000..7989643297
--- /dev/null
+++ b/docs/topics/signing.txt
@@ -0,0 +1,135 @@
+=====================
+Cryptographic signing
+=====================
+
+.. module:: django.core.signing
+ :synopsis: Django's signing framework.
+
+.. versionadded:: 1.4
+
+The golden rule of Web application security is to never trust data from
+untrusted sources. Sometimes it can be useful to pass data through an
+untrusted medium. Cryptographically signed values can be passed through an
+untrusted channel safe in the knowledge that any tampering will be detected.
+
+Django provides both a low-level API for signing values and a high-level API
+for setting and reading signed cookies, one of the most common uses of
+signing in Web applications.
+
+You may also find signing useful for the following:
+
+ * Generating "recover my account" URLs for sending to users who have
+ lost their password.
+
+ * Ensuring data stored in hidden form fields has not been tampered with.
+
+ * Generating one-time secret URLs for allowing temporary access to a
+ protected resource, for example a downloadable file that a user has
+ paid for.
+
+Protecting the SECRET_KEY
+=========================
+
+When you create a new Django project using :djadmin:`startproject`, the
+``settings.py`` file it generates automatically gets a random
+:setting:`SECRET_KEY` value. This value is the key to securing signed
+data -- it is vital you keep this secure, or attackers could use it to
+generate their own signed values.
+
+Using the low-level API
+=======================
+
+.. class:: Signer
+
+Django's signing methods live in the ``django.core.signing`` module.
+To sign a value, first instantiate a ``Signer`` instance::
+
+ >>> from django.core.signing import Signer
+ >>> signer = Signer()
+ >>> value = signer.sign('My string')
+ >>> value
+ 'My string:GdMGD6HNQ_qdgxYP8yBZAdAIV1w'
+
+The signature is appended to the end of the string, following the colon.
+You can retrieve the original value using the ``unsign`` method::
+
+ >>> original = signer.unsign(value)
+ >>> original
+ u'My string'
+
+If the signature or value have been altered in any way, a
+``django.core.signing.BadSigature`` exception will be raised::
+
+ >>> value += 'm'
+ >>> try:
+ ... original = signer.unsign(value)
+ ... except signing.BadSignature:
+ ... print "Tampering detected!"
+
+By default, the ``Signer`` class uses the :setting:`SECRET_KEY` setting to
+generate signatures. You can use a different secret by passing it to the
+``Signer`` constructor::
+
+ >>> signer = Signer('my-other-secret')
+ >>> value = signer.sign('My string')
+ >>> value
+ 'My string:EkfQJafvGyiofrdGnuthdxImIJw'
+
+Using the salt argument
+-----------------------
+
+If you do not wish to use the same key for every signing operation in your
+application, you can use the optional ``salt`` argument to the ``Signer``
+class to further strengthen your :setting:`SECRET_KEY` against brute force
+attacks. Using a salt will cause a new key to be derived from both the salt
+and your :setting:`SECRET_KEY`::
+
+ >>> signer = Signer()
+ >>> signer.sign('My string')
+ 'My string:GdMGD6HNQ_qdgxYP8yBZAdAIV1w'
+ >>> signer = Signer(salt='extra')
+ >>> signer.sign('My string')
+ 'My string:Ee7vGi-ING6n02gkcJ-QLHg6vFw'
+ >>> signer.unsign('My string:Ee7vGi-ING6n02gkcJ-QLHg6vFw')
+ u'My string'
+
+Unlike your :setting:`SECRET_KEY`, your salt argument does not need to stay
+secret.
+
+Verifying timestamped values
+----------------------------
+
+.. class:: TimestampSigner
+
+``TimestampSigner`` is a subclass of :class:`~Signer` that appends a signed
+timestamp to the value. This allows you to confirm that a signed value was
+created within a specified period of time::
+
+ >>> from django.core.signing import TimestampSigner
+ >>> signer = TimestampSigner()
+ >>> value = signer.sign('hello')
+ >>> value
+ 'hello:1NMg5H:oPVuCqlJWmChm1rA2lyTUtelC-c'
+ >>> signer.unsign(value)
+ u'hello'
+ >>> signer.unsign(value, max_age=10)
+ ...
+ SignatureExpired: Signature age 15.5289158821 > 10 seconds
+ >>> signer.unsign(value, max_age=20)
+ u'hello'
+
+Protecting complex data structures
+----------------------------------
+
+If you wish to protect a list, tuple or dictionary you can do so using the
+signing module's dumps and loads functions. These imitate Python's pickle
+module, but uses JSON serialization under the hood. JSON ensures that even
+if your :setting:`SECRET_KEY` is stolen an attacker will not be able to
+execute arbitrary commands by exploiting the pickle format.::
+
+ >>> from django.core import signing
+ >>> value = signing.dumps({"foo": "bar"})
+ >>> value
+ 'eyJmb28iOiJiYXIifQ:1NMg1b:zGcDE4-TCkaeGzLeW9UQwZesciI'
+ >>> signing.loads(value)
+ {'foo': 'bar'}