-
Notifications
You must be signed in to change notification settings - Fork 688
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added expiration to sessions #1494
Added expiration to sessions #1494
Conversation
Sorry, but I already have implemented similar functionality in my branch, so I'm going to close this one. |
Good PR though 😸 |
If you want to connect with us more on Gitter that would be awesome. We're trying to increase organization via Github comments, labels, & assignments in order to make collaboration with outside contributors easier without the need to rely much on either tools, but it's clearly an imperfect system, and still an organization-in-progress. Just noting this because we've closed a couple of your PRs that addressed "help wanted" issues that more organization and/or communication would've saved you from writing in the first place. |
0fbf174
to
97c601f
Compare
Oh hai everyone. I'm back with this PR again. Rebased on develop. Pros: sessions only good for 30 minutes Cons: Exact login times leaked in the cookies. This only really matters for sources and could be mitigated with @fowlslegs suggestion about using some sort of AEAD to encrypt/authenticate the session. |
@heartsucker it would be good to have the timeout configurable for test purposes. The test could verify that the session expires after say 0.1 sec ? |
@heartsucker can you flesh out a little more exactly how the timing of log in leaked to the cookies could potentially affect (or not affect) sources? |
@heartsucker this implementation looks fine. I'm a little surprised this does not already exist in flask since it's a common pattern. |
@trevortimm There's a discussion on #204 that covers a lot of this. At the end of the day, if someone has the cookie, they have the codename which means they win. So having the exact time of a session isn't terribly important. Thus, the con is really a very minor one that I'm more pointing out as a way of making note that we have very minimally expanded the amount of information an attacker could receive rather than stating it as an argument against this PR. On the source side, this mostly just protects against leaving a computer unattended. This is likely in that a long upload may mean they have to leave the computer unattended for a long time and popping their session could be very useful here. |
Also, this is strange. I don't get test failures locally. Travis did just update their trusty image today, so that could be related? |
@heartsucker looking at flask/sessions.py it seems possible to set permanent_session_lifetime to 30 minutes to get the same result. Am I missing something ? The permanent is the name of the variable is troubling ;-) |
@dachary Yes, see #880 (comment). Note that it's been a while since I've audited Firefox/Tor Browser's cookie handling code, and doing an up-to-date review is probably worthwhile. Also note that Tor Browser does things differently from Firefox, usually with an eye towards minimizing privacy risks/data leakage, so they may have recent (since 2015) patches that would help with this problem. It would also be good for somebody to clearly articulate the threat model in this case (and yes, it's my bad for not having done so already). Here's a rough sketch:
|
@dachary Missed your earlier comment. Yes, this could be configured or patched for testing. This was more meant as "reopen for review of functionality" thread and then if we decide this is something we want, I can polish it up. |
@heartsucker FWIW 💯 on implementing this session timeout :-) |
So the threat model with this feature would prevent capturing a cookie from being an unlimited access token, but the problem is that a source's codename is stored in the session. Once this is implemented, we can leave just a session token in the session use that for the current login behavior. My proposal would be to merge this (already useful for the journalist) and then work on a scheme to alter the login behavior of the source. |
Proposed during the engineering meeting: look at Flask-Login as it's better supported than it was in the past and therefore is a candidate to offload code to something with more eyes than just SD. However, this depends on how Tor Browser handles cookies. It is only an option if TB treats all cookies as session cookies. Writing anything to disk is not acceptable. |
Needs a rebase but if everyone agrees with my logic here, then I think this is a worthwhile incremental improvement. |
bad6431
to
25e1fde
Compare
ab239f6
to
103904f
Compare
Pinging @redshiftzero for final review on this one. |
securedrop/journalist.py
Outdated
flash('You have been logged out due to inactivity', 'error') | ||
|
||
session['expires'] = datetime.utcnow() + \ | ||
timedelta(minutes=getattr(config, 'SESSION_EXPIRATION_MINUTES', 30)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's good that this has a default of 30 minutes, but otherwise where is this config option defined? (x-linking with #1966). Or are you just assuming that it isn't defined?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct. I'm assuming it isn't defined, and I use getattr
everywhere. Should someone really want to override it, it will pick it up. The unit/integration tests assume it's undefined and have checks to del
the field in the finally
block to not affect other tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent. Let's also add it into config.py.example
as well so that we have all the possible config options documented in one place for the migration that will take place for #1966.
103904f
to
a53684e
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -0,0 +1,1195 @@ | |||
# Arabic translations for SecureDrop. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I suspect this file unintentionally snuck into a commit :)
securedrop/journalist.py
Outdated
flash('You have been logged out due to inactivity', 'error') | ||
|
||
session['expires'] = datetime.utcnow() + \ | ||
timedelta(minutes=getattr(config, 'SESSION_EXPIRATION_MINUTES', 30)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent. Let's also add it into config.py.example
as well so that we have all the possible config options documented in one place for the migration that will take place for #1966.
securedrop/tests/test_source.py
Outdated
|
||
logger.assert_called_once_with( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait where did this logger assertion go?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lost in the rebase D:
securedrop/journalist.py
Outdated
@@ -65,6 +65,13 @@ def get_source(filesystem_id): | |||
@app.before_request | |||
def setup_g(): | |||
"""Store commonly used values in Flask's special g object""" | |||
if 'expires' in session and datetime.utcnow() >= session['expires']: | |||
session.clear() | |||
flash('You have been logged out due to inactivity', 'error') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gettext('You have been logged out due to inactivity')
🇩🇪 🇸🇦 🇫🇷 🇳🇴
securedrop/source_app/__init__.py
Outdated
@@ -76,6 +77,15 @@ def setup_g(): | |||
g.html_lang = i18n.locale_to_rfc_5646(g.locale) | |||
g.locales = i18n.get_locale2name() | |||
|
|||
if 'expires' in session and datetime.utcnow() >= session['expires']: | |||
session.clear() | |||
flash('You have been logged out due to inactivity', 'error') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
gettext
here too
(:eyes: on #2391)
a53684e
to
344ffb2
Compare
55c7ae9
to
a38e303
Compare
a38e303
to
5090c3d
Compare
@redshiftzero Things are fixed, and I pushed enough times to kill transient CircleCI errors. :D |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Approving this! This is a good incremental improvement - and note it also fixes #2290, which is a confusing error message due to CSRF tokens expiring (they last 24 hours) that users have been seeing.
I do think we should add a notification to make clear to sources that even if the source session has expired, they still need to restart or exit Tor Browser. I'll make a followup issue for this since the functionality here is solid.
A quick fix to #880. Also, this should be compatible with with the fix @fowlslegs is proposing in #204 (comment).
It works by manually setting a session expiration time in the cookie's data and then checks it before each request either extending it or logging the user out. Sessions are set to expire after 30 minutes.