Skip to content

Latest commit

 

History

History
666 lines (433 loc) · 14.5 KB

File metadata and controls

666 lines (433 loc) · 14.5 KB

Framework Integration

secure keeps the same Secure object across frameworks. What changes is how you attach it.

Some sections below are first-class integrations with clear framework-level hooks or middleware. Others are intentionally minimal fallback examples where support is thinner or framework APIs vary by version.

How to choose an integration style

  • Use set_headers() when the response object is synchronous and you are already inside a response hook, middleware callback, or view.
  • Use set_headers_async() in async middleware, hooks, or handlers when you want one helper that works safely across async response objects.
  • Use SecureWSGIMiddleware when you want app-wide coverage and can wrap a WSGI application directly.
  • Use SecureASGIMiddleware when you want app-wide coverage in an ASGI stack such as FastAPI, Starlette, or Shiny.

Prefer middleware when your framework makes it easy and you want app-wide coverage. Use per-response setters when you are integrating into an existing hook, view, or minimal handler path.

Uvicorn Server header

Uvicorn adds Server: uvicorn by default. If you want secure to control the Server header, disable Uvicorn's default header with --no-server-header or server_header=False.

import uvicorn

uvicorn.run(app, host="0.0.0.0", port=8000, server_header=False)

Table of contents

aiohttp

Async framework with first-class middleware support.

Recommended: middleware with set_headers_async()

from aiohttp import web
from secure import Secure

secure_headers = Secure.with_default_headers()


@web.middleware
async def add_security_headers(request, handler):
    response = await handler(request)
    await secure_headers.set_headers_async(response)
    return response


app = web.Application(middlewares=[add_security_headers])

Alternative: set headers in a single handler

from aiohttp import web
from secure import Secure

secure_headers = Secure.with_default_headers()


async def home(request):
    response = web.Response(text="Hello, world")
    await secure_headers.set_headers_async(response)
    return response

Bottle

Small WSGI framework with request hooks.

Recommended: after_request hook with set_headers()

from bottle import Bottle, response
from secure import Secure

app = Bottle()
secure_headers = Secure.with_default_headers()


@app.hook("after_request")
def add_security_headers():
    secure_headers.set_headers(response)

Fallback: set headers in a route

from bottle import Bottle, response
from secure import Secure

app = Bottle()
secure_headers = Secure.with_default_headers()


@app.route("/")
def home():
    secure_headers.set_headers(response)
    return "Hello, world"

CherryPy

Minimal fallback example. CherryPy exposes the response object in the handler, so handler-level mutation is the practical integration point.

Minimal fallback: set headers in the exposed method

import cherrypy
from secure import Secure

secure_headers = Secure.with_default_headers()


class App:
    @cherrypy.expose
    def index(self):
        secure_headers.set_headers(cherrypy.response)
        return b"Hello, world"


cherrypy.quickstart(App())

Dash

Dash runs on top of Flask, so the usual Flask integration patterns apply.

Recommended: Flask after_request on app.server

import dash
from dash import html
from secure import Secure

app = dash.Dash(__name__)
server = app.server
secure_headers = Secure.with_default_headers()

app.layout = html.Div("Hello Dash!")


@server.after_request
def add_security_headers(response):
    secure_headers.set_headers(response)
    return response

Alternative: SecureWSGIMiddleware

import dash
from dash import html
from secure import Secure
from secure.middleware import SecureWSGIMiddleware

app = dash.Dash(__name__)
server = app.server
secure_headers = Secure.with_default_headers()

app.layout = html.Div("Hello Dash!")
server.wsgi_app = SecureWSGIMiddleware(server.wsgi_app, secure=secure_headers)

Django

Django is usually best integrated through Django middleware rather than raw WSGI wrapping.

Recommended: Django middleware class

Register the middleware class in your Django MIDDLEWARE setting.

from secure import Secure


class SecureHeadersMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        self.secure = Secure.with_default_headers()

    def __call__(self, request):
        response = self.get_response(request)
        self.secure.set_headers(response)
        return response

Fallback: set headers in a view

from django.http import HttpResponse
from secure import Secure

secure_headers = Secure.with_default_headers()


def home(request):
    response = HttpResponse("Hello, world")
    secure_headers.set_headers(response)
    return response

Falcon

Falcon exposes a clean response middleware hook.

Recommended: Falcon middleware

import falcon
from secure import Secure

secure_headers = Secure.with_default_headers()


class SecureMiddleware:
    def process_response(self, req, resp, resource, req_succeeded):
        secure_headers.set_headers(resp)


app = falcon.App(middleware=[SecureMiddleware()])

Fallback: set headers in the resource

import falcon
from secure import Secure

secure_headers = Secure.with_default_headers()


class HelloWorldResource:
    def on_get(self, req, resp):
        resp.text = "Hello, world"
        secure_headers.set_headers(resp)


app = falcon.App()
app.add_route("/", HelloWorldResource())

FastAPI

ASGI framework. Middleware is the clearest default.

Recommended: SecureASGIMiddleware

from fastapi import FastAPI
from secure import Secure
from secure.middleware import SecureASGIMiddleware

app = FastAPI()
secure_headers = Secure.with_default_headers()

app.add_middleware(SecureASGIMiddleware, secure=secure_headers)

Alternative: @app.middleware("http")

from fastapi import FastAPI
from secure import Secure

app = FastAPI()
secure_headers = Secure.with_default_headers()


@app.middleware("http")
async def add_security_headers(request, call_next):
    response = await call_next(request)
    await secure_headers.set_headers_async(response)
    return response

Fallback: set headers in one route

from fastapi import FastAPI, Response
from secure import Secure

app = FastAPI()
secure_headers = Secure.with_default_headers()


@app.get("/")
def home(response: Response):
    secure_headers.set_headers(response)
    return {"hello": "world"}

Flask

WSGI framework with a straightforward response hook.

Recommended: after_request

from flask import Flask
from secure import Secure

app = Flask(__name__)
secure_headers = Secure.with_default_headers()


@app.after_request
def add_security_headers(response):
    secure_headers.set_headers(response)
    return response

Alternative: SecureWSGIMiddleware

from flask import Flask
from secure import Secure
from secure.middleware import SecureWSGIMiddleware

app = Flask(__name__)
secure_headers = Secure.with_default_headers()

app.wsgi_app = SecureWSGIMiddleware(app.wsgi_app, secure=secure_headers)

Masonite

Minimal fallback example. Masonite routing and response APIs vary by version, so apply Secure to the response object you actually return.

Minimal fallback: apply to the response you return

from secure import Secure

secure_headers = Secure.with_default_headers()


def home(response):
    rendered = response.json({"hello": "world"})
    secure_headers.set_headers(rendered)
    return rendered

Morepath

Minimal fallback example. Morepath does not expose a conventional middleware layer for this, so view-level mutation is the practical integration point.

Minimal fallback: set headers in the view

import morepath
from secure import Secure

secure_headers = Secure.with_default_headers()


class App(morepath.App):
    pass


@App.path(path="")
class Root:
    pass


@App.view(model=Root)
def home(self, request):
    response = morepath.Response("Hello, world")
    secure_headers.set_headers(response)
    return response

Pyramid

Pyramid applications commonly use tweens for cross-cutting response changes.

Recommended: tween

Register the tween in your Configurator with config.add_tween("yourpackage.security.add_security_headers").

from secure import Secure

secure_headers = Secure.with_default_headers()


def add_security_headers(handler, registry):
    def tween(request):
        response = handler(request)
        secure_headers.set_headers(response)
        return response

    return tween

Fallback: set headers in a view

from pyramid.response import Response
from secure import Secure

secure_headers = Secure.with_default_headers()


def home(request):
    response = Response("Hello, world")
    secure_headers.set_headers(response)
    return response

Quart

Async Flask-compatible framework.

Recommended: after_request with set_headers_async()

from quart import Quart
from secure import Secure

app = Quart(__name__)
secure_headers = Secure.with_default_headers()


@app.after_request
async def add_security_headers(response):
    await secure_headers.set_headers_async(response)
    return response

Fallback: set headers in a route

from quart import Quart, Response
from secure import Secure

app = Quart(__name__)
secure_headers = Secure.with_default_headers()


@app.route("/")
async def home():
    response = Response("Hello, world")
    await secure_headers.set_headers_async(response)
    return response

Responder

Minimal fallback example. Route handlers typically own the response, so route-level mutation is the practical integration point.

Minimal fallback: set headers in the route

import responder
from secure import Secure

api = responder.API()
secure_headers = Secure.with_default_headers()


@api.route("/")
async def home(req, resp):
    resp.text = "Hello, world"
    await secure_headers.set_headers_async(resp)

Sanic

Sanic exposes response middleware for app-wide coverage.

Recommended: response middleware with set_headers_async()

from sanic import Sanic
from secure import Secure

app = Sanic("secure-app")
secure_headers = Secure.with_default_headers()


@app.middleware("response")
async def add_security_headers(request, response):
    await secure_headers.set_headers_async(response)
    return response

Use route-level setters when you only need a small integration or do not want extra app wiring in tests.

Fallback: set headers in a route

from sanic import Sanic, response
from secure import Secure

app = Sanic("secure-app")
secure_headers = Secure.with_default_headers()


@app.get("/")
async def home(request):
    resp = response.text("Hello, world")
    await secure_headers.set_headers_async(resp)
    return resp

Shiny

Shiny applications are ASGI apps, so ASGI middleware is the cleanest and most direct path.

Recommended: SecureASGIMiddleware

from secure import Secure
from secure.middleware import SecureASGIMiddleware
from shiny import App, ui

secure_headers = Secure.with_default_headers()

app_ui = ui.page_fluid("Hello Shiny!")


def server(input, output, session):
    pass


app = App(app_ui, server)
app = SecureASGIMiddleware(app, secure=secure_headers)

Starlette

ASGI framework. Use ASGI middleware unless you only need route-level control.

Recommended: SecureASGIMiddleware

from secure import Secure
from secure.middleware import SecureASGIMiddleware
from starlette.applications import Starlette
from starlette.responses import PlainTextResponse
from starlette.routing import Route

secure_headers = Secure.with_default_headers()


async def home(request):
    return PlainTextResponse("Hello, world")


app = Starlette(routes=[Route("/", home)])
app.add_middleware(SecureASGIMiddleware, secure=secure_headers)

Alternative: set headers in an endpoint

from secure import Secure
from starlette.applications import Starlette
from starlette.responses import Response
from starlette.routing import Route

secure_headers = Secure.with_default_headers()


async def home(request):
    response = Response("Hello, world")
    await secure_headers.set_headers_async(response)
    return response


app = Starlette(routes=[Route("/", home)])

Tornado

Minimal fallback example. Tornado usually applies headers inside request handlers, so handler-level mutation is the practical integration point.

Minimal fallback: set headers in the handler

import tornado.web
from secure import Secure

secure_headers = Secure.with_default_headers()


class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")
        secure_headers.set_headers(self)


app = tornado.web.Application([(r"/", MainHandler)])

TurboGears

Minimal fallback example. If you do not already have a framework-level hook in place, controller-level mutation is the practical integration point.

Minimal fallback: set headers in the controller

from tg import Response, TGController, expose
from secure import Secure

secure_headers = Secure.with_default_headers()


class RootController(TGController):
    @expose()
    def index(self):
        response = Response("Hello, world")
        secure_headers.set_headers(response)
        return response


root = RootController()

Custom frameworks

If your framework is not listed here, the integration rule is still simple: configure one Secure instance, then apply it to the response as late as possible before it is sent.

Recommended: use the response object's setter or headers mapping

from secure import Secure

secure_headers = Secure.with_default_headers()


def add_security_headers(response):
    secure_headers.set_headers(response)
    return response

Fallback: emit header pairs manually

from secure import Secure

secure_headers = Secure.with_default_headers()

for name, value in secure_headers.header_items():
    response.headers[name] = value