A hardened replacement layer for Flask with automatic security defaults. Independent of Microsoft Presidio (a data-anonymization toolkit).
Project description
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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file presidio_hardened_flask-0.2.0.tar.gz.
File metadata
- Download URL: presidio_hardened_flask-0.2.0.tar.gz
- Upload date:
- Size: 65.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cea38940d3dc37d5aa94024c1d0d6594fddb82de704cb34353875bbc21e7c9eb
|
|
| MD5 |
6c7b30dc36e5f3f95c1fcd29e43da888
|
|
| BLAKE2b-256 |
5cc6b4980cdb85f9730e46141c7fc3ac8532a06426a0a42a848b429999690e95
|
Provenance
The following attestation bundles were made for presidio_hardened_flask-0.2.0.tar.gz:
Publisher:
publish.yml on presidio-v/presidio-hardened-flask
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
presidio_hardened_flask-0.2.0.tar.gz -
Subject digest:
cea38940d3dc37d5aa94024c1d0d6594fddb82de704cb34353875bbc21e7c9eb - Sigstore transparency entry: 1791526570
- Sigstore integration time:
-
Permalink:
presidio-v/presidio-hardened-flask@502146b48388bf71fc06b00cc79a69c3669651e5 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/presidio-v
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@502146b48388bf71fc06b00cc79a69c3669651e5 -
Trigger Event:
push
-
Statement type:
File details
Details for the file presidio_hardened_flask-0.2.0-py3-none-any.whl.
File metadata
- Download URL: presidio_hardened_flask-0.2.0-py3-none-any.whl
- Upload date:
- Size: 13.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ba99c58beb4258c864c29b0e6477878c5e3a10dfea121962532f054d419278c1
|
|
| MD5 |
0f3fbffc71949d42299698331203e62e
|
|
| BLAKE2b-256 |
472752e137dc5dcb833604489bdb1967de7f884b7c1470c66052a2b59ac9c7a8
|
Provenance
The following attestation bundles were made for presidio_hardened_flask-0.2.0-py3-none-any.whl:
Publisher:
publish.yml on presidio-v/presidio-hardened-flask
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
presidio_hardened_flask-0.2.0-py3-none-any.whl -
Subject digest:
ba99c58beb4258c864c29b0e6477878c5e3a10dfea121962532f054d419278c1 - Sigstore transparency entry: 1791526915
- Sigstore integration time:
-
Permalink:
presidio-v/presidio-hardened-flask@502146b48388bf71fc06b00cc79a69c3669651e5 -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/presidio-v
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@502146b48388bf71fc06b00cc79a69c3669651e5 -
Trigger Event:
push
-
Statement type: