Sign sessions using SECRET_KEY to simplify revocation (#1219)

* Sign sessions using `SECRET_KEY`
* Add `CTFd.utils.security.signing.sign` and `CTFd.utils.security.signing.unsign`
bulk-clear-sessions
Kevin Chung 2020-01-20 21:53:08 -05:00 committed by GitHub
parent 83efc4d5eb
commit 60c46af58a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 32 additions and 2 deletions

View File

@ -1,4 +1,5 @@
from flask import current_app
from itsdangerous import Signer
from itsdangerous.url_safe import URLSafeTimedSerializer
from itsdangerous.exc import ( # noqa: F401
BadTimeSignature,
@ -19,3 +20,17 @@ def unserialize(data, secret=None, max_age=432000):
secret = current_app.config["SECRET_KEY"]
s = URLSafeTimedSerializer(secret)
return s.loads(data, max_age=max_age)
def sign(data, secret=None):
if secret is None:
secret = current_app.config["SECRET_KEY"]
s = Signer(secret)
return s.sign(data)
def unsign(data, secret=None):
if secret is None:
secret = current_app.config["SECRET_KEY"]
s = Signer(secret)
return s.unsign(data)

View File

@ -1,8 +1,10 @@
from flask.sessions import SessionInterface, SessionMixin
from flask.json.tag import TaggedJSONSerializer
from werkzeug.datastructures import CallbackDict
from itsdangerous import BadSignature, want_bytes
from CTFd.cache import cache
from CTFd.utils import text_type
from CTFd.utils.security.signing import sign, unsign
from uuid import uuid4
import six
@ -50,7 +52,7 @@ class CachingSessionInterface(SessionInterface):
def _generate_sid(self):
return str(uuid4())
def __init__(self, key_prefix, use_signer=False, permanent=False):
def __init__(self, key_prefix, use_signer=True, permanent=False):
self.key_prefix = key_prefix
self.use_signer = use_signer
self.permanent = permanent
@ -61,6 +63,14 @@ class CachingSessionInterface(SessionInterface):
sid = self._generate_sid()
return self.session_class(sid=sid, permanent=self.permanent)
if self.use_signer:
try:
sid_as_bytes = unsign(sid)
sid = sid_as_bytes.decode()
except BadSignature:
sid = self._generate_sid()
return self.session_class(sid=sid, permanent=self.permanent)
if not six.PY2 and not isinstance(sid, text_type):
sid = sid.decode("utf-8", "strict")
val = cache.get(self.key_prefix + sid)
@ -99,7 +109,12 @@ class CachingSessionInterface(SessionInterface):
value=val,
timeout=total_seconds(app.permanent_session_lifetime),
)
session_id = session.sid
if self.use_signer:
session_id = sign(want_bytes(session.sid))
else:
session_id = session.sid
response.set_cookie(
app.session_cookie_name,
session_id,