secure.py¶
secure.py 🔒 is a lightweight package that adds optional security headers for Python web frameworks.
Supported Python web frameworks:¶
aiohttp, Bottle, CherryPy, Django, Falcon, FastAPI, Flask, hug, Masonite, Pyramid, Quart, Responder, Sanic, Starlette, Tornado
Install¶
pip:
$ pip install secure
Pipenv:
$ pipenv install secure
After installing secure.py:
import secure
secure_headers = secure.Secure()
Documentation¶
Secure Headers¶
Security Headers are HTTP response headers that, when set, can enhance the security of your web application by enabling browser security policies.
You can assess the security of your HTTP response headers at securityheaders.com
Recommendations used by secure,py and more information regarding security headers can be found at the OWASP Secure Headers Project .
Server¶
NULL
(obfuscate server information, not
included by default)Strict-Transport-Security (HSTS)¶
max-age=63072000; includeSubdomains
X-Frame-Options (XFO)¶
SAMEORIGIN
X-XSS-Protection¶
0
X-Content-Type-Options¶
nosniff
Content-Security-Policy (CSP)¶
script-src 'self'; object-src 'self'
(not
included by default)*Referrer-Policy¶
no-referrer
Cache-control¶
no-cache
Permissions-Policy¶
- Additional information:
- The
Strict-Transport-Security
(HSTS) header will tell the browser to only utilize secure HTTPS connections for the domain, and in the default configuration, including all subdomains. The HSTS header requires trusted certificates and users will unable to connect to the site if using self-signed or expired certificates. The browser will honor the HSTS header for the time directed in themax-age
attribute (default = 2 years), and setting themax-age
to0
will disable an already set HSTS header. Use thehsts=False
option to not include the HSTS header in Secure Headers. - The
Content-Security-Policy
(CSP) header can break functionality and can (and should) be carefully constructed, use thecsp=True
option to enable default values.
- The
Usage¶
secure_headers.framework(response)
Default HTTP response headers:
Strict-Transport-Security: max-age=63072000; includeSubdomains
X-Frame-Options: SAMEORIGIN
X-XSS-Protection: 0
X-Content-Type-Options: nosniff
Referrer-Policy: no-referrer, strict-origin-when-cross-origin
Cache-control: no-cache, no-store, must-revalidate, max-age=0
Pragma: no-cache
Expires: 0
Options¶
You can toggle the setting of headers with default values by passing
True
or False
and override default values by passing a string to
the following options:
server
- set the Server header, e.g.Server=“Secure”
(string / bool / SecurePolicies, default=False)hsts
- set the Strict-Transport-Security header (string / bool / SecurePolicies, default=True)xfo
- set the X-Frame-Options header (string / bool / SecurePolicies, default=True)xxp
- set the X-XSS-Protection header (string / bool / SecurePolicies, default=True)content
- set the X-Content-Type-Options header (string / bool / SecurePolicies, default=True)csp
- set the Content-Security-Policy (string / bool / SecurePolicies, default=False) *referrer
- set the Referrer-Policy header (string / bool / SecurePolicies, default=True)cache
- set the Cache-control and Pragma headers (string / bool / SecurePolicies, default=True)feature
- set the Feature-Policy header (SecurePolicies / string / bool / SecurePolicies, default=False)
Example:
from secure import SecureHeaders
secure_headers = SecureHeaders(csp=True, hsts=False, xfo="DENY")
. . .
secure_headers.framework(response)
Policy Builder¶
ContentSecurityPolicy()¶
Directives: base_uri(sources)
,
child_src(sources)
, connect_src(sources)
,
default_src(sources)
, font_src(sources)
,
form_action(sources)
, frame_ancestors(sources)
,
frame_src(sources)
, img_src(sources)
,
manifest_src(sources)
, media_src(sources)
,
object_src(sources)
, plugin_types(types)
,
report_to(json_object)
, report_uri(uri)
,
require_sri_for(values)
, sandbox(values)
,
script_src(sources)
, style_src(sources)
,
upgrade_insecure_requests()
, worker_src(sources)
Example:
csp_policy = (
secure.ContentSecurityPolicy()
.default_src("'none'")
.base_uri("'self'")
.connect_src("'self'", "api.spam.com")
.frame_src("'none'")
.img_src("'self'", "static.spam.com")
)
secure_headers = secure.Secure(csp=csp_policy)
# default-src 'none'; base-uri 'self'; connect-src 'self' api.spam.com; frame-src 'none'; img-src 'self' static.spam.com
You can check the effectiveness of your CSP Policy at the CSP Evaluator
StrictTransportSecurity()¶
Directives: include_subDomains()
, max_age(seconds)
,
preload()
Example:
hsts_value = (
secure.StrictTransportSecurity()
.include_subdomains()
.preload()
.max_age(2592000)
)
secure_headers = secure.Secure(hsts=hsts_value)
# includeSubDomains; preload; max-age=2592000
XFrameOptions()¶
Directives: allow_from(uri)
, deny()
, sameorigin()
Example:
xfo_value = secure.XFrameOptions().deny()
secure_headers = secure.Secure(xfo=xfo_value)
# deny
ReferrerPolicy()¶
Directives: no_referrer()
, no_referrer_when_downgrade()
,
origin()
, origin_when_cross_origin()
, same_origin()
,
strict_origin()
, strict_origin_when_cross_origin()
,
unsafe_url()
Example:
referrer = secure.ReferrerPolicy().strict_origin()
secure_headers = secure.Secure(referrer=referrer).headers()
# strict-origin
PermissionsPolicy()¶
Directives: accelerometer(allowlist)
,
ambient_light_sensor(allowlist)
, autoplay(allowlist)
,
camera(allowlist)
, document_domain(allowlist)
,
encrypted_media(allowlist)
, fullscreen(allowlist)
,
geolocation(allowlist)
, gyroscope(allowlist)
,
magnetometer(allowlist)
, microphone(allowlist)
,
midi(allowlist)
, payment(allowlist)
,
picture_in_picture(allowlist)
, speaker(allowlist)
,
sync_xhr(allowlist)
, usb(allowlist)
, Values(allowlist)
,
vr(allowlist)
Example:
permissions = (
secure.PermissionsPolicy().geolocation("self", '"spam.com"').vibrate()
)
secure_headers = secure.Secure(permissions=permissions).headers()
# geolocation=(self "spam.com"), vibrate=()
CacheControl()¶
Directives: immutable()
, max_age(seconds)
,
max_stale(seconds)
, min_fresh(seconds)
, must_revalidate()
,
no_cache()
, no_store()
, no_transform()
,
only_if_cached()
, private()
, proxy_revalidate()
,
public()
, s_maxage(seconds)
, stale_if_error(seconds)
,
stale_while_revalidate(seconds)
,
Example:
cache = secure.CacheControl().no_cache()
secure_headers = secure.Secure(cache=cache).headers()
# no-store
Usage¶
Example:
import uvicorn
from fastapi import FastAPI
import secure
app = FastAPI()
server = secure.Server().set("Secure")
csp = (
secure.ContentSecurityPolicy()
.default_src("'none'")
.base_uri("'self'")
.connect_src("'self'" "api.spam.com")
.frame_src("'none'")
.img_src("'self'", "static.spam.com")
)
hsts = secure.StrictTransportSecurity().include_subdomains().preload().max_age(2592000)
referrer = secure.ReferrerPolicy().no_referrer()
permissions_value = (
secure.PermissionsPolicy().geolocation("self", "'spam.com'").vibrate()
)
cache_value = secure.CacheControl().must_revalidate()
secure_headers = secure.Secure(
server=server,
csp=csp,
hsts=hsts,
referrer=referrer,
permissions=permissions_value,
cache=cache_value,
)
@app.middleware("http")
async def set_secure_headers(request, call_next):
response = await call_next(request)
secure_headers.framework.fastapi(response)
return response
@app.get("/")
async def root():
return {"message": "Secure"}
if __name__ == "__main__":
uvicorn.run(app, port=8081, host="localhost")
. . .
Response Headers:
server: Secure
strict-transport-security: includeSubDomains; preload; max-age=2592000
x-frame-options: SAMEORIGIN
x-xss-protection: 0
x-content-type-options: nosniff
content-security-policy: default-src 'none'; base-uri 'self'; connect-src 'self'api.spam.com; frame-src 'none'; img-src 'self' static.spam.com
referrer-policy: no-referrer
cache-control: must-revalidate
permissions-policy: geolocation=(self 'spam.com'), vibrate=()
Supported Frameworks¶
Framework Agnostic¶
secure_headers.headers()
Example:
secure_headers.framework.headers(csp=True, feature=True)
Return Value:
{'Strict-Transport-Security': 'max-age=63072000; includeSubdomains', 'X-Frame-Options': 'SAMEORIGIN', 'X-XSS-Protection': '0', 'X-Content-Type-Options': 'nosniff', 'Content-Security-Policy': "script-src 'self'; object-src 'self'", 'Referrer-Policy': 'no-referrer, strict-origin-when-cross-origin', 'Cache-control': 'no-cache, no-store, must-revalidate', 'Pragma': 'no-cache', 'Feature-Policy': "accelerometer 'none'; ambient-light-sensor 'none'; autoplay 'none'; camera 'none'; encrypted-media 'none';fullscreen 'none'; geolocation 'none'; gyroscope 'none'; magnetometer 'none'; microphone 'none'; midi 'none';payment 'none'; picture-in-picture 'none'; speaker 'none'; sync-xhr 'none'; usb 'none'; vr 'none';"}
aiohttp¶
secure_headers.framework.aiohttp(resp)
Example:
from aiohttp import web
from aiohttp.web import middleware
import secure
secure_headers = secure.Secure()
. . .
@middleware
async def set_secure_headers(request, handler):
resp = await handler(request)
secure_headers.framework.aiohttp(resp)
return resp
. . .
app = web.Application(middlewares=[set_secure_headers])
. . .
Bottle¶
secure_headers.framework.bottle(response)
Example:
from bottle import route, run, response, hook
import secure
secure_headers = secure.Secure()
. . .
@hook("after_request")
def set_secure_headers():
secure_headers.framework.bottle(response)
. . .
CherryPy¶
"tools.response_headers.headers": secure_headers.framework.cherrypy()
Example:
CherryPy Application Configuration:
import cherrypy
import secure
secure_headers = secure.Secure()
. . .
config = {
"/": {
"tools.response_headers.on": True,
"tools.response_headers.headers": secure_headers.framework.cherrypy(),
}
}
. . .
Django¶
secure_headers.framework.django(response)
Example:
Django Middleware Documentation:
# securemiddleware.py
import secure
secure_headers = secure.Secure()
. . .
def set_secure_headers(get_response):
def middleware(request):
response = get_response(request)
secure_headers.framework.django(response)
return response
return middleware
. . .
# settings.py
...
MIDDLEWARE = [
'app.securemiddleware.set_secure_headers'
]
...
FastAPI¶
secure_headers.framework.falcon(resp)
Example:
from fastapi import FastAPI
import secure
secure_headers = secure.Secure()
. . .
@app.middleware("http")
async def set_secure_headers(request, call_next):
response = await call_next(request)
secure_headers.framework.fastapi(response)
return response
. . .
Falcon¶
secure_headers.framework.falcon(resp)
Example:
import falcon
import secure
secure_headers = secure.Secure()
. . .
class SetSecureHeaders(object):
def process_request(self, req, resp):
secure_headers.framework.falcon(resp)
. . .
app = api = falcon.API(middleware=[SetSecureHeaders()])
. . .
Flask¶
secure_headers.framework.flask(response)
Example:
from flask import Flask, Response
import secure
secure_headers = secure.Secure()
app = Flask(__name__)
. . .
@app.after_request
def set_secure_headers(response):
secure_headers.framework.flask(response)
return response
. . .
hug¶
secure_headers.framework.hug(response)
Example:
import hug
import secure
secure_headers = secure.Secure()
. . .
@hug.response_middleware()
def set_secure_headers(request, response, resource):
secure_headers.framework.hug(response)
. . .
Masonite¶
secure_headers.framework.masonite(self.request)
Example:
Masonite Middleware:
# SecureMiddleware.py
from masonite.request import Request
import secure
secure_headers = secure.Secure()
class SecureMiddleware:
def __init__(self, request: Request):
self.request = request
def before(self):
secure_headers.framework.masonite(self.request)
. . .
# middleware.py
...
HTTP_MIDDLEWARE = [
SecureMiddleware,
]
...
Pyramid¶
Pyramid Tween:
def set_secure_headers(handler, registry):
def tween(request):
response = handler(request)
secure_headers.framework.pyramid(response)
return response
return tween
Example:
from pyramid.config import Configurator
from pyramid.response import Response
import secure
secure_headers = secure.Secure()
. . .
def set_secure_headers(handler, registry):
def tween(request):
response = handler(request)
secure_headers.framework.pyramid(response)
return response
return tween
. . .
config.add_tween(".set_secure_headers")
. . .
Quart¶
secure_headers.framework.quart(response)
Example:
from quart import Quart, Response
import secure
secure_headers = secure.Secure()
app = Quart(__name__)
. . .
@app.after_request
async def set_secure_headers(response):
secure_headers.framework.quart(response)
return response
. . .
Responder¶
secure_headers.framework.responder(resp)
Example:
import responder
import secure
secure_headers = secure.Secure()
api = responder.API()
. . .
@api.route(before_request=True)
def set_secure_headers(req, resp):
secure_headers.framework.responder(resp)
. . .
You should use Responder’s built in
HSTS
and pass the hsts=False
option.
Sanic¶
secure_headers.framework.sanic(response)
Example:
from sanic import Sanic
import secure
secure_headers = secure.Secure()
app = Sanic()
. . .
@app.middleware("response")
async def set_secure_headers(request, response):
secure_headers.framework.sanic(response)
. . .
To set Cross Origin Resource Sharing (CORS) headers, please see sanic-cors .
Starlette¶
secure_headers.framework.starlette(response)
Example:
from starlette.applications import Starlette
import uvicorn
import secure
secure_headers = secure.Secure()
app = Starlette()
. . .
@app.middleware("http")
async def set_secure_headers(request, call_next):
response = await call_next(request)
secure_headers.framework.starlette(response)
return response
. . .
Tornado¶
secure_headers.framework.tornado(self)
Example:
import tornado.ioloop
import tornado.web
import secure
secure_headers = secure.Secure()
. . .
class BaseHandler(tornado.web.RequestHandler):
def set_default_headers(self):
secure_headers.framework.tornado(self)
. . .
Resources¶
Frameworks¶
- aiohttp - Asynchronous HTTP client/server framework for asyncio and Python
- Bottle - A fast and simple micro-framework for python web-applications.
- CherryPy - A pythonic, object-oriented HTTP framework.
- Django - The Web framework for perfectionists with deadlines.
- Falcon - A bare-metal Python web API framework for building high-performance microservices, app backends, and higher-level frameworks.
- Flask - The Python micro framework for building web applications.
- hug - Embrace the APIs of the future. Hug aims to make developing APIs as simple as possible, but no simpler.
- Masonite - The Modern And Developer Centric Python Web Framework.
- Pyramid - A Python web framework
- Quart - A Python ASGI web microframework.
- Responder - A familiar HTTP Service Framework
- Sanic - An Async Python 3.5+ web server that’s written to go fast
- Starlette - The little ASGI framework that shines. ✨
- Tornado - A Python web framework and asynchronous networking library, originally developed at FriendFeed.
General¶
Policies¶
- CSP: CSP Cheat Sheet | Scott Helme, Content-Security-Policy | MDN, Content Security Policy Cheat Sheet | OWASP, Content Security Policy CSP Reference & Examples
- XXP: X-XSS-Protection | MDN
- XFO: X-Frame-Options | MDN
- HSTS: Strict-Transport-Security | MDN, HTTP Strict Transport Security Cheat Sheet | OWASP
- Referrer: A new security header: Referrer Policy | Scott Helme, Referrer-Policy | MDN
- Feature: A new security header: Feature Policy | Scott Helme, Feature-Policy | MDN, Introduction to Feature Policy | Google Developers
- Cache: Cache-Control | MDN