DDoS in soccorso: come conduciamo stress e test di carico

DDoS in soccorso: come conduciamo stress e test di carico

Variti sviluppa protezione contro bot e attacchi DDoS e conduce anche test di stress e carico. Alla conferenza HighLoad++ 2018 abbiamo parlato di come proteggere le risorse da vari tipi di attacchi. In breve: isolare parti del sistema, utilizzare servizi cloud e CDN e aggiornare regolarmente. Ma non sarai comunque in grado di gestire la protezione senza aziende specializzate :)

Prima di leggere il testo è possibile leggere i brevi abstract sul sito della conferenza.
E se non vi piace leggere o volete semplicemente guardare il video, la registrazione del nostro reportage è qui sotto sotto spoiler.

Registrazione video della relazione

Molte aziende sanno già come eseguire i test di carico, ma non tutte eseguono gli stress test. Alcuni dei nostri clienti pensano che il loro sito sia invulnerabile perché ha un sistema ad alto carico e protegge bene dagli attacchi. Mostriamo che questo non è del tutto vero.
Naturalmente prima di effettuare i test chiediamo l'autorizzazione del cliente, firmata e timbrata, e con il nostro aiuto non è possibile sferrare un attacco DDoS a nessuno. Il test viene effettuato all'orario scelto dal cliente, quando il traffico verso la sua risorsa è minimo e i problemi di accesso non influiranno sui client. Inoltre, poiché durante il processo di test qualcosa può sempre andare storto, siamo in costante contatto con il cliente. Ciò consente non solo di riportare i risultati raggiunti, ma anche di modificare qualcosa durante il test. Al termine dei test, redigiamo sempre un rapporto in cui segnaliamo le carenze rilevate e forniamo raccomandazioni per eliminare i punti deboli del sito.

Come stiamo lavorando

Durante i test, emuliamo una botnet. Poiché lavoriamo con client che non si trovano sulle nostre reti, per garantire che il test non finisca nel primo minuto a causa di limiti o attivazione di protezioni, alimentiamo il carico non da un IP, ma dalla nostra stessa sottorete. Inoltre, per creare un carico significativo, disponiamo del nostro server di test abbastanza potente.

Postulati

Troppo non significa bene
Meno carico riusciamo a portare una risorsa al fallimento, meglio è. Se riesci a far sì che il sito smetta di funzionare con una richiesta al secondo o anche una richiesta al minuto, è fantastico. Perché secondo la legge della meschinità, gli utenti o gli aggressori cadranno accidentalmente in questa particolare vulnerabilità.

Il fallimento parziale è meglio del fallimento completo
Consigliamo sempre di rendere i sistemi eterogenei. Inoltre, vale la pena separarli a livello fisico e non solo attraverso la containerizzazione. In caso di separazione fisica, anche se qualcosa non funziona sul sito, c'è un'alta probabilità che non smetta di funzionare completamente e gli utenti continuino ad avere accesso almeno a parte delle funzionalità.

Una buona architettura è la base per la sostenibilità
La tolleranza agli errori di una risorsa e la sua capacità di resistere ad attacchi e carichi dovrebbero essere stabilite in fase di progettazione, anzi, nella fase di disegno dei primi diagrammi di flusso su un taccuino. Perché se si insinuano errori fatali, è possibile correggerli in futuro, ma è molto difficile.

Non solo il codice dovrebbe essere buono, ma anche la configurazione
Molte persone pensano che un buon team di sviluppo sia una garanzia di un servizio tollerante ai guasti. Un buon team di sviluppo è davvero necessario, ma devono esserci anche buone operazioni, un buon DevOps. Cioè, abbiamo bisogno di specialisti che configureranno correttamente Linux e la rete, scriveranno correttamente le configurazioni in nginx, imposteranno i limiti, ecc. Altrimenti, la risorsa funzionerà bene solo in fase di test e ad un certo punto tutto si romperà in produzione.

Differenze tra carico e stress test
Il test di carico consente di identificare i limiti di funzionamento del sistema. Lo stress test ha lo scopo di trovare punti deboli in un sistema e viene utilizzato per rompere questo sistema e vedere come si comporterà nel processo di guasto di alcune parti. In questo caso, la natura del carico solitamente rimane sconosciuta al cliente prima che inizi lo stress test.

Caratteristiche distintive degli attacchi L7

Solitamente dividiamo i tipi di carico in carichi ai livelli L7 e L3&4. L7 è un carico a livello di applicazione, molto spesso significa solo HTTP, ma intendiamo qualsiasi carico a livello di protocollo TCP.
Gli attacchi L7 hanno alcune caratteristiche distintive. In primo luogo, arrivano direttamente all'applicazione, ovvero è improbabile che vengano riflessi tramite mezzi di rete. Tali attacchi utilizzano la logica e, per questo motivo, consumano CPU, memoria, disco, database e altre risorse in modo molto efficiente e con poco traffico.

Inondazione HTTP

Nel caso di qualsiasi attacco, il carico è più facile da creare che da gestire, e anche nel caso di L7 questo è vero. Non è sempre facile distinguere il traffico di attacco dal traffico legittimo, e molto spesso ciò può essere fatto in base alla frequenza, ma se tutto è pianificato correttamente, è impossibile capire dai log dove si trova l'attacco e dove si trovano le richieste legittime.
Come primo esempio consideriamo un attacco HTTP Flood. Il grafico mostra che tali attacchi sono solitamente molto potenti; nell’esempio seguente, il numero massimo di richieste ha superato le 600mila al minuto.

DDoS in soccorso: come conduciamo stress e test di carico

HTTP Flood è il modo più semplice per creare un carico. In genere, richiede una sorta di strumento di test del carico, come ApacheBench, e imposta una richiesta e un obiettivo. Con un approccio così semplice, c'è un'alta probabilità di imbattersi nel caching del server, ma è facile aggirarlo. Ad esempio, aggiungendo stringhe casuali alla richiesta, che costringeranno il server a servire costantemente una nuova pagina.
Inoltre, non dimenticare l'agente utente nel processo di creazione di un carico. Molti user-agent dei più diffusi strumenti di test vengono filtrati dagli amministratori di sistema e in questo caso il carico potrebbe semplicemente non raggiungere il backend. Puoi migliorare notevolmente il risultato inserendo nella richiesta un'intestazione più o meno valida dal browser.
Per quanto semplici siano gli attacchi HTTP Flood, hanno anche i loro svantaggi. Innanzitutto, per creare il carico sono necessarie grandi quantità di energia. In secondo luogo, tali attacchi sono molto facili da individuare, soprattutto se provengono da un unico indirizzo. Di conseguenza, le richieste iniziano immediatamente a essere filtrate dagli amministratori di sistema o anche a livello di provider.

Cosa cercare

Per ridurre il numero di richieste al secondo senza perdere in efficienza, devi mostrare un po' di fantasia ed esplorare il sito. Pertanto, è possibile caricare non solo il canale o il server, ma anche singole parti dell'applicazione, ad esempio database o file system. Puoi anche cercare punti sul sito che eseguono calcoli di grandi dimensioni: calcolatrici, pagine di selezione dei prodotti, ecc. Infine, capita spesso che il sito abbia una sorta di script PHP che genera una pagina di diverse centinaia di migliaia di righe. Inoltre, uno script di questo tipo carica notevolmente il server e può diventare un bersaglio per un attacco.

Dove cercare

Quando eseguiamo la scansione di una risorsa prima del test, guardiamo prima, ovviamente, al sito stesso. Cerchiamo tutti i tipi di campi di input, file pesanti - in generale, tutto ciò che può creare problemi alla risorsa e rallentarne il funzionamento. Gli strumenti di sviluppo banali in Google Chrome e Firefox aiutano qui, mostrando i tempi di risposta della pagina.
Scansioniamo anche i sottodomini. Ad esempio, esiste un determinato negozio online, abc.com, e ha un sottodominio admin.abc.com. Molto probabilmente, questo è un pannello di amministrazione con autorizzazione, ma se lo carichi, può creare problemi alla risorsa principale.
Il sito può avere un sottodominio api.abc.com. Molto probabilmente, questa è una risorsa per le applicazioni mobili. L'applicazione può essere trovata nell'App Store o in Google Play, installa un punto di accesso speciale, analizza l'API e registra account di prova. Il problema è che le persone spesso pensano che tutto ciò che è protetto dall’autorizzazione sia immune agli attacchi di negazione del servizio. Presumibilmente, l'autorizzazione è il miglior CAPTCHA, ma non lo è. È facile creare 10-20 account di prova, ma creandoli otteniamo l'accesso a funzionalità complesse e non mascherate.
Naturalmente, guardiamo la cronologia, robots.txt e WebArchive, ViewDNS e cerchiamo vecchie versioni della risorsa. A volte capita che gli sviluppatori abbiano lanciato, ad esempio, mail2.yandex.net, ma la vecchia versione, mail.yandex.net, rimane. Questo mail.yandex.net non è più supportato, le risorse di sviluppo non gli vengono assegnate, ma continua a consumare il database. Di conseguenza, utilizzando la vecchia versione, puoi utilizzare efficacemente le risorse del backend e tutto ciò che si trova dietro il layout. Naturalmente, questo non accade sempre, ma lo incontriamo abbastanza spesso.
Naturalmente analizziamo tutti i parametri della richiesta e la struttura dei cookie. Puoi, ad esempio, scaricare un valore in un array JSON all'interno di un cookie, creare molti annidamenti e far funzionare la risorsa per un tempo irragionevolmente lungo.

Cerca carico

La prima cosa che viene in mente quando si ricerca un sito è caricare il database, poiché quasi tutti hanno una ricerca e per quasi tutti, purtroppo, è scarsamente protetta. Per qualche ragione, gli sviluppatori non prestano sufficiente attenzione alla ricerca. Ma c'è una raccomandazione qui: non dovresti effettuare richieste dello stesso tipo, perché potresti riscontrare la memorizzazione nella cache, come nel caso del flusso HTTP.
Anche effettuare query casuali al database non è sempre efficace. È molto meglio creare un elenco di parole chiave pertinenti alla ricerca. Se torniamo all’esempio di un negozio online: diciamo che il sito vende pneumatici per auto e permette di impostare il raggio dei pneumatici, il tipo di auto e altri parametri. Di conseguenza, combinazioni di parole rilevanti costringeranno il database a funzionare in condizioni molto più complesse.
Inoltre, conviene utilizzare l'impaginazione: è molto più difficile che una ricerca restituisca la penultima pagina dei risultati di ricerca rispetto alla prima. Cioè, con l'aiuto dell'impaginazione puoi diversificare leggermente il carico.
L'esempio seguente mostra il carico di ricerca. Si può vedere che fin dal primo secondo del test, ad una velocità di dieci richieste al secondo, il sito si è bloccato e non ha risposto.

DDoS in soccorso: come conduciamo stress e test di carico

Se non c'è ricerca?

Se non viene effettuata alcuna ricerca, ciò non significa che il sito non contenga altri campi di input vulnerabili. Questo campo potrebbe essere un'autorizzazione. Al giorno d'oggi, agli sviluppatori piace creare hash complessi per proteggere il database di accesso da un attacco Rainbow Table. Questo è positivo, ma tali hash consumano molte risorse della CPU. Un grande flusso di false autorizzazioni porta a un guasto del processore e, di conseguenza, il sito smette di funzionare.
La presenza sul sito di tutti i tipi di moduli per commenti e feedback è un motivo per inviare lì testi molto grandi o semplicemente creare un'enorme alluvione. A volte i siti accettano file allegati, anche in formato gzip. In questo caso, prendiamo un file da 1 TB, lo comprimiamo in diversi byte o kilobyte utilizzando gzip e lo inviamo al sito. Quindi si decomprime e si ottiene un effetto molto interessante.

API REST

Vorrei prestare un po' di attenzione a servizi così popolari come l'API Rest. Proteggere un'API Rest è molto più difficile di un normale sito Web. Anche i metodi banali di protezione contro la forza bruta delle password e altre attività illegittime non funzionano per l'API Rest.
L'API Rest è molto facile da violare perché accede direttamente al database. Allo stesso tempo, il fallimento di tale servizio comporta conseguenze piuttosto gravi per le imprese. Il fatto è che l'API Rest viene solitamente utilizzata non solo per il sito Web principale, ma anche per l'applicazione mobile e alcune risorse aziendali interne. E se tutto questo dovesse crollare, l’effetto sarebbe molto più forte che nel caso di un semplice guasto del sito web.

Caricamento di contenuti pesanti

Se ci viene offerto di testare alcune normali applicazioni a pagina singola, pagine di destinazione o siti Web di biglietti da visita che non hanno funzionalità complesse, cerchiamo contenuti pesanti. Ad esempio, immagini di grandi dimensioni inviate dal server, file binari, documentazione PDF: proviamo a scaricare tutto questo. Tali test caricano bene il file system e intasano i canali, e quindi sono efficaci. Cioè, anche se non spegni il server, scaricando un file di grandi dimensioni a bassa velocità, ostruirai semplicemente il canale del server di destinazione e quindi si verificherà un rifiuto di servizio.
Un esempio di tale test mostra che a una velocità di 30 RPS il sito ha smesso di rispondere o ha prodotto il 500esimo errore del server.

DDoS in soccorso: come conduciamo stress e test di carico

Non dimenticare di configurare i server. Spesso puoi scoprire che una persona ha acquistato una macchina virtuale, ha installato Apache lì, ha configurato tutto per impostazione predefinita, ha installato un'applicazione PHP e di seguito puoi vedere il risultato.

DDoS in soccorso: come conduciamo stress e test di carico

Qui il carico è andato alla radice ed è stato di soli 10 RPS. Abbiamo aspettato 5 minuti e il server si è bloccato. È vero che non si sa del tutto perché sia ​​caduto, ma si presume che semplicemente avesse troppa memoria e quindi abbia smesso di rispondere.

Basato sulle onde

Negli ultimi due anni gli attacchi con ondate sono diventati molto popolari. Ciò è dovuto al fatto che molte organizzazioni acquistano determinati componenti hardware per la protezione DDoS, che richiedono un certo periodo di tempo per accumulare statistiche e iniziare a filtrare l'attacco. Cioè non filtrano l’attacco nei primi 30-40 secondi, perché accumulano dati e imparano. Di conseguenza, in questi 30-40 secondi puoi lanciare così tanto sul sito che la risorsa rimarrà a lungo fino a quando tutte le richieste non saranno chiarite.
Nel caso dell'attacco riportato di seguito c'è stato un intervallo di 10 minuti, dopo il quale è arrivata una nuova parte modificata dell'attacco.

DDoS in soccorso: come conduciamo stress e test di carico

Cioè, la difesa ha imparato, ha iniziato a filtrare, ma è arrivata una nuova porzione dell'attacco completamente diversa e la difesa ha ricominciato ad apprendere. Infatti, il filtraggio smette di funzionare, la protezione diventa inefficace e il sito non è disponibile.
Gli attacchi Wave sono caratterizzati da valori di picco molto elevati, si può arrivare a centomila o un milione di richieste al secondo, nel caso di L7. Se parliamo di L3 e 4, allora possono esserci centinaia di gigabit di traffico o, di conseguenza, centinaia di mpps, se contiamo in pacchetti.
Il problema con tali attacchi è la sincronizzazione. Gli attacchi provengono da una botnet e richiedono un elevato grado di sincronizzazione per creare un picco una tantum molto ampio. E questo coordinamento non sempre funziona: a volte il risultato è una sorta di picco parabolico, il che sembra piuttosto patetico.

Non solo HTTP

Oltre all'HTTP su L7, ci piace sfruttare altri protocolli. Di norma, un normale sito Web, in particolare un normale hosting, ha protocolli di posta e MySQL in evidenza. I protocolli di posta sono soggetti a un carico minore rispetto ai database, ma possono anche essere caricati in modo abbastanza efficiente e finire con una CPU sovraccarica sul server.
Abbiamo avuto abbastanza successo sfruttando la vulnerabilità SSH del 2016. Ora questa vulnerabilità è stata risolta per quasi tutti, ma ciò non significa che il carico non possa essere inviato a SSH. Potere. C'è semplicemente un enorme carico di autorizzazioni, SSH consuma quasi l'intera CPU del server e quindi il sito web crolla a causa di una o due richieste al secondo. Di conseguenza, queste una o due richieste basate sui log non possono essere distinte da un carico legittimo.
Anche molte connessioni che apriamo nei server rimangono rilevanti. In precedenza Apache era colpevole di questo, ora nginx lo è effettivamente, poiché spesso è configurato per impostazione predefinita. Il numero di connessioni che nginx può mantenere aperte è limitato, quindi apriamo questo numero di connessioni, nginx non accetta più una nuova connessione e di conseguenza il sito non funziona.
Il nostro cluster di test ha una CPU sufficiente per attaccare l'handshake SSL. In linea di principio, come dimostra la pratica, anche alle botnet a volte piace fare questo. Da un lato è chiaro che non si può fare a meno di SSL, perché risultati di Google, ranking, sicurezza. D'altra parte, SSL purtroppo ha un problema con la CPU.

L3 e 4

Quando parliamo di attacco ai livelli L3&4, di solito parliamo di un attacco a livello di collegamento. Un carico di questo tipo è quasi sempre distinguibile da uno legittimo, a meno che non si tratti di un attacco SYN-flood. Il problema con gli attacchi SYN-flood contro gli strumenti di sicurezza è il loro grande volume. Il valore massimo L3&4 era 1,5-2 Tbit/s. Questo tipo di traffico è molto difficile da elaborare anche per le grandi aziende, tra cui Oracle e Google.
SYN e SYN-ACK sono pacchetti che vengono utilizzati quando si stabilisce una connessione. Pertanto, il SYN-flood è difficile da distinguere da un carico legittimo: non è chiaro se si tratti di un SYN venuto per stabilire una connessione, o di parte di un diluvio.

UDP-alluvione

In genere, gli aggressori non hanno le capacità di cui disponiamo noi, quindi è possibile utilizzare l’amplificazione per organizzare gli attacchi. Cioè, l'aggressore scansiona Internet e trova server vulnerabili o configurati in modo errato che, ad esempio, in risposta a un pacchetto SYN, rispondono con tre SYN-ACK. Effettuando lo spoofing dell'indirizzo di origine dall'indirizzo del server di destinazione, è possibile aumentare la potenza, ad esempio, di tre volte con un singolo pacchetto e reindirizzare il traffico alla vittima.

DDoS in soccorso: come conduciamo stress e test di carico

Il problema con le amplificazioni è che sono difficili da rilevare. Esempi recenti includono il caso clamoroso del vulnerabile memcached. Inoltre, ora ci sono molti dispositivi IoT, telecamere IP, che sono per lo più configurati per impostazione predefinita e per impostazione predefinita sono configurati in modo errato, motivo per cui gli aggressori molto spesso effettuano attacchi tramite tali dispositivi.

DDoS in soccorso: come conduciamo stress e test di carico

Difficile SYN-flood

Il SYN-flood è probabilmente il tipo di attacco più interessante dal punto di vista di uno sviluppatore. Il problema è che gli amministratori di sistema spesso utilizzano il blocco IP per la protezione. Inoltre, il blocco dell'IP colpisce non solo gli amministratori di sistema che agiscono tramite script, ma, sfortunatamente, anche alcuni sistemi di sicurezza acquistati per ingenti somme di denaro.
Questo metodo può trasformarsi in un disastro, perché se gli aggressori sostituiscono gli indirizzi IP, l’azienda bloccherà la propria sottorete. Quando il Firewall blocca il proprio cluster, l'output fallirà le interazioni esterne e la risorsa fallirà.
Inoltre, non è difficile bloccare la propria rete. Se l'ufficio del cliente dispone di una rete Wi-Fi o se le prestazioni delle risorse vengono misurate utilizzando vari sistemi di monitoraggio, prendiamo l'indirizzo IP di questo sistema di monitoraggio o del Wi-Fi dell'ufficio del cliente e lo utilizziamo come fonte. Alla fine la risorsa sembra essere disponibile, ma gli indirizzi IP di destinazione sono bloccati. Pertanto, la rete Wi-Fi della conferenza HighLoad, dove viene presentato il nuovo prodotto dell'azienda, potrebbe essere bloccata, con conseguenti costi aziendali ed economici.
Durante i test, non possiamo utilizzare l'amplificazione tramite memcached con risorse esterne, poiché esistono accordi per inviare traffico solo agli indirizzi IP consentiti. Di conseguenza, utilizziamo l'amplificazione tramite SYN e SYN-ACK, quando il sistema risponde all'invio di un SYN con due o tre SYN-ACK e in uscita l'attacco viene moltiplicato per due o tre volte.

Strumenti

Uno degli strumenti principali che utilizziamo per il carico di lavoro L7 è Yandex-tank. In particolare, un fantasma viene utilizzato come pistola, inoltre esistono diversi script per generare cartucce e per analizzare i risultati.
Tcpdump viene utilizzato per analizzare il traffico di rete e Nmap viene utilizzato per analizzare il server. Per creare il carico a livello L3&4, vengono utilizzati OpenSSL e un po' della nostra magia con la libreria DPDK. DPDK è una libreria Intel che consente di lavorare con l'interfaccia di rete bypassando lo stack Linux, aumentando così l'efficienza. Naturalmente utilizziamo DPDK non solo a livello L3&4, ma anche a livello L7, perché ci consente di creare un flusso di carico molto elevato, nell'ordine di diversi milioni di richieste al secondo da una macchina.
Utilizziamo anche determinati generatori di traffico e strumenti speciali che scriviamo per test specifici. Se ricordiamo la vulnerabilità in SSH, il set di cui sopra non può essere sfruttato. Se attacchiamo il protocollo di posta, prendiamo le utilità di posta o semplicemente scriviamo degli script su di esse.

risultati

In conclusione vorrei dire:

  • Oltre alle classiche prove di carico, è necessario condurre prove di stress. Abbiamo un esempio reale in cui il subappaltatore di un partner ha eseguito solo test di carico. Ha dimostrato che la risorsa può sopportare il carico normale. Ma poi è apparso un carico anomalo, i visitatori del sito hanno iniziato a utilizzare la risorsa in modo leggermente diverso e, di conseguenza, il subappaltatore si è arreso. Vale quindi la pena cercare le vulnerabilità anche se si è già protetti dagli attacchi DDoS.
  • È necessario isolare alcune parti del sistema dalle altre. Se hai una ricerca, devi spostarla su macchine separate, cioè nemmeno su Docker. Perché se la ricerca o l'autorizzazione falliscono, almeno qualcosa continuerà a funzionare. Nel caso di un negozio online, gli utenti continueranno a cercare i prodotti nel catalogo, a passare dall'aggregatore, ad acquistare se già autorizzati o ad autorizzarsi tramite OAuth2.
  • Non trascurare tutti i tipi di servizi cloud.
  • Utilizza la CDN non solo per ottimizzare i ritardi di rete, ma anche come mezzo di protezione contro gli attacchi all'esaurimento del canale e il semplice inondamento di traffico statico.
  • È necessario utilizzare servizi di protezione specializzati. Non puoi proteggerti dagli attacchi L3 e 4 a livello di canale, perché molto probabilmente semplicemente non hai un canale sufficiente. È anche improbabile che tu possa respingere gli attacchi L7, poiché possono essere molto grandi. Inoltre, la ricerca di piccoli attacchi è ancora prerogativa di servizi speciali, algoritmi speciali.
  • Aggiorna regolarmente. Questo vale non solo per il kernel, ma anche per il demone SSH, soprattutto se li avete aperti verso l'esterno. In linea di principio, tutto deve essere aggiornato, perché difficilmente sarai in grado di monitorare da solo alcune vulnerabilità.

Fonte: habr.com

Aggiungi un commento