Skip to content

Latest commit

 

History

History
276 lines (180 loc) · 7.2 KB

File metadata and controls

276 lines (180 loc) · 7.2 KB

secure

PyPI Version Python Versions License

Define HTTP security headers once. Apply them consistently across Python web apps.

secure provides a small, dependency-free API for configuring modern security headers across common Python web frameworks through ASGI middleware, WSGI middleware, or framework response hooks.

Use it when you want to avoid copy-pasted header strings spread across handlers, hooks, and middleware.

secure hex

Quick links: Quick start · Headers · Middleware


Why use secure

Setting headers manually is fine for one endpoint. It gets harder to review when values are copied across routes, response hooks, reverse-proxy settings, and different framework integrations.

secure helps you:

  • Keep one Secure policy object instead of scattered header strings
  • Apply the same policy through ASGI middleware, WSGI middleware, or response hooks
  • Start with practical presets, then customize headers for your application
  • Use builders for complex headers such as Content Security Policy and Permissions Policy
  • Make duplicate handling, header overwrites, and validation explicit

The defaults are a reasonable starting point, not a substitute for application-specific review. Content Security Policy in particular should be adjusted for the scripts, styles, assets, and third-party services your app actually uses.


Installation

uv add secure
# or
pip install secure

Quick start

FastAPI / ASGI middleware

Use middleware when you can attach secure once and cover the whole application.

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)

Response hooks and handlers

Use the same policy object in framework hooks, middleware callbacks, or handlers that expose a response object with headers support.

from secure import Secure

secure_headers = Secure.with_default_headers()

secure_headers.set_headers(response)
# or
await secure_headers.set_headers_async(response)

Secure.with_default_headers() maps to Preset.BALANCED, a practical default designed to be customized.


What headers are applied by default

Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Resource-Policy: same-origin
Content-Security-Policy: default-src 'self'; base-uri 'self'; font-src 'self' https: data:; form-action 'self'; frame-ancestors 'self'; img-src 'self' data:; object-src 'none'; script-src 'self'; script-src-attr 'none'; style-src 'self' https: 'unsafe-inline'; upgrade-insecure-requests
Strict-Transport-Security: max-age=31536000; includeSubDomains
Permissions-Policy: geolocation=(), microphone=(), camera=()
Referrer-Policy: strict-origin-when-cross-origin
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN

This preset reflects modern browser guidance from MDN and OWASP. It reduces cross-origin risk, prevents MIME sniffing, and provides a conservative Content Security Policy you can extend.

The default CSP avoids unsafe script execution by default, but CSP is context-sensitive. Interactive apps, dashboards, CDNs, analytics, embedded content, or other third-party integrations may require additional configuration.


Presets

from secure import Preset, Secure

Secure.from_preset(Preset.BALANCED)
Secure.from_preset(Preset.BASIC)
Secure.from_preset(Preset.STRICT)
  • BALANCED: practical default for many applications
  • BASIC: Helmet-style compatibility
  • STRICT: tighter CSP and isolation

Start with BALANCED, review the generated headers, and move stricter only when needed.


Middleware

secure provides both ASGI and WSGI middleware.

  • Overwrites headers by default
  • Allows controlled duplication via multi_ok
  • Only modifies HTTP responses in ASGI apps

ASGI

from secure import Secure
from secure.middleware import SecureASGIMiddleware

secure_headers = Secure.with_default_headers()
app = SecureASGIMiddleware(app, secure=secure_headers)

WSGI

from secure import Secure
from secure.middleware import SecureWSGIMiddleware

secure_headers = Secure.with_default_headers()
app = SecureWSGIMiddleware(app, secure=secure_headers)

Django example

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

Framework integration examples

These examples show common integration paths. See the full framework integration guide for additional frameworks and notes about first-class middleware versus minimal fallback examples.

FastAPI

from secure.middleware import SecureASGIMiddleware

app.add_middleware(SecureASGIMiddleware, secure=secure_headers)

Flask

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

Shiny for Python

from shiny import App
from secure.middleware import SecureASGIMiddleware

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

Interactive apps often need CSP configuration for their script, style, and asset loading patterns.


Policy builders

Content Security Policy

from secure.headers import ContentSecurityPolicy

csp = (
    ContentSecurityPolicy()
    .default_src("'self'")
    .script_src("'self'", "cdn.example.com")
)

Permissions Policy

from secure.headers import PermissionsPolicy

permissions = (
    PermissionsPolicy()
    .geolocation("'self'")
    .camera("'none'")
)

Header pipeline and validation

Use the optional pipeline when you need stricter control:

secure_headers = (
    Secure.with_default_headers()
    .allowlist_headers(...)
    .deduplicate_headers(...)
    .validate_and_normalize_headers(...)
)

This makes header allowlists, deduplication, normalization, and validation explicit instead of leaving those choices spread across application code.


Requirements

  • Python 3.10+
  • No external dependencies

Documentation

Read the full documentation.


Learn more


License

MIT License


Contributing

Issues and pull requests are welcome.


Acknowledgements

Built using guidance from MDN and OWASP secure headers recommendations.