mirror of
https://github.com/jhd3197/ServerKit.git
synced 2026-11-09 13:35:12 +09:00
Rate-limit buckets, login lockout and audit-log source IPs hand-parsed the leftmost X-Forwarded-For token — a client-controlled value — letting an attacker rotate it to dodge per-IP limits or forge audit trails. - config: add TRUST_PROXY_HEADERS / TRUSTED_PROXY_HOPS (+ _env_bool/_env_int) - create_app: wrap in gated Werkzeug ProxyFix (rightmost trusted hops) before the limiter/handlers so every remote_addr consumer sees the real client - new app/utils/client_ip.py::get_client_ip() — the single trusted seam - replace all six leftmost-XFF parsers (rate_limit, audit middleware, api_key_auth, api_analytics, audit_service, ddns) with get_client_ip() - deploy: root .env.example ships TRUST_PROXY_HEADERS=true (behind nginx); backend/.env.example documents it off for dev - tests: test_trusted_client_ip.py (7) incl. real flask-limiter bucket isolation Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
32 lines
1021 B
Python
32 lines
1021 B
Python
"""API Key authentication middleware."""
|
|
from flask import g, request, jsonify
|
|
|
|
|
|
def register_api_key_auth(app):
|
|
"""Register API key authentication as a before_request handler."""
|
|
|
|
@app.before_request
|
|
def authenticate_api_key():
|
|
"""Check for X-API-Key header and validate."""
|
|
api_key_header = request.headers.get('X-API-Key')
|
|
|
|
if not api_key_header:
|
|
return # No API key provided, fall through to JWT auth
|
|
|
|
from app.services.api_key_service import ApiKeyService
|
|
api_key = ApiKeyService.validate_key(api_key_header)
|
|
|
|
if not api_key:
|
|
return jsonify({'error': 'Invalid or expired API key'}), 401
|
|
|
|
# Record usage against the trusted client IP (see app.utils.client_ip).
|
|
from app.utils.client_ip import get_client_ip
|
|
api_key.record_usage(get_client_ip())
|
|
|
|
from app import db
|
|
db.session.commit()
|
|
|
|
# Store in g for downstream use
|
|
g.api_key = api_key
|
|
g.api_key_user = api_key.user
|