Implementare la protezione CSRF da zero in Python

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.

Torna su