티스토리 수익 글 보기

티스토리 수익 글 보기

[1.8.x] Fixed #19324 — Avoided creating a session record when loadin… · django/django@66d12d1 · GitHub
Skip to content
/ django Public

Commit 66d12d1

Browse files
carljmtimgraham
authored andcommitted
[1.8.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 64e8a5f commit 66d12d1

File tree

8 files changed

+95
8
lines changed

8 files changed

+95
8
lines changed

django/contrib/sessions/backends/cache.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def load(self):
2727
session_data = None
2828
if session_data is not None:
2929
return session_data
30-
self.create()
30+
self._session_key = None
3131
return {}
3232

3333
def create(self):
@@ -49,6 +49,8 @@ def create(self):
4949
"It is likely that the cache is unavailable.")
5050

5151
def save(self, must_create=False):
52+
if self.session_key is None:
53+
return self.create()
5254
if must_create:
5355
func = self._cache.add
5456
else:
@@ -60,7 +62,7 @@ def save(self, must_create=False):
6062
raise CreateError
6163

6264
def exists(self, session_key):
63-
return (KEY_PREFIX + session_key) in self._cache
65+
return session_key and (KEY_PREFIX + session_key) in self._cache
6466

6567
def delete(self, session_key=None):
6668
if session_key is None:

django/contrib/sessions/backends/cached_db.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,12 @@ def load(self):
5151
logger = logging.getLogger('django.security.%s' %
5252
e.__class__.__name__)
5353
logger.warning(force_text(e))
54-
self.create()
54+
self._session_key = None
5555
data = {}
5656
return data
5757

5858
def exists(self, session_key):
59-
if (KEY_PREFIX + session_key) in self._cache:
59+
if session_key and (KEY_PREFIX + session_key) in self._cache:
6060
return True
6161
return super(SessionStore, self).exists(session_key)
6262

django/contrib/sessions/backends/db.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ def load(self):
2626
logger = logging.getLogger('django.security.%s' %
2727
e.__class__.__name__)
2828
logger.warning(force_text(e))
29-
self.create()
29+
self._session_key = None
3030
return {}
3131

3232
def exists(self, session_key):
@@ -43,7 +43,6 @@ def create(self):
4343
# Key wasn't unique. Try again.
4444
continue
4545
self.modified = True
46-
self._session_cache = {}
4746
return
4847

4948
def save(self, must_create=False):
@@ -53,6 +52,8 @@ def save(self, must_create=False):
5352
create a *new* entry (as opposed to possibly updating an existing
5453
entry).
5554
"""
55+
if self.session_key is None:
56+
return self.create()
5657
obj = Session(
5758
session_key=self._get_or_create_session_key(),
5859
session_data=self.encode(self._get_session(no_load=must_create)),

django/contrib/sessions/backends/file.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def load(self):
9797
self.delete()
9898
self.create()
9999
except (IOError, SuspiciousOperation):
100-
self.create()
100+
self._session_key = None
101101
return session_data
102102

103103
def create(self):
@@ -108,10 +108,11 @@ def create(self):
108108
except CreateError:
109109
continue
110110
self.modified = True
111-
self._session_cache = {}
112111
return
113112

114113
def save(self, must_create=False):
114+
if self.session_key is None:
115+
return self.create()
115116
# Get the session data now, before we start messing
116117
# with the file it is stored within.
117118
session_data = self._get_session(no_load=must_create)

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.

docs/releases/1.7.9.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,27 @@ Django 1.7.9 release notes
66

77
Django 1.7.9 fixes several security issues and bugs in 1.7.8.
88

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.
29+
930
Bugfixes
1031
========
1132

docs/releases/1.8.3.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,27 @@ Also, ``django.utils.deprecation.RemovedInDjango20Warning`` was renamed to
1111
1.11 (LTS), 2.0 (drops Python 2 support). For backwards compatibility,
1212
``RemovedInDjango20Warning`` remains as an importable alias.
1313

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

tests/sessions_tests/tests.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,11 @@ def test_cycle(self):
175175
self.assertNotEqual(self.session.session_key, prev_key)
176176
self.assertEqual(list(self.session.items()), prev_data)
177177

178+
def test_save_doesnt_clear_data(self):
179+
self.session['a'] = 'b'
180+
self.session.save()
181+
self.assertEqual(self.session['a'], 'b')
182+
178183
def test_invalid_key(self):
179184
# Submitting an invalid session key (either by guessing, or if the db has
180185
# removed the key) results in a new key being generated.
@@ -313,6 +318,21 @@ def test_actual_expiry(self):
313318
self.session.delete(old_session_key)
314319
self.session.delete(new_session_key)
315320

321+
def test_session_load_does_not_create_record(self):
322+
"""
323+
Loading an unknown session key does not create a session record.
324+
325+
Creating session records on load is a DOS vulnerability.
326+
"""
327+
if self.backend is CookieSession:
328+
raise unittest.SkipTest("Cookie backend doesn't have an external store to create records in.")
329+
session = self.backend('someunknownkey')
330+
session.load()
331+
332+
self.assertFalse(session.exists(session.session_key))
333+
# provided unknown key was cycled, not reused
334+
self.assertNotEqual(session.session_key, 'someunknownkey')
335+
316336

317337
class DatabaseSessionTests(SessionTestsMixin, TestCase):
318338

0 commit comments

Comments
 (0)