Flask Middleware voor Geavanceerde Request Validatie en Rate Limiting
description
Met deze prompt leer ik hoe ik middleware kan inzetten om verzoeken te controleren en limieten toe te passen, wat helpt bij het voorkomen van misbruik en het verbeteren van de betrouwbaarheid van mijn Flask applicatie. Dit is effectiever dan standaard middleware omdat het maatwerk biedt voor beveiliging en prestatiebeheer.
prompt
Help me geavanceerde request validatie en rate limiting te implementeren in mijn Flask applicatie. Mijn huidige middleware setup is: <beschrijf je huidige middleware configuratie>. Leg ui ...
try_prompt
generate_helper
disclaimerOnPageApi
tags
ai_answers
provider: openai
model: gpt-4.1-nano
Natuurlijk! Ik zal je stap voor stap uitleggen hoe je middleware in Flask kunt gebruiken voor geavanceerde request validatie en rate limiting.
### 1. Middleware in Flask
In Flask kun je middleware implementeren door gebruik te maken van `before_request` en `after_request` decorators of door een WSGI middleware te schrijven. Voor eenvoudige validatie is `before_request` vaak voldoende.
### 2. Validatie op basis van headers, IP en payload
Je kunt in `before_request` controleren:
- **Headers**: bijvoorbeeld een API key of token
- **IP-adres**: bijvoorbeeld om bepaalde IP's te blokkeren
- **Payload inhoud**: bijvoorbeeld bepaalde vereiste velden of formaten
Voorbeeld:
```python
from flask import Flask, request, abort
app = Flask(__name__)
@app.before_request
def validate_request():
# Controleer headers
api_key = request.headers.get('X-API-KEY')
if api_key != 'jouw_verwachte_api_key':
abort(401, description="Ongeldige API key")
# Controleer IP-adres
client_ip = request.remote_addr
# Bijvoorbeeld blokkeren van een IP
geblokkeerde_ip's = ['192.168.1.10']
if client_ip in geblokkeerde_ip's:
abort(403, description="IP adres geblokkeerd")
# Controleer payload inhoud (bijvoorbeeld JSON)
if request.is_json:
data = request.get_json()
if 'belangrijk_veld' not in data:
abort(400, description="Veld 'belangrijk_veld' ontbreekt")
else:
abort(400, description="Verzoek moet JSON bevatten")
```
### 3. Rate limiting implementeren
Voor rate limiting kun je gebruik maken van een externe bibliotheek zoals `Flask-Limiter`. Hiermee kun je eenvoudig limieten instellen per IP of per gebruiker.
**Installatie:**
```bash
pip install flask-limiter
```
**Gebruik:**
```python
from flask import Flask
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
app = Flask(__name__)
limiter = Limiter(
app,
key_func=get_remote_address,
default_limits=["100 per uur"]
)
@app.route("/data")
@limiter.limit("10 per minuut")
def data_endpoint():
return "Dit is een gelimiteerd endpoint"
# Voeg ook in validate_request toe om per IP extra limieten te kunnen doen indien nodig
```
### 4. Samenvatting
- Gebruik `@app.before_request` voor custom validatie op headers, IP en payload
- Gebruik `Flask-Limiter` voor rate limiting
- Combineer beiden in je Flask app voor een robuuste beveiliging
### Voorbeeld samengevoegd:
```python
from flask import Flask, request, abort
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
app = Flask(__name__)
limiter = Limiter(
app,
key_func=get_remote_address,
default_limits=["100 per uur"]
)
@app.before_request
def validate_request():
# Header validatie
api_key = request.headers.get('X-API-KEY')
if api_key != 'jouw_verwachte_api_key':
abort(401, description="Ongeldige API key")
# IP blokkering
client_ip = request.remote_addr
geblokkeerde_ip's = ['192.168.1.10']
if client_ip in geblokkeerde_ip's:
abort(403, description="IP adres geblokkeerd")
# Payload validatie
if request.is_json:
data = request.get_json()
if 'belangrijk_veld' not in data:
abort(400, description="Veld 'belangrijk_veld' ontbreekt")
else:
abort(400, description="Verzoek moet JSON bevatten")
@app.route("/data")
@limiter.limit("10 per minuut")
def data_endpoint():
return "Geleverd data met limiet en validatie"
if __name__ == "__main__":
app.run()
```
Hierdoor heb je een goede basis voor geavanceerde request validatie en rate limiting in je Flask applicatie.
provider: deepseek
model: deepseek-chat
Absoluut. Laten we dit stap voor stap doorlopen. Omdat je nog geen middleware hebt, beginnen we vanaf de basis.
### Inleiding: Wat is Middleware in Flask?
In Flask fungeert middleware als een tussenlaag die alle inkomende requests verwerkt *voordat* ze je hoofdapplicatie (routes) bereiken, en uitgaande responses *nadat* ze je applicatie verlaten. Het is perfect voor cross-cutting concerns zoals authenticatie, logging, validatie en rate limiting.
Je kunt middleware in Flask op twee manieren implementeren:
1. **WSGI Middleware:** "Buiten" de Flask app, vaak bij het aanmaken van de `app` object.
2. **Blueprint/Before-Request Hook:** Meer Flask-eigen, waarbij je de `@app.before_request` decorator gebruikt. Voor jouw use case is dit een uitstekende en eenvoudige keuze.
We gaan beide benaderingen voor validatie bekijken en gebruiken een specifieke library voor rate limiting.
---
### Deel 1: Geavanceerde Request Validatie
Het doel is om requests vroegtijdig af te wijzen als ze niet aan je eisen voldoen.
#### Optie A: Met `@app.before_request` (Aanbevolen voor beginners)
Dit is de meest Flask-vriendelijke manier. Je maakt een functie die voor elke request wordt uitgevoerd.
1. **Installeer een library voor validatie (optioneel maar aanbevolen):**
`Flask-Inputs` is een goede keuze. Het gebruikt `WTForms` en `jsonschema` onder de motorkap.
```bash
pip install flask-inputs
```
2. **Implementatie in je `app.py` (of gelijkwaardig):**
```python
from flask import Flask, request, jsonify, abort
from flask_inputs import Inputs
from flask_inputs.validators import JsonSchema, HeaderSchema
import ipaddress
app = Flask(__name__)
# --- Schema's Definieren ---
# JSON Payload Validatie Schema (jsonschema)
payload_schema = {
'type': 'object',
'properties': {
'name': {'type': 'string', 'minLength': 1, 'maxLength': 50},
'email': {'type': 'string', 'format': 'email'},
'user_id': {'type': 'integer'}
},
'required': ['name', 'email'] # Verplichte velden
}
# Header Validatie Schema
header_schema = {
'type': 'object',
'properties': {
'Content-Type': {'type': 'string', 'pattern': '^application/json$'}, # Forceer JSON
'User-Agent': {'type': 'string', 'minLength': 1}, # Vereis een User-Agent
'X-Api-Key': {'type': 'string'} # Vereis een API Key
},
'required': ['Content-Type', 'User-Agent', 'X-Api-Key']
}
# --- Validatie Klasse (gebruikt door before_request) ---
class RequestValidator(Inputs):
json = [JsonSchema(schema=payload_schema)]
headers = [HeaderSchema(schema=header_schema)]
# --- IP Whitelist/Blacklist Functie ---
def validate_ip(client_ip):
"""Valideer het IP-adres tegen een whitelist of blacklist."""
# Voorbeeld: een simpele whitelist
whitelist = ['192.168.1.0/24', '123.456.78.90']
try:
ip = ipaddress.ip_address(client_ip)
for allowed_network in whitelist:
if ip in ipaddress.ip_network(allowed_network):
return True
return False # IP niet in whitelist
except ValueError:
return False # Ongeldig IP-adres
# --- DE MIDDLEWARE HOOK ---
@app.before_request
def validate_request():
"""Deze functie wordt voor elke request uitgevoerd."""
# 1. IP Validatie
client_ip = request.remote_addr
if not validate_ip(client_ip):
abort(403, description="Toegang geweigerd: IP-adres niet geautoriseerd.")
# 2. Header en JSON Payload Validatie
validator = RequestValidator(request)
if not validator.validate():
# Geef een duidelijke foutmelding terug met de validatiefouten
abort(400, description=f"Ongeldige request: {validator.errors}")
# --- Je normale route ---
@app.route('/api/data', methods=['POST'])
def handle_data():
# Je bent hier zeker dat de request valide is!
data = request.get_json()
return jsonify({"status": "success", "received_data": data}), 200
if __name__ == '__main__':
app.run(debug=True)
```
#### Optie B: Met Echte WSGI Middleware (Krachtiger)
Dit is een abstractere maar krachtigere methode. Je maakt een class die de `__call__` methode implementeert.
```python
class ValidationMiddleware:
def __init__(self, app):
self.app = app
def __call__(self, environ, start_response):
# Omgeving naar een request object voor gemak (vereist Werkzeug)
request = Request(environ)
# Simpele IP check (zelfde logica als hierboven)
client_ip = request.remote_addr
if not self.validate_ip(client_ip):
# Direct een error response sturen
response = Response("IP niet geautoriseerd", status=403, mimetype='text/plain')
return response(environ, start_response)
# Voor header/payload validatie moet je de body lezen.
# Dit is complexer omdat je de stream niet wilt verbruiken voor de hoofd-app.
# Vaak wordt dit daarom gecombineerd met `before_request` voor payload checks.
# Geef de request door aan de volgende laag (Flask app) als alles oké is.
return self.app(environ, start_response)
def validate_ip(self, client_ip):
#... (Zelfde implementatie als hierboven)
pass
# Hoe je het initialiseert:
app = Flask(__name__)
app.wsgi_app = ValidationMiddleware(app.wsgi_app) # Wrap de Flask app met je middleware
```
---
### Deel 2: Rate Limiting Implementeren
Voor rate limiting is het veel efficiënter om een bestaande, robuuste library te gebruiken dan het zelf te bouwen. `Flask-Limiter` is de standaardkeuze.
1. **Installeer Flask-Limiter:**
```bash
pip install flask-limiter
```
2. **Basis Implementatie:**
```python
from flask_limiter import Limiter
from flask_limiter.util import get_remote_address
# Initialiseer de Limiter
# 'get_remote_address' haalt het client IP op om te gebruiken als sleutel voor limieten.
limiter = Limiter(
get_remote_address,
app=app, # Je Flask app object
storage_uri="memory://", # Sla limietgegevens op in het geheugen. Gebruik Redis voor productie!
default_limits=["200 per day", "50 per hour"] # Globale standaardlimieten
)
# Pas limieten toe op een specifieke route
@app.route('/api/data', methods=['POST'])
@limiter.limit("10 per minute") # Max 10 requests per minuut voor deze endpoint
def handle_data():
data = request.get_json()
return jsonify({"status": "success", "received_data": data}), 200
# Je kunt ook limieten per gebruiker, API key, etc. instellen met een `key_func`
def get_api_key():
return request.headers.get('X-Api-Key') or get_remote_address()
limiter_with_key = Limiter(
get_api_key,
app=app,
storage_uri="memory://",
)
@app.route('/api/sensitive', methods=['GET'])
@limiter_with_key.limit("100 per hour")
def sensitive_data():
return jsonify({"secret": "data"})
```
3. **Belangrijke Note over Storage:**
* `storage_uri="memory://"` is goed voor ontwikkeling, maar **niet voor productie**. Bij meerdere worker processen (bv. met Gunicorn) werkt het niet correct.
* Voor productie, gebruik **Redis** of Memcached:
```python
# Voor Redis
limiter = Limiter(
get_remote_address,
app=app,
storage_uri="redis://localhost:6379", # Redis server adres
storage_options={"socket_connect_timeout": 30},
strategy="fixed-window" # of "moving-window"
)
```
---
### Conclusie & Volledige Setup
Combineer beide technieken voor een robuuste applicatie. Je `before_request` hook handelt de validatie af, en `Flask-Limiter` handelt de rate limiting af voordat je view functie überhaupt wordt uitgevoerd.
**Je uiteindelijke workflow ziet er zo uit:**
1. Request komt binnen bij de server.
2. **Flask-Limiter Middleware:** Controleert het rate limit. Is het overschreden? -> **429 Too Many Requests**.
3. **Jouw `before_request` functie:** Valideert IP -> Valideert Headers -> Valideert JSON Payload. Faalt één van deze? -> **4xx Fout**.
4. Pas na al deze checks wordt je daadwerkelijke `@app.route` functie uitgevoerd.
Dit patroon zorgt voor een veilige, betrouwbare en professionele API.