Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 2e47f3e

Browse files
carljmtimgraham
authored andcommitted
[1.4.x] Fixed #19324 -- Avoided creating a session record when loading the session.
The session record is now only created if/when the session is modified. This prevents a potential DoS via creation of many empty session records. This is a security fix; disclosure to follow shortly.
1 parent c570a5e commit 2e47f3e

6 files changed

Lines changed: 54 additions & 9 deletions

File tree

django/contrib/sessions/backends/cache.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def load(self):
2525
session_data = None
2626
if session_data is not None:
2727
return session_data
28-
self.create()
28+
self._session_key = None
2929
return {}
3030

3131
def create(self):
@@ -45,6 +45,8 @@ def create(self):
4545
raise RuntimeError("Unable to create a new session key.")
4646

4747
def save(self, must_create=False):
48+
if self.session_key is None:
49+
return self.create()
4850
if must_create:
4951
func = self._cache.add
5052
else:
@@ -56,7 +58,7 @@ def save(self, must_create=False):
5658
raise CreateError
5759

5860
def exists(self, session_key):
59-
return (KEY_PREFIX + session_key) in self._cache
61+
return session_key and (KEY_PREFIX + session_key) in self._cache
6062

6163
def delete(self, session_key=None):
6264
if session_key is None:

django/contrib/sessions/backends/cached_db.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,12 @@ def load(self):
3030
data = None
3131
if data is None:
3232
data = super(SessionStore, self).load()
33-
cache.set(self.cache_key, data, settings.SESSION_COOKIE_AGE)
33+
if self.session_key:
34+
cache.set(self.cache_key, data, settings.SESSION_COOKIE_AGE)
3435
return data
3536

3637
def exists(self, session_key):
37-
if (KEY_PREFIX + session_key) in cache:
38+
if session_key and (KEY_PREFIX + session_key) in cache:
3839
return True
3940
return super(SessionStore, self).exists(session_key)
4041

django/contrib/sessions/backends/db.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ def load(self):
2020
)
2121
return self.decode(force_unicode(s.session_data))
2222
except (Session.DoesNotExist, SuspiciousOperation):
23-
self.create()
23+
self._session_key = None
2424
return {}
2525

2626
def exists(self, session_key):
@@ -37,7 +37,6 @@ def create(self):
3737
# Key wasn't unique. Try again.
3838
continue
3939
self.modified = True
40-
self._session_cache = {}
4140
return
4241

4342
def save(self, must_create=False):
@@ -47,6 +46,8 @@ def save(self, must_create=False):
4746
create a *new* entry (as opposed to possibly updating an existing
4847
entry).
4948
"""
49+
if self.session_key is None:
50+
return self.create()
5051
obj = Session(
5152
session_key=self._get_or_create_session_key(),
5253
session_data=self.encode(self._get_session(no_load=must_create)),

django/contrib/sessions/backends/file.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,11 @@ def load(self):
5656
try:
5757
session_data = self.decode(file_data)
5858
except (EOFError, SuspiciousOperation):
59-
self.create()
59+
self._session_key = None
6060
finally:
6161
session_file.close()
6262
except IOError:
63-
self.create()
63+
self._session_key = None
6464
return session_data
6565

6666
def create(self):
@@ -71,10 +71,11 @@ def create(self):
7171
except CreateError:
7272
continue
7373
self.modified = True
74-
self._session_cache = {}
7574
return
7675

7776
def save(self, must_create=False):
77+
if self.session_key is None:
78+
return self.create()
7879
# Get the session data now, before we start messing
7980
# with the file it is stored within.
8081
session_data = self._get_session(no_load=must_create)

django/contrib/sessions/tests.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,11 @@ def test_cycle(self):
162162
self.assertNotEqual(self.session.session_key, prev_key)
163163
self.assertEqual(self.session.items(), prev_data)
164164

165+
def test_save_doesnt_clear_data(self):
166+
self.session['a'] = 'b'
167+
self.session.save()
168+
self.assertEqual(self.session['a'], 'b')
169+
165170
def test_invalid_key(self):
166171
# Submitting an invalid session key (either by guessing, or if the db has
167172
# removed the key) results in a new key being generated.
@@ -256,6 +261,20 @@ def test_decode(self):
256261
encoded = self.session.encode(data)
257262
self.assertEqual(self.session.decode(encoded), data)
258263

264+
def test_session_load_does_not_create_record(self):
265+
"""
266+
Loading an unknown session key does not create a session record.
267+
268+
Creating session records on load is a DOS vulnerability.
269+
"""
270+
if self.backend is CookieSession:
271+
raise unittest.SkipTest("Cookie backend doesn't have an external store to create records in.")
272+
session = self.backend('deadbeef')
273+
session.load()
274+
275+
self.assertFalse(session.exists(session.session_key))
276+
# provided unknown key was cycled, not reused
277+
self.assertNotEqual(session.session_key, 'deadbeef')
259278

260279
class DatabaseSessionTests(SessionTestsMixin, TestCase):
261280

docs/releases/1.4.21.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,24 @@ Django 1.4.21 release notes
55
*July 8, 2015*
66

77
Django 1.4.21 fixes several security issues in 1.4.20.
8+
9+
Denial-of-service possibility by filling session store
10+
======================================================
11+
12+
In previous versions of Django, the session backends created a new empty record
13+
in the session storage anytime ``request.session`` was accessed and there was a
14+
session key provided in the request cookies that didn't already have a session
15+
record. This could allow an attacker to easily create many new session records
16+
simply by sending repeated requests with unknown session keys, potentially
17+
filling up the session store or causing other users' session records to be
18+
evicted.
19+
20+
The built-in session backends now create a session record only if the session
21+
is actually modified; empty session records are not created. Thus this
22+
potential DoS is now only possible if the site chooses to expose a
23+
session-modifying view to anonymous users.
24+
25+
As each built-in session backend was fixed separately (rather than a fix in the
26+
core sessions framework), maintainers of third-party session backends should
27+
check whether the same vulnerability is present in their backend and correct
28+
it if so.

0 commit comments

Comments
 (0)