Skip to content

#43: Add Security Headers to Your Flask Application

Sooner or later our Flask application will be on the internet. The earlier we address security issues the better it is. Today we look at a first simple measure to add security headers.

Attention: The examples below are for version 0.2.x. In 0.3.x this package had breaking changes and the examples no longer work. Please check the docs for the current syntax.

Secure.py

Secure.py is a small package that adds optional security headers and cookie attributes to your Python web application. Flask is only one of currently 14 supported web frameworks, what makes it very unlikely that your framework of choice is not supported.

You can install secure.py into your virtual environment using this command:

pip install secure

Activate secure.py in your Flask application

In our app.py (where our Flask application is) we need to add these imports and create a method that runs after every request:

1
2
3
4
5
6
7
from secure import SecureHeaders
secure_headers = SecureHeaders()

@app.after_request
def set_secure_headers(response):
    secure_headers.flask(response)
    return response

That is all we need to do to activate security.py.

If you need to change certain headers, you can do that in the SecureHeaders() constructor:

secure_headers = SecureHeaders(csp=True, hsts=False, xfo="DENY")

What changed?

When I run HTTPie before installing secure.py, I got this output:

PS D:\Temp> http http://127.0.0.1:5000/

HTTP/1.0 200 OK 
Content-Length: 13 
Content-Type: text/html; charset=utf-8 
Date: Mon, 28 Sep 2020 16:04:56 GMT 
Server: Werkzeug/1.0.1 Python/3.8.1

Hello world!

After installing secure.py the security headers protect my application against a wide range of possible attacks:

PS D:\Temp> http http://127.0.0.1:5000/ 

HTTP/1.0 200 OK 
Cache-control: no-cache, no-store, must-revalidate, max-age=0 
Content-Length: 13 Content-Type: text/html; charset=utf-8 
Date: Mon, 28 Sep 2020 16:06:13 GMT 
Expires: 0 
Pragma: no-cache 
Referrer-Policy: no-referrer, strict-origin-when-cross-origin 
Server: Werkzeug/1.0.1 Python/3.8.1 
Strict-Transport-Security: max-age=63072000; includeSubdomains 
X-Content-Type-Options: nosniff 
X-Frame-Options: SAMEORIGIN 
X-XSS-Protection: 1; mode=block

Hello world!

I could write all those headers on my own but adding secure.py is a lot simpler and prevents me from silly mistakes.

Secure your cookies

Another common security problem is the missing secure flag on cookies. This flag forces the browser to only send this cookie over HTTPS. This snipped from the documentation contains all the parts you need to write a secure cookie:

from flask import Flask, Response
from secure import SecureCookie

secure_cookie = SecureCookie()

...

@app.route("/secure")
def set_secure_cookie():
    resp = Response("Secure")
    secure_cookie.flask(resp, name="spam", value="eggs")
    return resp

Next

The security headers are a good start and thanks to secure.py, it only took us a few lines of code to activate them. With this basic part out of the way, we can focus on other security issues, like how we manage credentials or prevent attackers from posting data on our behalf. But before I add more (security related) features, I want to make sure my application keeps working as expected.