Race Condition (sicurezza)

Indice dei contenuti

    Una race condition in sicurezza informatica è una vulnerabilità che emerge quando un sistema esegue operazioni in sequenza assumendo che nessuna altra operazione intervenga nel frattempo — ma un attaccante riesce a inserirsi nella finestra temporale tra due passi per alterare il risultato. La finestra è spesso dell’ordine dei millisecondi, ma con strumenti automatizzati è sfruttabile in modo affidabile.

    La forma classica è il Time-of-Check to Time-of-Use (TOCTOU): il sistema verifica una condizione (check) e successivamente la utilizza (use), ma tra i due momenti lo stato del sistema cambia.

    Pattern TOCTOU

    # Codice vulnerabile — pattern TOCTOU
    def preleva(utente_id, importo):
        saldo = db.query("SELECT saldo FROM conti WHERE id = ?", utente_id)
        
        if saldo >= importo:          # CHECK: verifica saldo
            # finestra vulnerabile — un'altra richiesta può arrivare qui
            db.execute("UPDATE conti SET saldo = saldo - ? WHERE id = ?",
                       importo, utente_id)   # USE: scala l'importo
            return "Prelievo riuscito"
        return "Saldo insufficiente"

    Se due richieste di prelievo arrivano simultaneamente, entrambe leggono il saldo originale, entrambe superano il controllo, e il conto viene scalato due volte nonostante il saldo fosse sufficiente per una sola operazione.

    Double-spending

    Il caso più noto è il double-spending: un utente con credito 100 invia due richieste di acquisto da 100 in parallelo. Entrambe leggono il saldo prima che una delle due lo aggiorni — entrambe ottengono due beni con un credito solo.

    In ambito fintech, criptovalute e sistemi di punti fedeltà, questa vulnerabilità ha causato perdite significative.

    Limit bypass

    Un attaccante può bypassare limiti applicativi sfruttando la race condition:

    • Coupon monouso: applicare lo stesso codice sconto più volte in parallelo.
    • Rate limiting: superare il numero massimo di richieste consentite inviandole simultaneamente prima che il contatore venga aggiornato.
    • Verifica email unica: creare più account con la stessa email inviando le richieste in parallelo, prima che il controllo di unicità sia stato scritto nel database.

    Race condition su filesystem

    In ambiente Unix, la sequenza:

    // Vulnerabile TOCTOU su file
    if (access("/tmp/file_sicuro", W_OK) == 0) {   // CHECK
        // attaccante crea symlink qui
        fd = open("/tmp/file_sicuro", O_WRONLY);    // USE
        write(fd, dati_privilegiati, len);
    }

    L’attaccante sostituisce /tmp/file_sicuro con un symlink verso /etc/passwd nella finestra tra access() e open(). Il processo privilegiato scrive in /etc/passwd invece del file previsto.

    Race condition nelle sessioni

    Se la generazione di un token di sessione non è atomica (leggi-incrementa-scrivi separati), due richieste simultanee possono ottenere lo stesso token — portando alla condivisione involontaria di sessione tra utenti diversi.

    Tecniche di sfruttamento

    L’attaccante sincronizza le richieste per massimizzare la sovrapposizione temporale:

    • Last-byte synchronization: inviare richieste HTTP parziali (tutti i byte tranne l’ultimo), poi inviare l’ultimo byte di tutte simultaneamente — il server le elabora quasi in parallelo.
    • Connessioni multiple HTTP/1.1: aprire più connessioni TCP e inviare richieste in parallelo.
    • HTTP/2 single-packet attack: HTTP/2 permette di multiplexare più richieste sullo stesso frame TCP — arrivano al server nello stesso istante fisico, eliminando quasi del tutto la variabilità di rete.

    Contromisure

    Operazioni atomiche nel database: usare transazioni con livello di isolamento adeguato e aggiornamenti condizionali:

    -- Atomico: scala solo se il saldo è sufficiente
    UPDATE conti
    SET saldo = saldo - 100
    WHERE id = 42 AND saldo >= 100;
    -- Verificare che affected_rows = 1

    Lock pessimistico: acquisire un lock esplicito prima del check:

    BEGIN;
    SELECT saldo FROM conti WHERE id = 42 FOR UPDATE;  -- lock esclusivo
    -- check e update sicuri
    COMMIT;

    Lock ottimistico (versioning): aggiungere un campo version al record e verificare che non sia cambiato tra check e update:

    UPDATE conti SET saldo = saldo - 100, version = version + 1
    WHERE id = 42 AND version = 7;
    -- Se affected_rows = 0, il record è stato modificato: retry

    Idempotency key: per operazioni finanziarie, richiedere un identificatore univoco per richiesta — il server rifiuta duplicati della stessa chiave.

    Relazione con altri argomenti

    • OWASP Top 10 — A04 Insecure Design quando la race condition è architetturale.
    • Privilege Escalation — le race condition su filesystem sono spesso usate per escalation locale.
    • Vulnerabilità Informatica — classificata CWE-362 (Concurrent Execution Using Shared Resource with Improper Synchronization).

    Ultimo aggiornamento: