Il server è sotto attacco di uno scraper (load a 37): come bloccarlo con .htaccess senza bloccare i clienti veri

di Luca Cavallini Casi di assistenza
Il server è sotto attacco di uno scraper (load a 37): come bloccarlo con .htaccess senza bloccare i clienti veri

Il sintomo

Il carico del server (il load average, il numero che riassume quante richieste sono in coda per essere processate) sale in pochi minuti a un valore fuori scala: da un paio di unità, normali su un negozio in salute, a oltre 30. Il sito risponde a scatti, le pagine impiegano secondi a caricare quando prima erano immediate, e in alcuni momenti smette di rispondere del tutto. Non c'è stato nessun deploy, nessuna modifica al catalogo, nessuna campagna pubblicitaria lanciata in quel momento: il traffico "normale" delle persone vere non è cambiato, ma qualcos'altro si è aggiunto sopra.

Guardando i log di accesso in tempo reale (tail -f sul log di Apache o Nginx, filtrato sui minuti più recenti), la causa è evidente in pochi secondi: centinaia di richieste al minuto verso pagine di categoria e schede prodotto, provenienti da una manciata di indirizzi IP che appartengono a un provider di cloud computing, non a connessioni residenziali o mobili. È uno scraper, cioè un programma che scarica sistematicamente il catalogo, non un picco di visitatori.

Cosa sembrava e non era

La prima idea, quella più immediata, è bloccare gli indirizzi IP che compaiono nei log: sembra la soluzione più diretta, e per i primi minuti sembra anche funzionare, il carico scende non appena quegli IP smettono di rispondere. Il problema si ripresenta poco dopo: lo scraper è coordinato su più indirizzi dello stesso provider cloud, a rotazione, e ogni IP bloccato viene sostituito da uno nuovo nel giro di minuti. Bloccare IP uno per volta, in questo scenario, è una rincorsa che non si vince mai: si passa il tempo ad aggiungere righe a una lista che lo scraper aggira semplicemente cambiando IP.

C'è però un dettaglio che la rotazione degli IP non nasconde: tutte le richieste dello scraper, indipendentemente da quale IP le fa in un dato momento, dichiarano lo stesso, ristretto gruppo di identità del browser (lo user agent), e per giunta versioni datate di qualche anno di Chrome, il tipo di stringa che un browser vero aggiorna automaticamente e che quindi non ci si aspetta più di vedere in giro. La rotazione copre l'indirizzo, non la firma.

La causa vera e la scelta di blocco

Il blocco efficace, in questo scenario, combina due criteri nella stessa regola, in testa al file .htaccess del negozio, subito dopo RewriteEngine on, così che la richiesta venga respinta da Apache prima che arrivi a PrestaShop e al database: è la differenza tra rispondere con un errore a costo quasi zero e far comunque eseguire tutta la logica applicativa a una richiesta che verrà comunque scartata.

# IP noti del cloud provider coinvolto
RewriteCond %{REMOTE_ADDR} ^203\.0\.113\.10$ [OR]
RewriteCond %{REMOTE_ADDR} ^203\.0\.113\.11$
RewriteRule ^ - [F,L]

# Firma User-Agent dello scraper (build Chrome datate, usate su IP a rotazione)
RewriteCond %{HTTP_USER_AGENT} "Chrome/87\.0\.4280\.141" [OR]
RewriteCond %{HTTP_USER_AGENT} "Chrome/61\.0\.3163\.100" [OR]
RewriteCond %{HTTP_USER_AGENT} "Chrome/74\.0\.3729\.169"
RewriteRule ^ - [F,L]

(Gli indirizzi qui sopra sono di esempio: nel caso reale erano quattro IP di un unico provider cloud, individuati dai log del momento. Vanno sempre ricavati dal traffico effettivo, non copiati da una lista trovata altrove.)

Il blocco per firma è quello che regge davvero alla rotazione: anche quando lo scraper cambia IP, la stringa dello user agent resta la stessa finché non la cambiano anche loro, e a quel punto serve aggiornare la regola, non inseguire indirizzi singoli uno alla volta.

Il rischio vero di questo tipo di blocco

Bloccare per firma, non solo per indirizzo, è più efficace, ma alza anche il rischio di un effetto collaterale che nessuno controlla finché non gli succede: colpire un cliente vero il cui dispositivo dichiara, per conto suo, qualcosa che assomiglia alla firma bloccata. Non è un rischio teorico: sullo stesso negozio, con un filtro diverso ma con la stessa logica di fondo (riconoscere programmi automatici da una stringa), la prima versione di quella regola prendeva anche il nome di un marchio di telefono legittimo, e PrestaShop, quando ritiene una richiesta fatta da un robot, salta in silenzio l'aggiunta al carrello: il cliente vedeva "fatto", il carrello restava vuoto, nessun errore in nessun log. La storia completa, con i numeri di quel caso, è in questo articolo: vale la pena leggerla prima di scrivere un blocco per firma, perché è il pericolo concreto di questa tecnica, e quasi nessuno lo controlla prima di attivarla.

Per questo, prima di attivare un blocco per firma, va verificato esplicitamente che non intercetti traffico legittimo. Nel caso che ha portato a questa regola, tre categorie di traffico sono state controllate una per una prima di considerare il blocco sicuro:

  • l'accesso al back office da una sessione amministrativa già autenticata (deve restare fuori dal blocco: un firewall a livello di .htaccess non distingue un amministratore loggato da un visitatore anonimo, se non per l'URL a cui accede);
  • un accesso reale da smartphone, con lo user agent originale del browser mobile (deve restare fuori: un blocco troppo largo su "versioni vecchie di Chrome" può prendere anche browser mobili legittimi con versioni diverse dal previsto);
  • le richieste automatiche legittime di un servizio terzo integrato col negozio, per esempio un corriere o un sistema di pagamento che interroga le API del negozio con il proprio user agent dichiarato (deve restare fuori: è automazione, ma è automazione voluta).

Solo dopo aver verificato che nessuna di queste tre categorie finiva nel blocco, la regola è stata considerata sicura da tenere attiva.

Come verificare che tenga

  • Da riga di comando, con curl, provare una richiesta dichiarando uno degli user agent bloccati: deve tornare 403. Provarne una con uno user agent normale e aggiornato: deve tornare 200, sulla home, su una categoria, su una scheda prodotto.
  • Seguire il carico del server nei minuti successivi all'attivazione del blocco: deve scendere in modo continuo, non a scatti, se lo scraper non ha ancora cambiato IP o firma. Un ritorno del carico dopo un calo iniziale è il segnale che la firma è cambiata e la regola va aggiornata.
  • Controllare nei log, per qualche giorno dopo l'attivazione, che le tre categorie di traffico legittimo elencate sopra continuino a funzionare regolarmente: è il controllo che davvero previene il problema del cliente bloccato per errore, non una verifica da fare una sola volta e poi dimenticare.
  • Tenere un backup del file .htaccess precedente al blocco, con un rollback pronto (una copia vera, non un comando a memoria): se qualcosa va storto, il modo più veloce per tornare a un negozio funzionante è ripristinare il file, non correggere la regola sotto pressione con il sito giù.

Il negozio su cui è stato applicato questo blocco resta anonimo di proposito. Un blocco a livello di .htaccess, per IP e per firma, è l'intervento giusto quando un server è sotto attacco adesso e serve un sollievo immediato: costa pochissimo, perché risponde prima che PrestaShop debba fare nulla. Ma è un intervento chirurgico, non definitivo: se cambiano completamente sia gli indirizzi sia le firme, serve un approccio diverso (per esempio un blocco basato sulla frequenza delle richieste, non sulla loro identità). E soprattutto, prima di attivarlo, va sempre verificato su cosa NON deve cadere, non solo su cosa deve fermare. Chi si occupa di interventi urgenti di questo tipo su un negozio PrestaShop trova un riepilogo dei servizi su questa pagina.

Lascia un commento

I commenti sono moderati prima della pubblicazione.

← Blog

Caricamento in corso ...
Torna all'inizio