flask extension for defending against cross-site request forgery attacks (xsrf/csrf), by protecting flask request endpoints with uniquely generated tokens for each request.
FLASK | PYTHON | XSRF |
---|---|---|
BUILD STATUS
REFERENCE / LINKS
- pypi: package
- readthedocs: docs
- github: wiki
- github: source
- github: releases
- changelog notes
- travis-ci: build-status
- coveralls: coverage-status
- contributing notes
- github: issues
- flask route handlers are decorated to generate, send a uniquely hashed token
- the hashed values are stored on the server, using flask sessions
- the token is sent in the response header
X-XSRF-Token
- on subsequent client requests..
- the client will be expected to send the token back to the server
- either through form data, or through the header
X-XSRF-Token
- either through form data, or through the header
- to the server receive, validate uniquely hashed tokens
- the client will be expected to send the token back to the server
FEATURES
- flexible - decide which style of implementation suits you best.
- capture, validate
XSRF-Tokens
through headers, cookies, form-fields; the style is an easily configurable choice.
- capture, validate
- timeout - optionally, you can specify a default time window for valid tokens
- tested - used internally @ google.
REQUIREMENTS
python | flask |
---|---|
2.7.6+ |
0.11.0+ |
INSTALLATION
install with pip (more often it is recommended to lock / specify a specific version):
pip install --disable-pip-version-check flask-xsrf
pip install --disable-pip-version-check flask-xsrf==1.0.3
IMPLEMENTATION
implementation of the library with your flask app breaks down into four steps.
1: add a secret_key
to your flask app config object:
from flask import Flask
flask_app = Flask(__name__)
flask_app.secret_key = '<:session_secret_key>'
flask_app.config['session_cookie_secure'] = True
flask_app.config['remember_cookie_name'] = 'testdomain.com'
flask_app.config['remember_cookie_duration_in_days'] = 1
2: create an instance of an XSRFTokenHandler
object, and specify a method/callable
which will be used as a getter by the token handler to get a user_id
.
optionally, you can assign auto-generated id's for anonymous requests.
lastly, you may specify a default timeout
, in number of seconds, to expire
tokens after a specific the amount of time:
from flask import Response
from flask import session
import flask_xsrf as xsrf
@flask_app.before_request
def before_request():
if 'user_id' not in session:
session['user_id'] = 'random_generated_anonymous_id'
def get_user_id():
return session.get('user_id')
xsrf_handler = xsrf.XSRFTokenHandler(
user_fn=get_user_id, secret='xsrf_secret', timeout=3600)
NOTE: currently, usage of the session
is required (see TODO notes below).
3: decorate GET
request-handlers to send a generated token:
@flask_app.route('/test', methods=['GET'])
@xsrf_handler.send_token()
def test_get():
return Response('success')
4: decorate POST
request-handlers to receive, validate sent tokens:
@flask_app.route('/test', methods=['POST'])
@xsrf_handler.handle_token()
def test_post():
return Response('success')
that's all there is to it. please feel free to contact me gn@gregorynicholas.com or to submit an issue on github for any questions or help. however, creating a fork and submitting pull-requests are much preferred. contributions will be very much appreciated.
STAR, FORK THIS PROJECT
forks |
stars |
---|---|
LOCAL ENVIRONMENT SETUP (OSX)
here's a list summary of the python environment setup:
- setup
pyenv
,pyenv-python-2.7.11
- setup
virtualenv
,virtualenvwrapper
- create project
virtualenv
- with a command such as
$ mkvirtualenv flask-xsrf-dev
- create project
- install pip dependencies
- install local development build
$ python setup.py --verbose develop
INSTALL PYENV
pyenv (aka NVM or RVM for python) allows installing python at specific versions, and helps to manage multiple versions on osx. install is easy, and can be tricky to work with, unless you remember to set the proper environment shell variables:
init-pyenv(){
export PYENV_ROOT="$HOME/.pyenv"
export PYENV_PY_VERSION="2.7.11"
export PYENV_VERSION_BIN_DIR="$PYENV_ROOT/versions/$PYENV_PY_VERSION/bin"
export PYENV_BUILD_ROOT="$PYENV_ROOT/sources"
export PYENV_SHIMS_DIR="$PYENV_ROOT/shims"
export PYENV_SHELL="bash"
export PYENV_DEBUG=0
export PATH="$PYENV_ROOT/bin:$PYENV_VERSION_BIN_DIR:$PATH"
}
init-virtualenv(){
export VIRTUALENVWRAPPER_SCRIPT="$PYENV_VERSION_BIN_DIR/virtualenvwrapper.sh"
export WORKON_HOME="$HOME/.virtualenvs"
export PIP_VIRTUALENV_BASE=$
. "$VIRTUALENVWRAPPER_SCRIPT"
}
now we're ready to install pyenv/python:
init-pyenv
git clone https://github.com/yyuu/pyenv.git "$PYENV_ROOT"
eval "$(pyenv init -)"
pyenv install 2.7.11 && say "pyenv install of python 2.7.11 complete"
pyenv rehash
pyenv global 2.7.11
next, configure virtualenv:
init-virtualenv
pyenv exec pip install --disable-pip-version-check --upgrade pip
pyenv exec pip install --disable-pip-version-check virtualenv && pyenv rehash
pyenv exec pip install --disable-pip-version-check virtualenvwrapper && pyenv rehash
next, create the project virtualenv, install dependencies:
mkvirtualenv flask-xsrf-dev && pyenv rehash
workon flask-xsrf-dev && pyenv rehash
pyenv exec pip install --disable-pip-version-check -r .serpent/runtime-config/pip/requirements.txt && pyenv rehash
DEVELOPMENT FLOW
- TODO: generate asciicinema movie clip of setup steps..?
- activate the environment
$ init-pyenv
$ init-virtualenv
$ eval "$(pyenv init -)"
$ workon flask-xsrf-dev
- run tests
$ python setup.py nosetests
- view coverage report
$ coverage report --show-missing
viola. that's pretty much all there is to the flow (for now..).
- add feature: enable checking of referer headers / client ip-address
- remove hard-coded dependency / usage of
session
. - add feature: enable storage of tokens in cookie.
- this might help ease implementation, as the client would not have to manually manage passing of tokens to server.
the derived work is distributed under the Apache License Version 2.0.