diff --git a/django/contrib/sessions/backends/cookies.py b/django/contrib/sessions/backends/cookies.py
new file mode 100644
index 0000000..bff221b
--- /dev/null
+++ b/django/contrib/sessions/backends/cookies.py
@@ -0,0 +1,90 @@
+import zlib
+try:
+    import cPickle as pickle
+except ImportError:
+    import pickle
+
+from django.conf import settings
+from django.core import signing
+from django.utils.encoding import smart_str
+
+from django.contrib.sessions.backends.base import SessionBase
+
+
+class SessionStore(SessionBase):
+    salt = 'django.contrib.sessions.backends.cookies'
+
+    def load(self):
+        """
+        We load the data from the key itself instead of fetching from
+        some external data store. Opposite of _get_session_key(),
+        raises BadSignature if signature fails.
+        """
+        signer = signing.TimestampSigner(salt=self.salt)
+        try:
+            base64d = signer.unsign(
+                self._session_key, max_age=settings.SESSION_COOKIE_AGE)
+            pickled = signing.b64_decode(smart_str(base64d))
+            return pickle.loads(zlib.decompress(pickled))
+        except (signing.BadSignature, ValueError):
+            self.create()
+            return {}
+
+    def create(self):
+        """
+        To create a new key, we simply make sure that the modified flag is set
+        so that the cookie is set on the client for the current request.
+        """
+        self.modified = True
+
+    def save(self):
+        """
+        To save, we get the session key as a securely signed string and then
+        set the modified flag so that the cookie is set on the client for the
+        current request.
+        """
+        self._session_key = self._get_session_key()
+        self.modified = True
+
+    def exists(self, session_key=None):
+        """
+        This method makes sense when you're talking to a shared resource, but
+        it doesn't matter when you're storing the information in the client's
+        cookie.
+        """
+        return False
+
+    def delete(self, session_key=None):
+        """
+        To delete, we clear the session key and the underlying data structure
+        and set the modified flag so that the cookie is set on the client for
+        the current request.
+        """
+        self._session_key = ''
+        self._session_cache = {}
+        self.modified = True
+
+    def cycle_key(self):
+        """
+        Keeps the same data but with a new key.  To do this, we just have to
+        call ``save()`` and it will automatically save a cookie with a new key
+        at the end of the request.
+        """
+        self.save()
+
+    def _get_session_key(self):
+        """
+        Most session backends don't need to override this method, but we do,
+        because instead of generating a random string, we want to actually
+        generate a secure url-safe Base64-encoded string of data as our
+        session key.
+        """
+        payload = getattr(self, '_session_cache', {})
+        pickled = pickle.dumps(payload, pickle.HIGHEST_PROTOCOL)
+        base64d = signing.b64_encode(zlib.compress(pickled))
+        return signing.TimestampSigner(salt=self.salt).sign(base64d)
+
+    def _set_session_key(self, session_key):
+        self._session_key = session_key
+
+    session_key = property(_get_session_key, _set_session_key)
diff --git a/django/contrib/sessions/tests.py b/django/contrib/sessions/tests.py
index 2eb43f3..0c34101 100644
--- a/django/contrib/sessions/tests.py
+++ b/django/contrib/sessions/tests.py
@@ -7,11 +7,13 @@ from django.contrib.sessions.backends.db import SessionStore as DatabaseSession
 from django.contrib.sessions.backends.cache import SessionStore as CacheSession
 from django.contrib.sessions.backends.cached_db import SessionStore as CacheDBSession
 from django.contrib.sessions.backends.file import SessionStore as FileSession
+from django.contrib.sessions.backends.cookies import SessionStore as CookieSession
 from django.contrib.sessions.models import Session
 from django.contrib.sessions.middleware import SessionMiddleware
 from django.core.exceptions import ImproperlyConfigured, SuspiciousOperation
 from django.http import HttpResponse
 from django.test import TestCase, RequestFactory
+from django.test.utils import override_settings
 from django.utils import unittest
 
 
@@ -214,10 +216,7 @@ class SessionTestsMixin(object):
         # Tests get_expire_at_browser_close with different settings and different
         # set_expiry calls
         try:
-            try:
-                original_expire_at_browser_close = settings.SESSION_EXPIRE_AT_BROWSER_CLOSE
-                settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = False
-
+            with override_settings(SESSION_EXPIRE_AT_BROWSER_CLOSE=False):
                 self.session.set_expiry(10)
                 self.assertFalse(self.session.get_expire_at_browser_close())
 
@@ -227,8 +226,7 @@ class SessionTestsMixin(object):
                 self.session.set_expiry(None)
                 self.assertFalse(self.session.get_expire_at_browser_close())
 
-                settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = True
-
+            with override_settings(SESSION_EXPIRE_AT_BROWSER_CLOSE=True):
                 self.session.set_expiry(10)
                 self.assertFalse(self.session.get_expire_at_browser_close())
 
@@ -237,11 +235,8 @@ class SessionTestsMixin(object):
 
                 self.session.set_expiry(None)
                 self.assertTrue(self.session.get_expire_at_browser_close())
-
-            except:
-                raise
-        finally:
-            settings.SESSION_EXPIRE_AT_BROWSER_CLOSE = original_expire_at_browser_close
+        except:
+            raise
 
     def test_decode(self):
         # Ensure we can decode what we encode
@@ -302,9 +297,10 @@ class FileSessionTests(SessionTestsMixin, unittest.TestCase):
         shutil.rmtree(self.temp_session_store)
         super(FileSessionTests, self).tearDown()
 
+    @override_settings(
+        SESSION_FILE_PATH="/if/this/directory/exists/you/have/a/weird/computer")
     def test_configuration_check(self):
         # Make sure the file backend checks for a good storage dir
-        settings.SESSION_FILE_PATH = "/if/this/directory/exists/you/have/a/weird/computer"
         self.assertRaises(ImproperlyConfigured, self.backend)
 
     def test_invalid_key_backslash(self):
@@ -324,17 +320,9 @@ class CacheSessionTests(SessionTestsMixin, unittest.TestCase):
 
 
 class SessionMiddlewareTests(unittest.TestCase):
-    def setUp(self):
-        self.old_SESSION_COOKIE_SECURE = settings.SESSION_COOKIE_SECURE
-        self.old_SESSION_COOKIE_HTTPONLY = settings.SESSION_COOKIE_HTTPONLY
-
-    def tearDown(self):
-        settings.SESSION_COOKIE_SECURE = self.old_SESSION_COOKIE_SECURE
-        settings.SESSION_COOKIE_HTTPONLY = self.old_SESSION_COOKIE_HTTPONLY
 
+    @override_settings(SESSION_COOKIE_SECURE=True)
     def test_secure_session_cookie(self):
-        settings.SESSION_COOKIE_SECURE = True
-
         request = RequestFactory().get('/')
         response = HttpResponse('Session test')
         middleware = SessionMiddleware()
@@ -347,9 +335,8 @@ class SessionMiddlewareTests(unittest.TestCase):
         response = middleware.process_response(request, response)
         self.assertTrue(response.cookies[settings.SESSION_COOKIE_NAME]['secure'])
 
+    @override_settings(SESSION_COOKIE_HTTPONLY=True)
     def test_httponly_session_cookie(self):
-        settings.SESSION_COOKIE_HTTPONLY = True
-
         request = RequestFactory().get('/')
         response = HttpResponse('Session test')
         middleware = SessionMiddleware()
@@ -361,3 +348,15 @@ class SessionMiddlewareTests(unittest.TestCase):
         # Handle the response through the middleware
         response = middleware.process_response(request, response)
         self.assertTrue(response.cookies[settings.SESSION_COOKIE_NAME]['httponly'])
+
+
+class CookieSessionTests(SessionTestsMixin, TestCase):
+
+    backend = CookieSession
+
+    def test_save(self):
+        """
+        This test tested exists() in the other session backends, but that
+        doesn't make sense for us.
+        """
+        pass
diff --git a/django/core/signing.py b/django/core/signing.py
index 3165cf8..224d942 100644
--- a/django/core/signing.py
+++ b/django/core/signing.py
@@ -161,7 +161,7 @@ class TimestampSigner(Signer):
     def __init__(self, *args, **kwargs):
         self.time_func = kwargs.pop('time', time.time)
         super(TimestampSigner, self).__init__(*args, **kwargs)
-    
+
     def timestamp(self):
         return baseconv.base62.encode(int(self.time_func() * 10000))
 
diff --git a/docs/topics/http/sessions.txt b/docs/topics/http/sessions.txt
index 8529f53..9d19acc 100644
--- a/docs/topics/http/sessions.txt
+++ b/docs/topics/http/sessions.txt
@@ -95,6 +95,33 @@ defaults to output from ``tempfile.gettempdir()``, most likely ``/tmp``) to
 control where Django stores session files. Be sure to check that your Web
 server has permissions to read and write to this location.
 
+Using cookie-based sessions
+---------------------------
+
+.. versionadded:: 1.4
+
+To use cookies-based sessions, set the :setting:`SESSION_ENGINE` setting to
+``"django.contrib.sessions.backends.cookies"``. The session data will be
+stored using Django's tools for :doc:`cryptographic signing </topics/signing>`
+and the :setting:`SECRET_KEY` setting. It's recommended to set the
+:setting:`SESSION_COOKIE_HTTPONLY` to ``True`` to prevent tampering from
+JavaScript.
+
+.. warning::
+
+    The session data is **signed but not encrypted**!
+
+    When using the cookies backend the session data can be read out
+    and will be invalidated when being tampered with. The same invalidation
+    happens if the client storing the cookie (e.g. your user's browser)
+    can't store all of the session cookie and drops data. Even though
+    Django compresses the data before it's still entirely possible to
+    exceed the `common limit of 4096 bytes`_ per cookie.
+
+    Also, the size of a cookie can have an impact on the `speed of your site`_.
+
+.. _`common limit of 4096 bytes`: http://tools.ietf.org/html/rfc2965#section-5.3
+.. _`speed of your site`: http://yuiblog.com/blog/2007/03/01/performance-research-part-3/
 
 Using sessions in views
 =======================
@@ -420,6 +447,7 @@ Controls where Django stores session data. Valid values are:
     * ``'django.contrib.sessions.backends.file'``
     * ``'django.contrib.sessions.backends.cache'``
     * ``'django.contrib.sessions.backends.cached_db'``
+    * ``'django.contrib.sessions.backends.cookies'``
 
 See `configuring the session engine`_ for more details.
 
