Implementare la protezione CSRF in Go da zero

La Cross-Site Request Forgery (CSRF) è una vulnerabilità che consente a un attaccante di indurre un utente autenticato a eseguire azioni indesiderate su un'applicazione web. In questo articolo vedremo come implementare una semplice protezione CSRF da zero in Go.

1. Concetto base

Il principio è semplice: per ogni richiesta mutante (POST, PUT, DELETE), si richiede un token CSRF che deve essere inviato dall'utente e verificato sul server.

2. Generazione del token

Il token CSRF viene generato e memorizzato nella sessione dell'utente. Viene anche incluso nei form HTML.

func generateCSRFToken() (string, error) {
    b := make([]byte, 32)
    if _, err := rand.Read(b); err != nil {
        return "", err
    }
    return base64.URLEncoding.EncodeToString(b), nil
}

3. Aggiunta del token alla sessione

Quando si crea una nuova sessione o si accede a una pagina protetta, si genera il token CSRF se non esiste già.

func ensureCSRFToken(w http.ResponseWriter, r *http.Request) string {
    session, _ := store.Get(r, "session-name")
    token, ok := session.Values["csrf_token"].(string)
    if !ok || token == "" {
        token, _ = generateCSRFToken()
        session.Values["csrf_token"] = token
        session.Save(r, w)
    }
    return token
}

4. Inclusione del token nel form HTML

Il token CSRF va incluso come campo nascosto in ogni form HTML.

<form method="POST" action="/submit">
  <input type="hidden" name="csrf_token" value="{{.CSRFToken}}">
  <input type="text" name="message">
  <button type="submit">Invia</button>
</form>

5. Verifica del token lato server

Al momento della ricezione della richiesta, si confronta il token inviato con quello salvato nella sessione.

func validateCSRFToken(r *http.Request) bool {
    session, _ := store.Get(r, "session-name")
    storedToken, _ := session.Values["csrf_token"].(string)
    submittedToken := r.FormValue("csrf_token")
    return storedToken != "" && submittedToken == storedToken
}

6. Applicazione pratica

All'interno del tuo handler POST, verifica il token prima di procedere:

func handleSubmit(w http.ResponseWriter, r *http.Request) {
    if !validateCSRFToken(r) {
        http.Error(w, "Invalid CSRF token", http.StatusForbidden)
        return
    }
    // continua con l'elaborazione della richiesta
}

Conclusione

Abbiamo visto come costruire una semplice ma efficace protezione CSRF in Go. Questo approccio può essere ulteriormente migliorato utilizzando token "double submit" o integrandosi con librerie di sessione più avanzate.

Torna su