security: implement pentest remediation (PROXY-001, BURN-001, RATE-001)

PROXY-001: Add startup warning when TRUSTED_PROXY_SECRET empty in production
- validate_security_config() checks for missing proxy secret
- Additional warning when PKI enabled without proxy secret
- Tests for security configuration validation

BURN-001: HEAD requests now trigger burn-after-read deletion
- Prevents attacker from probing paste existence before retrieval
- Updated test to verify new behavior

RATE-001: Add RATE_LIMIT_MAX_ENTRIES to cap memory usage
- Default 10000 unique IPs tracked
- Prunes oldest entries when limit exceeded
- Protects against memory exhaustion DoS

Test count: 284 -> 291 (7 new security tests)
This commit is contained in:
Username
2025-12-24 21:42:15 +01:00
parent bebc6e0354
commit 89eee3378a
10 changed files with 271 additions and 15 deletions

View File

@@ -36,6 +36,32 @@ def setup_logging(app: Flask) -> None:
app.logger.info("FlaskPaste starting", extra={"config": type(app.config).__name__})
def validate_security_config(app: Flask) -> None:
"""Validate security configuration and log warnings.
Checks for common security misconfigurations that could lead to
vulnerabilities in production deployments.
"""
is_production = not app.debug and not app.config.get("TESTING")
# PROXY-001: Check TRUSTED_PROXY_SECRET
proxy_secret = app.config.get("TRUSTED_PROXY_SECRET", "")
if is_production and not proxy_secret:
app.logger.warning(
"SECURITY WARNING: TRUSTED_PROXY_SECRET is not set. "
"Client certificate headers (X-SSL-Client-SHA1) can be spoofed. "
"Set FLASKPASTE_PROXY_SECRET to a shared secret known only by your reverse proxy."
)
# Warn if PKI is enabled without proxy secret
pki_enabled = app.config.get("PKI_ENABLED", False)
if pki_enabled and not proxy_secret:
app.logger.warning(
"SECURITY WARNING: PKI is enabled but TRUSTED_PROXY_SECRET is not set. "
"Certificate-based authentication can be bypassed by spoofing headers."
)
def setup_security_headers(app: Flask) -> None:
"""Add security headers to all responses."""
@@ -235,6 +261,9 @@ def create_app(config_name: str | None = None) -> Flask:
# Setup logging first
setup_logging(app)
# Validate security configuration
validate_security_config(app)
# Setup request ID tracking
setup_request_id(app)