Flask: How To Prevent Replay Attacks - python

I'm trying to implement a logic in my Flask application to prevent reply attacks.
Regarding to the question asked here, My idea is to set the current session lifetime when user logs out from the system. In general, it is suggested to set the session lifetime this way:
#app.before_request
def before_request():
session.permanent = True
app.permanent_session_lifetime = timedelta(minutes=10)
However, I want to set my current session life time when user logs out from the system. Something like the following code:
#app.after_request
def app_after_request(response):
response.headers["X-Frame-Options"] = "SAMEORIGIN"
if "__logged_out__" in session and session["__logged_out__"] is True:
session.clear()
response.set_cookie(app.session_cookie_name, '', expires=0)
return response
I also checked this question, but the problem is that I'm dealing with some confidential data and I have to ensure that session is cleared after user logged out from the system. Is there any way to set one session lifetime after creation manually? or is there any easy way to handle this situation with flask-login?

I found the solution. I should simply use Flask-KVSession package to store session data in database (or any other data storage) instead of server memory. As the package website introduced:
Flask-KVSession is an MIT-licensed server-side session replacement for
Flaskā€˜s signed client-based session management. Instead of storing
data on the client, only a securely generated ID is stored on the
client, while the actual session data resides on the server.
You also need to create a key-value paired table in your database (it has named sessions by default, but you can change the name and schema as well) and point it to your flask app object. More information can be found here.

Related

Flask: share sessions between domain.com and username.domain.com

I have a flask running at domain.com
I also have another flask instance on another server running at username.domain.com
Normally user logs in through domain.com
However, for paying users they are suppose to login at username.domain.com
Using flask, how can I make sure that sessions are shared between domain.com and username.domain.com while ensuring that they will only have access to the specifically matching username.domain.com ?
I am concerned about security here.
EDIT:
Later, after reading your full question I noticed the original answer is not what you're looking for.
I've left the original at the bottom of this answer for Googlers, but the revised version is below.
Cookies are automatically sent to subdomains on a domain (in most modern browsers the domain name must contain a period (indicating a TLD) for this behavior to occur). The authentication will need to happen as a pre-processor, and your session will need to be managed from a centralised source. Let's walk through it.
To confirm, I'll proceed assuming (from what you've told me) your setup is as follows:
SERVER 1:
Flask app for domain.com
SERVER 2:
Flask app for user profiles at username.domain.com
A problem that first must be overcome is storing the sessions in a location that is accessible to both servers. Since by default sessions are stored on disk (and both servers obviously don't share the same hard drive), we'll need to do some modifications to both the existing setup and the new Flask app for user profiles.
Step one is to choose where to store your sessions, a database powered by a DBMS such as MySQL, Postgres, etc. is a common choice, but people also often choose to put them somewhere more ephemeral such as Memcachd or Redis for example.
The short version for choosing between these two starkly different systems breaks down to the following:
Database
Databases are readily available
It's likely you already have a database implemented
Developers usually have a pre-existing knowledge of their chosen database
Memory (Redis/Memchachd/etc.)
Considerably faster
Systems often offer basic self-management of data
Doesn't incur extra load on existing database
You can find some examples database sessions in flask here and here.
While Redis would be more difficult to setup depending on each users level of experience, it would be the option I recommend. You can see an example of doing this here.
The rest I think is covered in the original answer, part of which demonstrates the matching of username to database record (the larger code block).
Old solution for a single Flask app
Firstly, you'll have to setup Flask to handle subdomains, this is as easy as specifying a new variable name in your config file. For example, if your domain was example.com you would append the following to your Flask configuration.
SERVER_NAME = "example.com"
You can read more about this option here.
Something quick here to note is that this will be extremely difficult (if not impossible) to test if you're just working off of localhost. As mentioned above, browsers often won't bother to send cookies to subdomains of a domain without dots in the name (a TLD). Localhost also isn't set up to allow subdomains by default in many operating systems. There are ways to do this like defining your own DNS entries that you can look into (/etc/hosts on *UNIX, %system32%/etc/hosts on Windows).
Once you've got your config ready, you'll need to define a Blueprint for a subdomain wildard.
This is done pretty easily:
from flask import Blueprint
from flask.ext.login import current_user
# Create our Blueprint
deep_blue = Blueprint("subdomain_routes", __name__, subdomain="<username>")
# Define our route
#deep_blue.route('/')
def user_index(username):
if not current_user.is_authenticated():
# The user needs to log in
return "Please log in"
elif username != current_user.username:
# This is not the correct user.
return "Unauthorized"
# It's the right user!
return "Welcome back!"
The trick here is to make sure the __repr__ for your user object includes a username key. For eg...
class User(db.Model):
username = db.Column(db.String)
def __repr__(self):
return "<User {self.id}, username={self.username}>".format(self=self)
Something to note though is the problem that arises when a username contains special characters (a space, #, ?, etc.) that don't work in a URL. For this you'll need to either enforce restrictions on the username, or properly escape the name first and unescape it when validating it.
If you've got any questions or requests, please ask. Did this during my coffee break so it was a bit rushed.
You can do this with the builtin Flask sessions, which are cookie-based client-side sessions. To allow users to login to multiple subdomains in '.domain.com', you need only to specify
app.config['SESSION_COOKIE_DOMAIN'] = '.domain.com'
and the client's browser will have a session cookie that allows him to login to every Flask instance that is at 'domain.com'.
This only works if every instance of Flask has the same app.secret_key
For more information, also see
Same Flask login session across two applications

Google+ login - Server side flow - Storing credentials - Python examples

I am building an app on Google App Engine using Flask. I am implementing Google+ login from the server-side flow described in the Python examples: https://developers.google.com/+/web/signin/server-side-flow and https://github.com/googleplus/gplus-quickstart-python/blob/master/signin.py.
Both of the examples have:
credentials = oauth_flow.step2_exchange(code)
and
session['credentials'] = credentials
storing the credentials object to the Flask session. When I run this code on my Google App Engine project, I get the error:
TypeError: <oauth2client.client.OAuth2Credentials object at 0x7f6c3c953610> is not JSON serializable
As discussed in this issue (marked WontFix), the OAuth2Credentials is not designed to by JSON serializable. It has methods to_json and from_json, which could be used to store it, e.g:
session['credentials'] = credentials.to_json()
However, in the same issue:
Never store a Credentials object in a cookie, it contains the applications
client id and client secret.
Perhaps I misunderstand how a Flask session object works, but from the doc:
... A session basically makes it possible to remember information from one request to another. The way Flask does this is by using a signed cookie. So the user can look at the session contents, but not modify it unless they know the secret key...
And therefore, we should not be storing a credentials object in the session, even if it is a signed cookie.
In my case, I currently only need to re-use the access token for disconnect purposes, so I can just store that.
What is the correct way to deal with this situation? Should the credentials not be stored in the session at all? Should at this point in the examples there be a comment "Securely save credentials here"?
Flask used to use pickle instead of JSON to store values in the session, and the Google example code was written with that in mind. Flask switched to a JSON-based format to reduce the impact of the server-side secret being disclosed (a hacker can hijack your process with pickle, not with JSON).
Store just the access token in your session:
session['credentials'] = credentials.access_token
You can recreate the credentials object with that token, using the AccessTokenCredentials class at a later time, should you need it again:
credentials = AccessTokenCredentials(session['credentials'], 'user-agent-value')
The AccessTokenCredentials object stores just the credentials; because it lacks the client id and client secret it cannot be used to refresh the token, however.
The user agent value is something you get to make up; it can help diagnose problems if you have access to the OAuth server logs; with Google I would not count on that so just make something up here.
"Flask by default uses the Werkzeug provided 'secure cookie' as session system. It works by pickling the session data, compressing it and base64 encoding it." - http://flask.pocoo.org/snippets/51/
In other words, flask is really weird. Anything you put in the session, it gets ciphered with the server key, sent to the client and stored in the client. The server then receives it on each subsequent request and decodes it with the same key. It also means session data will survive server reboots because it's sitting in the client.
To improve this for my app I've used the flask SessionInterface with Couchdb - and now the client only knows a sessionID that is checked against my database where the actual data is stored. Hurray.
Check this out, it has a few approaches to server side sessions depending what db you may be using - http://flask.pocoo.org/snippets/category/sessions/

GAE: cookies vs datastore

While looking for a good and efficient way to have a session in my app, I found GAE Boilerplate and GAE Sessions.
GAEB is amazing but very vast for my needs: I don't need Federate Login nor a default User structure, but I like the design structure and the way they solved some issues (routes, forms,...).
GAES is quite simple but powerfull to treat sessions. The most I like is the way it stores everything in a cookie, in this case, it stores the full user entity in a cookie so in the next page calls, no other datastore hits are done: the user data is always read from the cookie (this need, obviusly to update the data if the user updates something, which is not usual).
In the other hand, GAEB stores only the user ID and then retrieves, on each page call, the username and the user email. This is part of the code for the BaseHandler it uses (GAEB uses NDB model):
#webapp2.cached_property
def username(self):
if self.user:
try:
user_info = models.User.get_by_id(long(self.user_id))
if not user_info.activated:
self.auth.unset_session()
self.redirect_to('home')
else:
return str(user_info.username)
except AttributeError, e:
# avoid AttributeError when the session was delete from the server
logging.error(e)
self.auth.unset_session()
self.redirect_to('home')
return None
Same for email, and in the render_template function it does this:
def render_template(self, filename, **kwargs):
.... some code.....
# set or overwrite special vars for jinja templates
kwargs.update({
'app_name': self.app.config.get('app_name'),
'user_id': self.user_id,
'username': self.username,
'email': self.email,
... more vars ...
})
kwargs.update(self.auth_config)
It seems that reads 2 times (one for username and one for email) from the datastore, because this funcs makes models.User.get_by_**field**(long(self.user_id))
The only thing I don't know exactly what means is the #webapp2.cached_property, that maybe means that all this datastore reads are done from a cache and really don't hit the datastore.
Can someone tell me what is the better solution to save hits to the database? It seems that it is better to have all the user data in a cookie (obviously, secured) and don't hit the datastore on every page call, but maybe I'm mistaken (I'm relatively noob with GAE) and all this reads to datastore are cached and, then, for free.
Saving session data in the cookie is highly discouraged:
It has to be transfered with each request (slow on mobile connections)
The HTTP Header size you can transfer to the GAE is limited (64Kb if i remember correctly) - thats the upper bound of data you could store
Even if you encrypt and sign your session, you would still be vulnerable to reply attacks (you cannot perform a logout safely)
I don't know the implementations you mentioned, but we have an session implementation in our CMS, see https://bitbucket.org/viur/server/src/b2e9e3dca3adabee97e1761d630600a387a02c44/session.py?at=master .
The general idea is to generate a random string (used as a session identifier).
On session load a datastore "get by key" is performed (which is cached, so if that object is still in memcache, it wont hit the datastore at all).
And saving is only performed if the data stored inside the session changed or the session has not been
updated for the last 5 minutes.
Then you can copy the values of your user object into the session and wont have an additional datastore request.

Invalidate an old session in Flask

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()

Somewhat confused about how auth and sessions work in Python/Django

I'm using an existing database for my latest Django project, so unless I change my Models or the Django auth code, it's going to be rather difficult to merge the two.
Rather than messing with the existing auth backend, I'm planning on just writing my own authentication app.
Anyway, all of my previous authentication apps have been written in PHP, where basically i just throw everything in session variables and verify them on every page... Here's what I'm a bit confused about. It appears that when a user is authenticated/logged in, the entire user is added to a session, but I can't figure out where or how that is occurring.
In the default Django login function, it's assigning user to request.user ... is this being saved as a session variable somehow or is it just passed into the next view? If it is just being passed to the next view, how are future requests authenticated without requiring further login requests?
The default Django auth login is below..
def login(request, user):
"""
Persist a user id and a backend in the request. This way a user doesn't
have to reauthenticate on every request.
"""
if user is None:
user = request.user
# TODO: It would be nice to support different login methods, like signed cookies.
if SESSION_KEY in request.session:
if request.session[SESSION_KEY] != user.id:
# To avoid reusing another user's session, create a new, empty
# session if the existing session corresponds to a different
# authenticated user.
request.session.flush()
else:
request.session.cycle_key()
request.session[SESSION_KEY] = user.id
request.session[BACKEND_SESSION_KEY] = user.backend
if hasattr(request, 'user'):
request.user = user
user_logged_in.send(sender=user.__class__, request=request, user=user)
I also tried to follow the user_logged_in.send(), which is in django.dispatch.dispatcher.send but I'm not entirely sure what that's supposed to do either.
def send(self, sender, **named):
"""
Send signal from sender to all connected receivers.
If any receiver raises an error, the error propagates back through send,
terminating the dispatch loop, so it is quite possible to not have all
receivers called if a raises an error.
Arguments:
sender
The sender of the signal Either a specific object or None.
named
Named arguments which will be passed to receivers.
Returns a list of tuple pairs [(receiver, response), ... ].
"""
responses = []
if not self.receivers:
return responses
for receiver in self._live_receivers(_make_id(sender)):
response = receiver(signal=self, sender=sender, **named)
responses.append((receiver, response))
return responses
Basically what I'm looking for is for someone to explain an efficient way to save user session data in Python that does not depend on the Django framework. A little run-through of the Django authentication would be nice as well.
HTTP is stateless; regardless of the server used, the framework or language, there is no intrinsic way for an HTTP client to say "this request is part of that session". That's part of the design of HTTP.
So sessions are always a feature of the web application; either supported by the a web app framework or implemented in the app itself. The most usual way for a stateful session to be created from the stateless protocol is with cookies; Clients will store cookies at the request of a server and return those same cookies to that server in future requests.
Session data can be serialized and stored in the cookie itself, but that's both insecure (secret information could be forged or eavesdropped), and inefficient (takes bandwidth even though the individual bytes are of no use to the client), and so the preferred solution is to use an opaque (and even better, single use) session key is stored in a cookie, and the web application will store session data out of band; either in memory, in the filesystem, or a database backend, or some other option.
django takes care of most of this transparently in "middleware", modules that modify incoming requests and outgoing responses. The auth middleware will read a cookie and check if that represents a logged in user, and if so, add a user object to the request; it also attaches cookies to responses when a user gets logged in. the session middlware works in a similar fashion, checking for a cookie, and reading the session data from wherever it was stored between requests, and also grabbing session data from responses and storing them, along with setting a cookie to associate the client's session with the session data it just stored.
Since both of these features are useful, independent of each other (I tend to avoid sessions, but usually use some kind of authentication), they do not depend on each other. Session-like authentication is implemented in a similar manner as sessions, but authenticated users are not stored in "The Session", nor are sessions attached to "The authenticated User".
You might not think so, but django's authentication system is designed to be extended; if you already have a database of valid users you'd like to authenticate against, it's very simple to add a new auth backend that dovetails neatly into the standard django auth application (which means you can also use other applications that depend on it in turn).

Categories

Resources