hopefully fix for the frozen session issue

through some reason or another, people are somehow get cookies that
aren't prepended with a dot. this is a problem because both sessions at,
as best as I can tell, mix so it tries to read from a different cookie
than we write to. this essentially "freezes" the session in place. users
are unable to login, logout, signup, toggle poor mode, toggle NSFW, etc.
this attempts to delete bad session cookies (i.e. cookies with a domain
that don't start with a dot). we don't do this on "dotless" domains
(and by extension localhost) because browser support for setting cookies
on FQDNs that only have one dot has tenuous support among browsers
anyway). this *may* log some people out, but... their days of being able
to do stuff on the site were numbered anyway.
pull/50/head
justcool393 2022-12-06 12:05:17 -06:00
parent 6c491b9d11
commit 6fc99839ce
2 changed files with 46 additions and 14 deletions

View File

@ -29,7 +29,8 @@ app.config['SERVER_NAME'] = SITE
app.config['SECRET_KEY'] = environ.get('SECRET_KEY').strip()
app.config['SEND_FILE_MAX_AGE_DEFAULT'] = 3153600
if not IS_LOCALHOST:
app.config['SESSION_COOKIE_DOMAIN'] = f'.{SITE}'
app.config["COOKIE_DOMAIN"] = f'.{SITE}'
app.config['SESSION_COOKIE_DOMAIN'] = app.config["COOKIE_DOMAIN"]
app.config["SESSION_COOKIE_SECURE"] = True
app.config["SESSION_COOKIE_NAME"] = "session_" + environ.get("SITE_NAME").strip().lower()
app.config['MAX_CONTENT_LENGTH'] = 100 * 1024 * 1024

View File

@ -52,23 +52,54 @@ def before_request():
@app.after_request
def after_request(response):
def after_request(response:Response):
_fix_frozen_sessions(response)
if response.status_code < 400:
if CLOUDFLARE_AVAILABLE and CLOUDFLARE_COOKIE_VALUE and g.desires_auth:
logged_in = bool(getattr(g, 'v', None))
response.set_cookie("lo", CLOUDFLARE_COOKIE_VALUE if logged_in else '',
max_age=60*60*24*365 if logged_in else 1, samesite="Lax")
if getattr(g, 'db', None):
g.db.commit()
g.db.close()
del g.db
_set_cloudflare_cookie(response)
_commit_and_close_db()
return response
@app.teardown_appcontext
def teardown_request(error):
if getattr(g, 'db', None):
g.db.rollback()
g.db.close()
del g.db
_rollback_and_close_db()
stdout.flush()
def _set_cloudflare_cookie(response:Response) -> None:
'''
Sets a cookie that can be used by an upstream DDoS protection and caching provider
'''
if not g.desires_auth: return
if not CLOUDFLARE_AVAILABLE or not CLOUDFLARE_COOKIE_VALUE: return
logged_in = bool(getattr(g, 'v', None))
response.set_cookie("lo", CLOUDFLARE_COOKIE_VALUE if logged_in else '',
max_age=60*60*24*365 if logged_in else 1, samesite="Lax",
domain=app.config["COOKIE_DOMAIN"])
def _fix_frozen_sessions(response:Response) -> None:
'''
Deletes bad session cookies, hopefuly resolving an ongoing issue with sessions becoming
frozen. This deletes cookies whose domain don't start with a dot (on domains that have
at least one dot in them)
'''
domain = app.config["SESSION_COOKIE_DOMAIN"]
if IS_LOCALHOST or not '.' in domain: return # "dotless" domains in general aren't really supportable
bad_domain = domain.replace('.', '', 1)
cookie_header = request.headers.get("Cookie")
response.delete_cookie(app.config["SESSION_COOKIE_NAME"], domain=bad_domain, httponly=True, secure=True)
if not cookie_header or not f'domain={bad_domain}' in cookie_header: return
def _commit_and_close_db() -> bool:
if not getattr(g, 'db', None): return False
g.db.commit()
g.db.close()
del g.db
return True
def _rollback_and_close_db() -> bool:
if not getattr(g, 'db', None): return False
g.db.rollback()
g.db.close()
del g.db
return True