Securing RESTapi in flask - python

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.

Related

How does an API callback work and how can I implement one?

I am a bit confused as to how these callbacks work.
I have seen some REST APIs ask for callback URLs mainly for authentication(Oauth) and if I understand correctly, the API calls an endpoint implemented at our backend once they're done. But if this is a website and the API calls a backend how will it call the particular session that an user triggered that API call from?
Do we generate some unique ID and use that to identify each session?
Also is it possible to implement something like this for uses other than authentication?
Excuse me if this seems trivial but I've been trying to figure it out for hours now.
Thanks in advance! :)
P.S: I'm trying it out in python using FastAPI.
Yes, you usually include a key that identifies the "transaction". Either one that is pre-shared (for webhooks where an API makes callbacks at specific times when an event happens and the request isn't a response to something initiated by you), or one that you generate to identify the session itself after redirecting a user outside of your own application.
remote server makes a request to /webhooks?key=foo
.. or what usually happens, includes a signature as an HTTP header that signs the message based on an already exchanged key.
See for example Stripe's webhook documentation for an example of how the request is signed.
However, in the case where the user is redirected back to your application (which is the flow for many authentication schemes), the redirect back is given to the user's client - and their session will still be active. In that case the redirect will include a session identifier from the authentication system, which you can then use in your backend to verify that the user actually was authenticated and with what autorization.
/foo -> redirect -> https://remote/auth
(enters authentication information)
https://remote/auth -> redirect -> /authenticated?secret_key=foobar

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.

How to properly and securely handle cookies and sessions in Python's Flask?

In application I am writing at the moment I've been saving in users browser a cookie that had session ID stored inside, and that ID was used as a reference to a session stored in the database containing user's information including the fact if the user is logged in properly.
I wanted to review the security of my solution and I stared to look into how I should be setting up cookies upon login, what to store in server side stored session and how to destroy that information on logout since as of now my users were staying logged in for ages, which was not my intention.
The problem I have is no definite answer on how to handle the whole user login/session/logout issue properly in Flask - some people are talking about using Flask's Response.delete_cookie() function, others to expire it using .set_cookie() with zero expiration time, others are mentioning Flask's session module, other itsdangerous module...
What is the most secure, right and proper way of handling that in terms of modules that should be used with Flask, code examples and so on?
The background
Method #1
An easy and safe way to handle sessions is to do the following:
Use a session cookie that contains a session ID (a random number).
Sign that session cookie using a secret key (to prevent tempering — this is what itsdangerous does).
Store actual session data in a database on the server side (index by ID, or use a NoSQL key / value store).
When a user accesses your page, you read the data from the database.
When a user logs out, you delete the data from the database.
Note that there are a few drawbacks.
You need to maintain that database backend (more maintenance)
You need to hit the database for every request (less performance)
Method #2
Another option is to store all the data in the cookie, and sign (and optionally encrypt) said cookie. This method, however, has numerous shortcomings too:
This is easier on the backend (less maintenance, better performance).
You need to be careful to not include data your users should not see in sessions (unless you're encrypting).
The volume of data you can save in a cookie is limited.
You can't invalidate an individual session (!).
The code
Flask actually implements signed session cookies already, so it implements method #2.
To get from #2 to #1, all you have to do is:
Generate random Session IDs (you could use os.urandom + base64).
Save session data in a database backend, indexed by Session ID (serialize it using e.g. JSON, Picke if you need Python objects, but avoid if you can).
Delete sessions from your database backend when a user logs out.
Make sure you're protected against session fixation attacks. To do so, make sure you generate a new session ID when a user logs in, and do not reuse their existing session ID.
Also, make sure you implement expiration on your sessions (just a matter of adding a "last-seen" timestamp).
You could most likely get some inspiration from Django's implementation.
I would recommend you go with the Flask KVSession plugin with the simplekv module to persist the session information.
Conceptually, Flask KVSession provides an implementation for Method #1 described above using the Flask session interface. That way you don't have to alter your code to get it running, and you can use the extension methods to do additional things such as session expiration. It also takes care of the session signing, and does some basic checks to prevent tampering. You will still want to do this over HTTPS to absolutely prevent session stealing however.
Simplekv is the actual module that handles the writing and reading to various data storage formats. This can be as simple as a flat file, as fast as Redis, or as persistent as a database (NoSQL or otherwise). The reason this is a separate module is so Flask KVSession can just be a plain adapter to Flask without having to know about the storage mechanism.
You can find code samples at http://flask-kvsession.readthedocs.org/en/latest/. If you need more examples, I can provide one.
Alternatively, if you need a more enterprise and heavyweight server sided implementation for Flask, you can also look at this recipe using Beaker which works as WSGI middleware (meaning other frameworks also use it). http://flask.pocoo.org/snippets/61/. The Beaker API is at http://beaker.readthedocs.org/en/latest/.
One advantage Beaker provides over Flask KVSession is that Beaker will lazy load sessions, so if you don't read the session information, it won't make a connection to the database on every call. However, Beaker depends on SQLAlchemy which is going to be a larger module than simplekv module.
Unless that specific performance case is important, I would still go with Flask KVSession because of its slightly simpler API and smaller code base.

How do I save values to an anonymous cookie based session in Pyramid using Beaker

I have a Pyramid application using Beaker Encrypted cookie sessions. I can log a user in using a RequestWithUserAttribute, pyramid.security.authenticated_userid(), remember() and forget() just fine.
However, the majority of users will never log in, and there is a specific value I'd like to save in a cookie (encrypted if at all possible) that the user has given then site (their location, or any string for that matter).
I cannot discover how to set more than the principal for a session using the remember() function, and I'd prefer not to send my own Set-Cookie headers, let alone deal with the encryption of the data myself.
I have found that you can pass keyword arguments to remember():
remember(request, principal, *kw)
But when I try to send any extra values I continuously run into an error.
Ideally I would expect something like this:
remember(request, 'public', {'location':request.params.get('location')})
Is this even the correct route?
Sessions and Authentication in Pyramid (and in general) are disjoint concepts. There are a lot of people who learn the way to store the authenticated user "is in a session", but in no way is this a requirement. The point of a session is to store arbitrary data for a visitor to your site across requests. That could be the fact that they are logged in or it could be your random strings.
The point is you can store random stuff in the session. It is available in Pyramid (after you've setup the session_factory on the Configurator) directly on the request object via request.session.
request.session['mykey'] = 'some random value'
This does not require you to use authentication, remember/forget, or anything other than a session factory.
https://docs.pylonsproject.org/projects/pyramid/en/1.2-branch/narr/sessions.html

Categories