Is it safe to store User ID within the Flask session? - python

I have found a number of articles online explaining that the Flask session is secured and not encrypted.
This means that users can Base64 decode the cookie and read the values inside.
Is it an issue if my application uses the User ID value from this session cookie to authenticate the user after he successfully logs in?
If I understand correctly, the user will not be able to change his cookie value to an ID of another user, as it signed by the application private key on the server side. Is that correct?
And if so, are there any issues with using the secured contents of the Flask cookie-based session to allow users access to protected views of the application?

To summarise answers given in the comments:
Cookie can be tampered with but if Flask session security is enabled, such tampered session will be thrown away, forcing the client to re-login
The session data Base64-encoded within the cookie can be quite easily viewed. Therefore anything that your clients are not supposed to see should not be included there

Related

Authentication performed via Python script using an API access key

I create a client (plain python script) and server (flask app) in Python. I wonder how to perform simple authentication using a access key in Python. Authentication is for scripts - they are not real users (no registration required). The key will be assigned to the script during implementation.
Scenario 1:
Client sends message to server.
Server (flask app) reads token from Authorization header.
Server looks for the token in the database.
If server finds token in database it will authenticate the client.
The access token is explicitly passed to the client before sending messages and the client stores it in an environment variable.
Disadventages:
Tokens are not stored as hashes
Scenario 2:
Client sends message to server.
Server (flask app) reads token (jwt) from Authorization header.
Server decodes jwt token to: {'accessKeyId': 'fake_id', 'accessKey': 'fake_key'}
Server looks for the key in database by id.
If server finds row in db and key matches to decoded server will authenticate the client.
Adventages:
Keys are stored as hashes
The access token is explicitly passed to the client too. It requires:
create {'accessKeyId': 'fake_id', 'accessKey': 'fake_key'} object,
hash 'fake_key' (and save to server database),
generate jwt token (finally it will passed for client and it will be in environment variable).
Is the 2nd approach correct? How is this implemented in applications where the user gets an access key after registration in web app and can query the api (via REST) if the key is correct - sth like OpenWeatherMaps?
Edit:
I found out that I need to implement the KEY API mechanism. Are there any guides on how to do this in Flask/Python?
This decision is very situation dependent. I've read over your question and personally, I'd go with scenario 1. Unless of course, you don't want the script to know its own key? But that defeats the purpose in some ways because you'd need to refresh that key since JWTs have an expiry value and common practice is to have them short-lived. It seems like scenario 2 just adds unnecessary complexity.
You're still validating the token against the database in both scenarios. Unless you're encrypting the JWT then you'll still be enabling users of that JWT to view that same token. If you want to encrypt it, then go for it...but you've just created an encrypted token-like header which just adds more overhead because instead of just going token to database you're going to JWT -> decrypt -> Token -> database on every request and you'll still need some sort of access token for them to regenerate that JWT.
If you have special claims to give them and want to communicate that via JWT then that's valid, but you'll still need them to be short-lived and they have to refresh them.
tl;dr I'd go with scenario 1, it just seems based on the example to be the better solution in my opinion.
Not sure how much you've looked into JWT's but this may be a good place to start.

If Pyramid sessions are one-way hashed and not stored server-side, where is the data coming from?

When using the SignedCookieSessionFactory, the documentation states that the sha512 HMAC digest algorithm is used. As a result of this, once session data is serialised, it is signed and sent to the user's client under the session cookie.
There is no mention in Pyramid's documentation that sessions are also cached server-side (under this SessionFactory).
This presents a contradiction and has also led to authentication confusion when paired with SessionAuthenticationPolicy. If session data cannot be retrieved from the client's session cookie (as it is one-way hashed), how is it that the following is possible?
Authenticate with the application such that reading Request.authenticated_userid does not return None.
Copy the session cookie to the clipboard.
Logout by accessing a view whereby the headers from forget(request) are returned in the Response.
Replace the session cookie with the copied value.
The user is now logged back in.
I understand that simply deleting the cookie client-side is insufficient to completely invalidate the session (without blacklisting), but this then poses the following questions:
How is it possible to remain secure with browser-scoped sessions unless every session cookie the user has recently been issued is somehow remembered and blacklisted/invalidated?
Is the session cookie actually a key/session ID that's being used to lookup a session in memory? As it's one-way signed, surely this is the only explanation?
Is it possible then to invalidate sessions server-side, a little more robust than just the headers=forget(request) pattern?
Ultimately, I imagine the answer is something along the lines of:
"Maintain server-side sessions using an include such as pyramid_redis_sessions"
Any explanation would be appreciated.
The cookie contains all of the data. The session content itself is stored in the cookie, alongside an hmac signature of that content. This means that a client may view the content if they try hard enough, but they cannot change it as they cannot create trusted signatures without the server-side secret.
How is it possible to remain secure with browser-scoped sessions unless every session cookie the user has recently been issued is somehow remembered and blacklisted/invalidated?
It depends what you're using the session for - you need to take these issues into consideration before putting data into a session object.
Is it possible then to invalidate sessions server-side, a little more robust than just the headers=forget(request) pattern?
Not unless you store some sort of id in the session and then a server-side table of blacklisted ids. If you did this then you could write your own wrapper session-factory that opened the session, checked the id, and returned an empty one if it was blacklisted. Of course then you might just want to use a server-side session library like pyramid_redis_sessions or pyramid_beaker with one of its server-side-storage backends.

Strange session behaviour with a Flask app on Heroku

I have a web application that uses GitHub's OAuth API in order to allow the app's users to interact with GitHub. However, I'm seeing some very odd behaviour with regards to the session cookie.
As a bit of background, I am using peewee to interface with Heroku's Postgres server, and have a User model like so:
class User(peewee.Model):
login = peewee.TextField(unique=False)
token = peewee.TextField()
I am using the web application flow described in the GitHub OAuth documentation, and am successfully getting called back with an access token, which I store in the database, and also in the session [1]:
#app.route('/callback')
def finishlogin():
# I've verified that `token` and `login` are both valid at this point
user = User.create(login=login, token=token)
session['token'] = token
return redirect(url_for('home'))
My route for home is as follows:
#app.route('/')
def home():
if 'token' in session:
user = User.get(token=session.get('token'))
return 'Your login is {}'.format(user.login)
else:
# ...
So far, so good, and this works correctly. However, I am experiencing instances of users logging in, refreshing the page and finding that they are suddenly logged in as someone else. Logging the requests to the app shows that on the second request the session cookie itself has sent the wrong value (i.e. session.get('token') in home() returns a valid, but incorrect value. Clearly the user's browser can't know any other session value, so it seems that there is some "leakage" in setting the session between different clients and requests.
I'm not sure what the problem might be. My database is stored on the Flask g object as described in the peewee docs and has before_request and teardown_request hooks set up to open and close the database connection, and from all the documentation and example code I have read (and I've read a lot!), I seem to be using the session object correctly. I have set up a working secret_key for the session store.
I'm wondering if this could be something going on with Heroku and their routing mesh? But then, how would one user suddenly send another user's session?
Any hints or advice would be appreciated—I've been staring at this for a long time and am at my wits' end.
[1] I'm aware that storing the token directly is a bad design choice. The application is non-public and this will be fixed, but for now I want to describe the problem as it exists, even though it's not ideal.
Answering my own question for future reference.
It seems that this was being caused by Flask's default session cookie behaviour, which is to send a Set-Cookie header with every single request, even for static assets. Our local Squid proxy was therefore gladly caching those requests and re-issuing Set-Cookie headers for every user.
Setting Cache-Control headers for the whole app seems to fix the issue.

GAE redirect the user to oauth2 without loosing the information from the initial request?

I'm using GAE with python. I'm working on an application that opens specific files from drive.
When you try to open a file from drive with your application, you are redirected to a url like this one :
http://my-app.appspot.com/state=%7B%22ids%22:%5B%220B1AXKdjZqM9FZDNIZEhMZEh0YzA%22%5D,%22action%22:%22open%22,%22userId%22:%22102709420614967238115%22%7D
In my program, I need to check if the application is authorized by the user; and to do so, I need it to be redirected to the oauth2 util it's authorized and then come back to the previous url.. or at least I need to efficiently save the information:
state=%7B%22ids%22:%5B%220B1AXKdjZqM9FZDNIZEhMZEh0YzA%22%5D,%22action%22:%22open%22,%22userId%22:%22102709420614967238115%22%7D
How can I redirect the user without loosing the information from the initial request ?
You can use state parameter
state
Any string Provides any state that might be useful to your application
upon receipt of the response. The Google Authorization Server
roundtrips this parameter, so your application receives the same value
it sent. Possible uses include redirecting the user to the correct
resource in your site, nonces, and cross-site-request-forgery
mitigations.

Securing RESTapi in flask

The app I'm deving uses a lot of ajax calls. Unfortunately I hit a snag when researching on how to restrict access to the api. For example:
i have table that does an ajax call to http://site/api/tasks/bob
i need to make sure that only bob, logged in, can read that table
(otherwise somebody who knows the pattern might request to see bob's
tasks by simply entering the url in the browser).
on a different page,the same table needs to be able to call http://site/api/tasks/all and show the tasks of all users (only an admin should be able to do that)
Thank you for your time reading this and maybe answering it.
The thousand-foot view is you need to authenticate the user either with:
A) HTTP-Auth (either basic or digest) on each request.
B) Server-side sessions. (The user authenticates and receives a session key - their user information is stored in the session backend on the server, attached to that key Once they have a session they can make requests passing their session key back to you (either in the URL or in a cookie) and the information they have access to is returned to them.)
Flask has a pair of useful extensions that deal with a large part of this sort of thing - check out Flask-Login and Flask-Principal to see examples of how authorization can be added to a Flask application.

Categories