.. flask-paranoid documentation master file, created by sphinx-quickstart on Sat Jul 1 17:13:54 2017. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. .. image:: _static/logo.png :alt: flask-paranoid :align: center Flask-Paranoid is a simple extension for the Flask microframework that protects the application against certain attacks in which the user session cookie is stolen and then used by the attacker. How Does It Work? ================= When a client connects to the application for the first time, a token that represents certain characteristics of this client is generated and stored. In succesive requests sent by this client, this token is regenerated and compared against the stored one. If the tokens are different, it is assumed that the client is sending requests from a different environment than the one in which the session was originally created, so in this case the session is destroyed and the request rejected as a preventive measure. By default, the token is generated from the IP address and the user agent of the client. This means that if a user session cookie is stolen and then used from a different location or from a different browser, the generated token will change, and that will block this "different" request. The idea is based on the strong session protection feature of Flask-Login, but generalized so that it can also be used outside of the Flask-Login context. The token generation and storage can be customized to fit different types of applications. Quick Start =========== Here is a simple application that uses Flask-Paranoid to protect the user session:: from flask import Flask from flask_paranoid import Paranoid app = Flask(__name__) app.config['SECRET_KEY'] = 'top-secret!' paranoid = Paranoid(app) paranoid.redirect_view = '/' @app.route('/') def index(): return render_template('index.html') In this example, the paranoid token computed on the initial request from a client will be stored in the user session. If a subsequent request generates a different token, the session will be cleared and then a redirect to the root URL will be made, forcing the potential attacker to log in again. Configuration ============= The only configuration is what determines the action that is taken when an invalid session is detected, after the session is cleared. The default is to return a 401 error back to the client. To redirect to a given URL, set ``paranoid.redirect_view`` to the desired location:: paranoid.redirect_view = '/login' Absolute URLs are also supported:: paranoid.redirect_view = 'https://www.google.com' To redirect to a registered Flask route, set ``paranoid.redirect_view`` to the desired endpoint name:: paranoid.redirect_view = 'index' To redirect to a route inside a blueprint, use the same syntax used by the ``url_for()`` function:: paranoid.redirect_view = 'auth.login' To have the extension invoke a callback function that generates the response, use the ``paranoid.on_invalid_session`` decorator. The function should return a valid Flask response:: @paranoid.on_invalid_session def invalid_session(): return 'please login', 401 Using with Flask-Login ====================== Since this extension overrides the similar feature in Flask-Login, it is recommended that when using both extensions in an application, session protection is disabled in Flask-Login:: login_manager.session_protection = None Flask-Paranoid detects if Flask-Login is being used, and in that case clears the "remember me" cookie in addition to the user session when an invalid session is detected. Customization ============= Flask-Paranoid allows applications to customize its inner workings through the use of subclasses. Changing the Token Generation Algorithm --------------------------------------- The default implementation creates a SHA256 hash of the client IP address and user agent. To use a different token generation algorithm, override the ``create_token`` method:: class MyParanoid(Paranoid): def create_token(self): pass This method is invoked in the context of a request, so it can access the ``request`` object to obtain information about the client. Changing Where the Token is Stored ---------------------------------- The default implementation writes the paranoid token to ``session['_paranoid_token']``. If a different storage mechanism is desired, override the ``write_token_to_session()`` and ``get_token_from_session()`` methods:: class MyParanoid(Paranoid): def write_token_to_session(self, token): pass def get_token_from_session(self): pass A tricky use case is when this extension is used with an API project that does not use the user session and instead provides authentication tokens to clients. In such a case, the application's token generation function can be enhanced to include the paranoid token. For example:: def get_token(username, password): if validate_user(username, password): return encode_jwt(claims={'username': username, 'paranoid_token': paranoid.create_token()}) And then the token storage methods can be overriden as follows:: class MyParanoid(Paranoid): def write_token_to_session(self, token): # nothing to do here, the paranoid token is inserted in the JWT # by the application pass def get_token_from_session(self): claims = decode_jwt(request.headers['X-API-TOKEN']) if 'paranoid_token' not in claims: abort(401) return claims['paranoid_token'] In this example, the ``encode_jwt`` and ``decode_jwt`` are application functions that work with JWT tokens. The client authenticates by passing the JWT token in the ``X-API-TOKEN`` header. Using a Different Session Cleanup Mechanism ------------------------------------------- By default, this extension empties the contents of the user session, and if Flask-Login is used, also deletes its "remember me" cookie. To change or replace the session cleanup algorithm, override the ``clear_session()`` method:: class MyParanoid(Paranoid): def clear_session(self, response): pass The ``response`` argument passed to this method is the response object that will be returned to the client after the session has been cleaned up. Any cookies that need to be deleted or modified must be included in this response object. .. toctree:: :maxdepth: 2 :caption: Contents: