Add a Content Security Policy header to your Flask application. More information on CSP:
Install the extension with using pip, or easy_install. Pypi Link
$ pip install flask-csp
Add the csp_header(...) decorator after the app.route(...) decorator to create a csp header on each route. The decorator can either be passed no value (Add default policies) or custom values by a dict (Add custom policies). For more information on the default policies see "Change Default Policies" below.
from flask_csp.csp import csp_header
...
@app.route('/')
@csp_header()
Pass the csp_header wrapper a dict with the policies to change:
from flask_csp.csp import csp_header
...
@app.route('/')
@csp_header({'default-src':"'none'",'script-src':"'self'"})
Notes:
- Only policies with a non empty value are added to the header. The wrapper @csp_header({'default-src':""}) will remove 'default-src ...' from the header
- 4 keywords in policies must always be encapsulated in single quotes: 'none', 'self', 'unsafe-inline','unsafe-eval'
- The data permission is spelled with a colon
- ex: @csp_header({'default-src':"'none'",'script-src':"'self'", 'font-src': "data: 'self'"})
To set the header to "Report only" pass the key/value pair 'report-only':True to the custom header dict:
from flask_csp.csp import csp_header
...
@app.route('/')
@csp_header({'report-only':True})
The default policies are as follows:
{
"default-src": "'self'",
"script-src": "",
"img-src": "",
"object-src": "",
"plugin-src": "",
"style-src": "",
"media-src": "",
"child-src": "",
"connect-src": "",
"base-uri": "",
"report-uri": "/csp_report"
}
Edit default policies via command line:
>>> from flask_csp.csp import csp_default
>>> h = csp_default()
>>> h.update({'child-src':"'self'"})
Edit default policies on flask app:
from flask_csp.csp import csp_header, csp_default
h = csp_default()
h.update({'script-src':"'self' code.jquery.com"})
To view the default policies:
>>> from flask_csp.csp import csp_default
>>> h = csp_default()
>>> h.read()
Note:
- Python interpreter must be reloaded for changes to the default policies to take place
Based on the default settings, reports will be sent to the route 'csp_report' through a POST request. This is totally customizable but here is a very simplistic example of handling these reports:
@app.route('/csp_report',methods=['POST'])
def csp_report():
with open('/var/log/csp/csp_reports', "a") as fh:
fh.write(request.data.decode()+"\n")
return 'done'