La protezione CSRF (Cross-Site Request Forgery) è una misura di sicurezza fondamentale per qualsiasi applicazione web che gestisce richieste stateful e autenticazione utente. In questo articolo vedremo come implementarla da zero utilizzando Python e un semplice server HTTP.
1. Cos'è un Token CSRF
Un token CSRF è un valore univoco e imprevedibile generato dal server e associato alla sessione dell'utente. Questo token viene incluso nei moduli HTML e inviato con ogni richiesta che modifica lo stato sul server. Se il token ricevuto non corrisponde a quello salvato nella sessione, la richiesta viene rifiutata.
2. Setup del Server
Creiamo un semplice server HTTP usando http.server
e memorizziamo i token CSRF nella sessione utente tramite cookie.
import http.server
import http.cookies
import secrets
import urllib.parse
csrf_tokens = {}
class SimpleHandler(http.server.BaseHTTPRequestHandler):
def do_GET(self):
session_id = self.get_or_create_session()
token = self.generate_csrf_token(session_id)
self.send_response(200)
self.send_header("Content-type", "text/html")
self.end_headers()
self.wfile.write(self.render_form(token).encode())
def do_POST(self):
session_id = self.get_or_create_session()
content_length = int(self.headers.get('Content-Length', 0))
body = self.rfile.read(content_length).decode()
post_data = urllib.parse.parse_qs(body)
token = post_data.get("csrf_token", [""])[0]
if not self.validate_csrf_token(session_id, token):
self.send_response(403)
self.end_headers()
self.wfile.write(b"Forbidden: Invalid CSRF token")
return
self.send_response(200)
self.end_headers()
self.wfile.write(b"Form submitted successfully!")
def get_or_create_session(self):
cookie = http.cookies.SimpleCookie(self.headers.get("Cookie"))
if "session_id" in cookie:
return cookie["session_id"].value
session_id = secrets.token_hex(16)
self.send_header("Set-Cookie", f"session_id={session_id}")
return session_id
def generate_csrf_token(self, session_id):
token = secrets.token_hex(32)
csrf_tokens[session_id] = token
return token
def validate_csrf_token(self, session_id, token):
return csrf_tokens.get(session_id) == token
def render_form(self, token):
return f"""
<!DOCTYPE html>
<html>
<body>
<form method="POST">
<input type="hidden" name="csrf_token" value="{token}">
<input type="text" name="data">
<input type="submit" value="Submit">
</form>
</body>
</html>
"""
if __name__ == "__main__":
http.server.HTTPServer(("localhost", 8080), SimpleHandler).serve_forever()
3. Funzionamento
- Alla prima richiesta, il server genera un session_id e un csrf_token, salvandoli rispettivamente in un cookie e in memoria.
- Il token viene incluso come campo nascosto nel form HTML.
- Quando l'utente invia il form, il server confronta il token ricevuto con quello salvato per quella sessione.
- Se il token è valido, l’azione è accettata; altrimenti, viene restituito un errore.
4. Considerazioni di Sicurezza
- Questa implementazione è didattica e non scalabile per ambienti di produzione.
- È consigliabile usare librerie mature per gestire la sessione e i token.
- Proteggere i cookie con l’attributo
HttpOnly
e, se possibile,Secure
.
Conclusione
Abbiamo visto come implementare una protezione CSRF da zero in Python utilizzando semplici tecniche. Anche se esistono soluzioni più sofisticate, comprendere come funziona la protezione CSRF aiuta a scrivere codice più sicuro e consapevole.