Skip to content

Commit 6b78640

Browse files
panagiksasvetlov
authored andcommitted
Patch Session Fixation (#273)
1 parent 95c2d2e commit 6b78640

File tree

5 files changed

+99
-2
lines changed

5 files changed

+99
-2
lines changed

aiohttp_session/__init__.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,14 @@ class Session(MutableMapping):
1818
def __init__(self, identity, *, data, new, max_age=None):
1919
self._changed = False
2020
self._mapping = {}
21-
self._identity = identity
21+
self._identity = identity if data != {} else None
2222
self._new = new
23+
self._new = new if data != {} else True
2324
self._max_age = max_age
2425
created = data.get('created', None) if data else None
2526
session_data = data.get('session', None) if data else None
2627

27-
if new or created is None:
28+
if self._new or created is None:
2829
self._created = int(time.time())
2930
else:
3031
self._created = created

tests/test_encrypted_cookie_storage.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,27 @@ async def handler(request):
136136
assert '' == morsel.value
137137
assert not morsel['httponly']
138138
assert morsel['path'] == '/'
139+
140+
141+
async def test_encrypted_cookie_session_fixation(aiohttp_client, fernet, key):
142+
async def login(request):
143+
session = await get_session(request)
144+
session['k'] = 'v'
145+
return web.Response()
146+
147+
async def logout(request):
148+
session = await get_session(request)
149+
session.invalidate()
150+
return web.Response()
151+
152+
app = create_app(login, key)
153+
app.router.add_route('DELETE', '/', logout)
154+
client = await aiohttp_client(app)
155+
resp = await client.get('/')
156+
assert 'AIOHTTP_SESSION' in resp.cookies
157+
evil_cookie = resp.cookies['AIOHTTP_SESSION'].value
158+
resp = await client.delete('/')
159+
assert resp.cookies['AIOHTTP_SESSION'].value == ""
160+
client.session.cookie_jar.update_cookies({'AIOHTTP_SESSION': evil_cookie})
161+
resp = await client.get('/')
162+
assert resp.cookies['AIOHTTP_SESSION'].value != evil_cookie

tests/test_memcached_storage.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,3 +203,27 @@ def key_factory():
203203
value = await load_cookie(client, memcached)
204204
assert 'key' in value['session']
205205
assert value['session']['key'] == 'value'
206+
207+
208+
async def test_memcached_session_fixation(aiohttp_client, memcached):
209+
async def login(request):
210+
session = await get_session(request)
211+
session['k'] = 'v'
212+
return web.Response()
213+
214+
async def logout(request):
215+
session = await get_session(request)
216+
session.invalidate()
217+
return web.Response()
218+
219+
app = create_app(login, memcached)
220+
app.router.add_route('DELETE', '/', logout)
221+
client = await aiohttp_client(app)
222+
resp = await client.get('/')
223+
assert 'AIOHTTP_SESSION' in resp.cookies
224+
evil_cookie = resp.cookies['AIOHTTP_SESSION'].value
225+
resp = await client.delete('/')
226+
assert resp.cookies['AIOHTTP_SESSION'].value == ""
227+
client.session.cookie_jar.update_cookies({'AIOHTTP_SESSION': evil_cookie})
228+
resp = await client.get('/')
229+
assert resp.cookies['AIOHTTP_SESSION'].value != evil_cookie

tests/test_nacl_storage.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,3 +129,27 @@ async def handler(request):
129129
assert '' == morsel.value
130130
assert not morsel['httponly']
131131
assert morsel['path'] == '/'
132+
133+
134+
async def test_nacl_session_fixation(aiohttp_client, secretbox, key):
135+
async def login(request):
136+
session = await get_session(request)
137+
session['k'] = 'v'
138+
return web.Response()
139+
140+
async def logout(request):
141+
session = await get_session(request)
142+
session.invalidate()
143+
return web.Response()
144+
145+
app = create_app(login, key)
146+
app.router.add_route('DELETE', '/', logout)
147+
client = await aiohttp_client(app)
148+
resp = await client.get('/')
149+
assert 'AIOHTTP_SESSION' in resp.cookies
150+
evil_cookie = resp.cookies['AIOHTTP_SESSION'].value
151+
resp = await client.delete('/')
152+
assert resp.cookies['AIOHTTP_SESSION'].value == ""
153+
client.session.cookie_jar.update_cookies({'AIOHTTP_SESSION': evil_cookie})
154+
resp = await client.get('/')
155+
assert resp.cookies['AIOHTTP_SESSION'].value != evil_cookie

tests/test_redis_storage.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,30 @@ def key_factory():
245245
assert value['session']['key'] == 'value'
246246

247247

248+
async def test_redis_session_fixation(aiohttp_client, redis):
249+
async def login(request):
250+
session = await get_session(request)
251+
session['k'] = 'v'
252+
return web.Response()
253+
254+
async def logout(request):
255+
session = await get_session(request)
256+
session.invalidate()
257+
return web.Response()
258+
259+
app = create_app(login, redis)
260+
app.router.add_route('DELETE', '/', logout)
261+
client = await aiohttp_client(app)
262+
resp = await client.get('/')
263+
assert 'AIOHTTP_SESSION' in resp.cookies
264+
evil_cookie = resp.cookies['AIOHTTP_SESSION'].value
265+
resp = await client.delete('/')
266+
assert resp.cookies['AIOHTTP_SESSION'].value == ""
267+
client.session.cookie_jar.update_cookies({'AIOHTTP_SESSION': evil_cookie})
268+
resp = await client.get('/')
269+
assert resp.cookies['AIOHTTP_SESSION'].value != evil_cookie
270+
271+
248272
async def test_redis_from_create_pool(redis_params):
249273

250274
async def handler(request):

0 commit comments

Comments
 (0)