How do I create a new clean session and invalidate the current one in Flask?
Do I use make_null_session() or open_session()?
I do this by calling session.clear().
EDIT:
After reading your comment in another answer, I see that you're trying to prevent a replay attack that might be made using a cookie that was issued in the past. I solved that problem as much as possible* with this approach:
Override SecureCookieSessionInterface.save_session(), copying the code from the overridden version rather than calling it.
When the overridden version of save_session() calls save_cookie(), make it pass a session_expires argument 30 minutes in the future. This causes cookies more than 30 minutes old to be considered invalid.
Make the overridden version of save_session() update a session variable every so often, to make sure the cookie and its session_expires time get rewritten regularly. (I name this session variable '_refresh' and store the current time in it, then rewrite it only if more than a few seconds have passed since the last-stored time. This optimization avoids rewriting the cookie on every HTTP request.)
Duplicating Flask code in the custom save_session() makes this approach a bit ugly and brittle, but it is necessary in order to change the arguments passed to save_cookie(). It would be nice if Flask made this easier, or at least implemented its own safeguard against replay attacks.
*WARNING: This approach by itself will not stop replay attacks that might happen during a session cookie's valid lifetime. This fundamental problem with cookie-based sessions is discussed in RFC 6896 and A Secure Cookie Protocol by Liu, Kovacs, Huang, Gouda.
If you have security concerns (and everyone should have) There is the answer:
This is not REALLY possible
Flask uses cookie-based sessions. When you edit or delete session, you send a REQUEST to CLIENT to remove the cookie, normal clients (browsers) will do. But if session hijacked by an attacker, the attacker's session remains valid.
You can add an after_request callback to remove the session cookie if a particular flag is set:
#app.after_request
def remove_if_invalid(response):
if "__invalidate__" in session:
response.delete_cookie(app.session_cookie_name)
return response
Then you simply set that session key whenever you want to invalidate the session:
#app.route("/logout")
def logout():
session["__invalidate__"] = True
return redirect(url_for("index"))
See also: http://werkzeug.pocoo.org/docs/wrappers/#werkzeug.wrappers.BaseResponse.delete_cookie
If you use default flask sessions and set the app.permanent_session_lifetime, then the session will not work if a user tries to replay the same session as long as the session has expired.If you look at the source code for open_session, there is line:
max_age = total_seconds(app.permanent_session_lifetime)
try:
data = s.loads(val, max_age=max_age)
return self.session_class(data)
except BadSignature:
return self.session_class()
Related
I have an error recovery problem in a Flask view function. Its simplified version is here:
#app.route('/log', methods=['POST', 'GET'])
def elog():
form = LogForm()
if form.validate_on_submit():
flask.session['logformdata'] = form.data
return flask.redirect(flask.url_for('elog'))
try:
formdata = flask.session.pop('logformdata')
except KeyError:
return flask.render_template('log.html', form=form)
log = ... # result of a query
form.process(data=formdata)
return flask.render_template('log.html', form=form, log=log)
The important part is that it implements the post/redirect/get pattern and stores the form data between POST and GET in the flask.session storage which is implemented with HTTP cookies.
Now let's assume there is a bug somewhere and the function crashes with certain input. Of course, it should not happen. But when it does, the user's experience is terrible.
In detail:
the user posts a form (POST)
the form data is stored in the flask.session, i.e. as a cookie
after a redirect, the function is called again (GET), but now it crashes unexpectedly. The user sees some error message. That is not good, but bugs happen.
the user reloads the page intending to start over, but gets the same error repeated again and again!
The key point is that the statement flask.session.pop removes the form data from the session storage, but when the function crashes, the corresponding cookie remains in the user's browser. Each reload triggers the bug again. Restarting the browser may help (depending on session.permanent flag). The only guaranteed remedy is to manually delete the cookie from the browser. This effectively makes the webpage unusable.
I think I can mitigate the problem with setting a very short cookie lifetime (15 seconds or so) or by generating a new secret key after each restart. I did not try it and it is definitely not a good solution if session cookie contains other data.
How can I make functions like the one above more robust?
If there's no way to handle the error within the view itself, my approach would be to register an error handler for the entire app, and manage the session within here i.e. reset to a default/safe state.
Example:
#app.errorhandler(500)
def handle_internal_server_error(ex):
"""Handle an unexpected and uncaught (500) server error."""
# Reset session here & display error (e.g. flash or other)
return # redirect or render
I think your simplified example misses an important bit representing what exactly crashes.
If some piece of the logic can crash unexpectedly you should expect for it to do so and handle these cases. For example, by wrapping it in a usual try/catch and removing the session cookie within the catch block.
You are managing the session manually and that is causing this issue. Please look into implementing Message Flashing in your application. You can customize flashes to use server side session or even use cookies. Flash messages can be maintained across multiple pages even with redirects as well.
I'm using Flask builtin session mecanism.
Here is my understanding of session mecanism (with flask) :
all session data are stored in a signed cookie (with app.secret_key)
when a session data is modified, the cookie is changed
session's data are protected against write client side (due to signature) but not against read
Imagine the following scenario :
In my session I put a variable try_number=3
Each time the user make an action, a decrease this number
If this number is equal to 0, action is forbidden
The user connect to the application for the first time, the application send a Set-Cookie: sesssion=Flask.sign("try_number=3"), let's call this cookie COOKIE_A.
The user perform his first action, he send COOKIE_A, the application reply with Set-Cookie: sesssion=Flask.sign("try_number=2"), let's call this cookie COOKIE_B.
Now, if the user perform another action, but doesn't use COOKIE_B but COOKIE_A again (using curl for exemple), the cookie is still signed, and will be handled by the server, with try_number=3.
Therefore, only using the COOKIE_A for all operation, he will be able to "spoof" session mecanism, and make unlimited action with the same session.
Is there any builtin mecanism to prevent this ?
(I'm not talking about snippet for using sqlite / redis, but builtin solution)
This is not a failure of the security of Flask's cookies, it's a failure of your counter design. There is no built in protection against replay attacks.
You can shorten the expiration time of the session cookie. This doesn't really solve the problem, it just makes the window smaller. It also makes the session inconvenient for regular use, which would annoy your normal users.
Ultimately, you'll have to store some information on the server and check against it. You could send a nonce with every request and keep a store of which ones have been sent back, ignoring ones that have been seen before. You could also just store all session information (except some identifying key) on the server side, so it can't be re-sent.
Two questions regarding session timeouts in cherrypy:
1) Is there a way to determine the remaining time in a session? This is related to trying to use http://plugins.jquery.com/epilgrim.sessionTimeoutHandler/
2) Is there a way to make a call to cherrypy NOT reset the timeout, such that the plugin above could call a URL to determine the time remaining in the session without resetting said time
Edit to help clarify: The purpose here is to be able to have a client-side process that periodically queries the server via AJAX to determine the amount of time left in a users session. This is to overcome difficulties with keeping a client side session timeout timer in-sync with the server-side timer - I'd like to simply have the client ask the server "how much time do I have left?" and act accordingly. Of course, if the act of asking resets the timeout, then this won't work, as the AJAX "time left" requests would essentially become a session keep-alive. So I need to be able to make an AJAX query to the server without resetting the session timeout timer for the user.
I believe cherrypy uses the expiration time in the cookie with the key session_id. Mine says:
Wed 22 Jan 2014 03:44:31 PM EST
You could extend the expiration with your set of circumstances and edit the session cookie.
EDIT: You will also need to extend the server timeout...
cherrypy.request.config.update({'tools.sessions.timeout': 60})
https://groups.google.com/forum/#!topic/cherrypy-users/2yrG79QoYFQ
Hope this helps!
You need to subclass the session and add a "stats" function to it and a flag to prevent saving in the session "stats" request handler. Or disable sessions in the config for the "stats" path and load session exp info directly from your storage without using normal session class.
I have found the answer to 2) question while going through source code of cherrypy session class. Apparently, you do not want to save session after serving such requests - this will then also not update expiration time (and will not save any changes to session object).
I found in the source code that setting cherrypy.serving.request._sessionsaved = True does exactly that. And added decorator for convinience:
def nosessionsave( func ):
"""
Decorator to avoid session saving and thus not resetting session timeout.
"""
def decorate( *args, **data ):
cherrypy.serving.request._sessionsaved = True
return func( *args, **data )
return decorate
Just add #nosessionsave before method def.
I'm building an application in which the client pings the server every now and then (let's not get into why). When the server handles these requests, it checks whether the client is logged in or not using request.user.is_authenticated()
It looks something like this:
def handle_ping_request(request):
if request.user.is_authenticated():
# Do something...
else:
# Do Something else...
I've noticed that sometimes the server receives a log-in request immediately followed by a ping request (from the same user). The client is then successfully logged-in, the response returns with a new session ID (of the logged in user) and (I guess that) the old session-ID (of the anonymous user) is removed. When the ping request is processed, its request contains the old session-ID. Thus the ping request returns with a third session ID and on the next request the client makes, the client is no longer logged in.
My log-in code looks something like:
if not request.user.is_authenticated():
user = auth.authenticate(...credentials...)
if user and user.is_active:
auth.login(request, user)
Do you have any suggestions on how to avoid this problem? Preferably without involving the client.
Thanks.
It's probably too messy to handle this on the server because you'll have to create some kind of a semaphore system that will also try to guess if any ping is currently from a client that is also being authenticated. My suggestion would be simply to change the client code not to ping while it's waiting for a response to its login request.
You could create an alternative to the standard contrib.auth.login method that keeps the same session id, rather than generating a new one, either by using a custom authentication backend that doesn't generate a new key, or by creating a custom session backend that overrides the cycle_key() method in contrib.sessions.base to reuse the same key.
BUT: think about what you might be opening yourself up to by reusing the same session key - depending on where this system is in use, you'd be making yourself more open to session hijacking (ie: there's only one session id to sniff), as well as potentially issues where caches may return the unauth page content instead of the auth page content because the sessionid is technically the same and the cache can't tell the difference between the two situations, etc, etc, etc.
In short: there's a reason it's built to work, by default, the way it does.
I want keep track of a unique identifier for each browser that connects to my web application (that is written in Pylons.) I keep a cookie on the client to keep track of this, but if the cookie isn't present, then I want to generate a new unique identifier that will be sent back to the client with the response, but I also may want to access this value from other code used to generate the response.
Is attaching this value to pylons.request safe? Or do I need to do something like use threading_local to make a thread local that I reset when each new request is handled?
Why do you want a unique identifier? Basically every visitor already gets a unique identifier, his Session. Beaker, Pylons session and caching middleware, does all the work and tracks visitors, usually with a Session cookie. So don't care about tracking users, just use the Session for what it's made for, to store whatever user specific stuff you have .
from pylons import session
session["something"] = whatever()
session.save()
# somewhen later
something = session["something"]
Whatever you were to set on the request will only survive for the duration of the request. the problem you are describing is more appropriately handled with a Session as TCH4k has said. It's already enabled in the middleware, so go ahead.