presidio-hardened-flask
A hardened drop-in replacement for Flask that automatically applies production-grade security defaults (v0.2.0). Change one import line and your existing Flask app gets security headers, rate limiting, CSRF protection (no weak defaults), secret redaction (sink-enforced), input sanitization, and more.
v0.2.0 Security Updates
- Sink redaction now enforced at the logging sink (RedactingFilter on app.logger).
- CSRF no longer accepts the dangerous built-in "presidio-dev-key" fallback — requires a strong
SECRET_KEY(raises clear error otherwise). - pip-audit integrated in dev dependencies and CI.
- All major audit findings from third-party review remediated; docs (SECURITY, PRESIDIO-REQ, README) updated to match implemented behavior.
Quick Start
Install
pip install presidio-hardened-flask
Usage — Change One Import
Before (plain Flask):
from flask import Flask, jsonify, request
app = Flask(__name__)
@app.route("/")
def index():
return "Hello, world!"
After (presidio-hardened-flask):
from presidio_flask import Flask, jsonify, request
app = Flask(__name__)
@app.route("/")
def index():
return "Hello, secure world!"
That's it. Your app now automatically receives:
| Feature | What It Does (v0.2.0) |
|---|---|
| Security Headers | CSP, HSTS, X-Frame-Options, Permissions-Policy, and more on every response |
| Rate Limiting | 60 req/min per IP with exponential backoff (configurable) |
| CSRF Protection | Sec-Fetch-Site aware + token-based fallback (v0.2: requires strong SECRET_KEY; no 'presidio-dev-key' fallback) |
| Secret Redaction | Passwords, API keys, tokens redacted in before_request logs + sink-level RedactingFilter on app.logger for all records |
| Session Hardening | Secure, HttpOnly, SameSite=Lax cookies by default |
| Input Sanitization | Blocks SQL injection, XSS, and path traversal attempts (abort 400) |
| CVE Quick-Check | Warns at startup + pip-audit in [dev] and CI (v0.2) |
| Security Logging | Structured Presidio event logging (with automatic sink redaction) |
Side-by-Side Comparison
Security Headers
Plain Flask returns no security headers by default:
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
presidio-hardened-flask adds them automatically:
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: strict-origin-when-cross-origin
Content-Security-Policy: default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
Permissions-Policy: geolocation=(), camera=(), microphone=()
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Resource-Policy: same-origin
Rate Limiting
Plain Flask has no built-in rate limiting. A single client can hammer your API.
presidio-hardened-flask enforces per-IP rate limits with informative headers:
X-RateLimit-Limit: 60
X-RateLimit-Remaining: 57
When exceeded, returns 429 Too Many Requests with exponential backoff.
Secret Redaction
Plain Flask will happily log passwords and tokens in plain text:
app.logger.info("Request: %s", request.json)
# {"username": "alice", "password": "hunter2", "api_key": "sk-live-abc123"}
presidio-hardened-flask automatically redacts sensitive fields:
from presidio_flask import redact_dict
print(redact_dict({"password": "hunter2", "api_key": "sk-123"}))
# {"password": "***REDACTED***", "api_key": "***REDACTED***"}
CSRF Protection
Plain Flask has no CSRF protection.
presidio-hardened-flask uses a modern two-layer approach:
- Sec-Fetch-Site header — blocks cross-origin mutating requests automatically
- Token-based fallback — for older browsers or custom setups
from presidio_flask import generate_csrf_token
# In your template / API response:
token = generate_csrf_token()
# Include as X-CSRF-Token header or _csrf_token form field
Configuration
All features are enabled by default but fully configurable:
app = Flask(__name__)
app.config.update(
# Rate limiting
PRESIDIO_RATE_LIMIT=100, # requests per window (default: 60)
PRESIDIO_RATE_WINDOW=120, # window in seconds (default: 60)
PRESIDIO_RATE_LIMIT_ENABLED=True, # disable entirely if False
# CSRF
PRESIDIO_CSRF_ENABLED=True,
# Input sanitization
PRESIDIO_SANITIZE_ENABLED=True,
# Secret redaction
PRESIDIO_REDACTION_ENABLED=True,
# Security logging
PRESIDIO_LOGGING_ENABLED=True,
# CVE check on startup
PRESIDIO_CVE_CHECK=True,
# Custom security headers (merge with/override defaults)
PRESIDIO_SECURITY_HEADERS={
"X-Frame-Options": "SAMEORIGIN",
"Content-Security-Policy": "default-src 'self'; img-src *",
},
)
Development
# Clone and install
git clone https://github.com/presidio-v/presidio-hardened-flask.git
cd presidio-hardened-flask
uv venv .venv && source .venv/bin/activate
uv pip install -e ".[dev]"
# Run tests
pytest --cov=presidio_flask
# Lint and format
ruff format .
ruff check . --fix
License
Metadata
Release files for presidio-hardened-flask 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| presidio_hardened_flask-0.2.0.tar.gz | 65.2 kB | With provenance | Details for presidio_hardened_flask-0.2.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| presidio_hardened_flask-0.2.0-py3-none-any.whl | Python 3 | none | any | Details for presidio_hardened_flask-0.2.0-py3-none-any.whl |
Total release size: 79.2 kB
Release files / presidio_hardened_flask-0.2.0.tar.gz
| Download URL | presidio_hardened_flask-0.2.0.tar.gz |
|---|---|
| Size | 65.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
cea38940d3dc37d5aa94024c1d0d6594fddb82de704cb34353875bbc21e7c9eb
|
|
BLAKE2b-256 checksum How to use checksums |
5cc6b4980cdb85f9730e46141c7fc3ac8532a06426a0a42a848b429999690e95
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 11, 2026.
Transparency logRelease files / presidio_hardened_flask-0.2.0-py3-none-any.whl
| Download URL | presidio_hardened_flask-0.2.0-py3-none-any.whl |
|---|---|
| Size | 13.9 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ba99c58beb4258c864c29b0e6477878c5e3a10dfea121962532f054d419278c1
|
|
BLAKE2b-256 checksum How to use checksums |
472752e137dc5dcb833604489bdb1967de7f884b7c1470c66052a2b59ac9c7a8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.12
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 11, 2026.
Transparency logSecurity for presidio-hardened-flask 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for presidio_hardened_flask-0.2.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jun 11, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.