Objem není jediná cesta, jak shodit server
Když si kdokoli představí DDoS útok, představí si záplavu: gigabity za sekundu, graf mířící svisle vzhůru, rouru zaplněnou tak, že se do ní nic dalšího nevejde. Ta představa sedí na objemové útoky a je důvodem, proč je většina obran — a většina dashboardů — postavená na hledání špičky.
Pomalý útok žádnou špičku neudělá. Míří na úplně jiný limit: kolik požadavků může váš server zpracovávat naráz. Každý webový server má konečný pool workerů, vláken, procesů nebo file deskriptorů a každý z nich je obsazený po celou dobu, kdy je požadavek nedokončený. Útočníkovi, který dokáže držet požadavek nedokončený navždy, stačí tolik spojení, aby ten pool zaplnil. U výchozí konfigurace originu jde často o stovky — dost málo na to, aby to zvládl jeden stroj na domácí přípojce.
Výsledkem je výpadek, který vypadá jako chyba v aplikaci. Web se zasekne nebo vyprší, serveru nedochází CPU, přenos se drží na běžné hladině a access log ztichl — protože požadavky, které vás zabíjejí, nebyly nikdy dokončeny, takže se nikdy nezalogovaly.
Tři rodiny pomalých útoků
Všechny zneužívají tentýž předpoklad: že klient, který otevřel spojení, ho hodlá také dokončit.
| Útok | Co odkapává | Co vyčerpá | Klasický cíl |
|---|---|---|---|
| Slowloris (pomalé hlavičky) | Jednu řádku hlavičky za pár sekund, nikdy prázdný řádek ukončující požadavek | Pool spojení a workerů | Servery s vláknem na spojení |
| R.U.D.Y. (pomalý POST) | Tělo požadavku po bajtech pod velkým deklarovaným Content-Length | Workery zpracovávající požadavky, aplikační vlákna | Jakýkoli endpoint přijímající POST — formuláře, uploady, API |
| Slow read | Nic — inzeruje maličké TCP okno a odpověď odsává pomalu | Odesílací buffery, sloty spojení, paměť | Endpointy vracející velké odpovědi |
Slowloris otevře mnoho spojení a na každém pošle platný, ale neúplný požadavek: řádek požadavku, jednu hlavičku, pak ticho. Těsně předtím, než vyprší serverový timeout na hlavičky, pošle další řádku hlavičky. Požadavek se nikdy neuzavře, takže server zdvořile čeká dál a spojení zůstává obsazené tak dlouho, jak se útočníkovi chce kapat. OWASP tuto rodinu vede jako slow-rate odepření služby.
R.U.D.Y. („R-U-Dead-Yet“) dělá totéž o krok později. Odešle formulář nebo API volání s velkým deklarovaným Content-Length a pak doručuje tělo po jednom dvou bajtech. Serveru bylo přesně řečeno, kolik dat přijde, takže na ně čeká — a ve většině stacků je aplikační worker tomu požadavku přidělený už během čekání.
Slow read trik obrací. Požadavek je normální a v pořádku dokončený; útočník jen odmítá číst odpověď a inzeruje téměř nulové TCP přijímací okno, takže server nemůže vyprázdnit odesílací buffer ani spojení uvolnit. Když si takhle opakovaně vyžádá váš největší soubor, obsadí kromě slotů i paměť.
Společná mají ekonomiku. Každé spojení stojí útočníka jeden socket a pár bajtů za minutu, zatímco vás stojí jednoho workera. Takový směnný kurz nespraví žádná šířka pásma nahoře.
Které stacky jsou zranitelné
Bez obalu: cokoli, co vyhradí workera spojení na celou dobu požadavku.
- Servery s vláknem nebo procesem na spojení jsou klasické oběti — Apache s MPM prefork a worker, starší konektory Tomcatu a většina aplikačních serverů, které pouštíte přímo na port. Jakmile je otevřeno
MaxRequestWorkersspojení, další skutečný návštěvník stojí ve frontě za nimi. - Událostmi řízené servery — nginx, Node.js, Go a Apache s MPM event — zvládají nečinná spojení mnohem levněji, což je důvod, proč se opakuje, že „nginx je vůči Slowlorisu imunní“. Je to blíž pravdě než lži, ale platí to jen o vstupních dveřích: i nginx má strop
worker_connectionsa file deskriptorů a ve chvíli, kdy požadavek pošle dál, může za ním stát PHP-FPM pool s několika desítkami potomků. Zaplňte ten pool a web je dole bez ohledu na to, jak efektivně proxy čeká. - Aplikační vrstvy s pevnými pooly bývají skutečným úzkým hrdlem: potomci FPM, workeři Puma nebo Unicorn, pool databázových spojení, javovský thread pool. Jsou dimenzované na běžnou souběžnost, což je mnohem menší číslo, než většina lidí předpokládá.
HTTP/2 a HTTP/3 změnily aritmetiku, ale tuhle třídu útoků neposlaly do důchodu. Mnoho pomalých požadavků teď sdílí jedno spojení jako samostatné streamy, takže limity na spojení chytí méně a rozhodujícím stropem se stává limit souběžných streamů a paměť držená na stream. Pomalé chování funguje dál, jen mu stačí méně socketů.
Proč monitoring tvrdí, že je všechno v pořádku
Tahle část stojí týmy nejvíc času, protože metriky, na které se lidé dívají, jsou metriky navržené k odhalení záplavy.
| Signál | Při objemové záplavě | Při pomalém útoku |
|---|---|---|
| Přenos | Saturovaný | Normální, občas i nižší než obvykle |
| Požadavky za sekundu | Vysoko nad běžnou hladinou | Beze změny nebo klesající |
| CPU na originu | Vysoké | Často nečinné — workeři čekají, nepracují |
| Souběžná spojení | Vysoká | Vysoká a soustavně rostoucí |
| Průměrná délka požadavku | Zhruba normální | Roste k hodnotám vašich timeoutů |
| Access log | Zaplavený | Ticho: nedokončené požadavky se nezapisují |
Dívejte se tedy na to správné. Souběžná spojení ve stavu ESTABLISHED na webovém portu, počet zaneprázdněných workerů (Apache server-status, aktivní spojení v nginx stub_status, stav vašeho FPM poolu) a rozložení délky požadavků místo jejího průměru. Pomalý útok vypadá jako dlouhá plochá plošina otevřených spojení, která se nikdy nerozpletou, tence rozprostřená přes mnoho zdrojových adres, z nichž každá posílá zanedbatelný počet bajtů.
Příznak, který nenechá nikoho na pochybách: origin je z prohlížeče nedostupný, ale na nesouvisejícím portu přes SSH odpovídá okamžitě. Nic není přetížené. Všechno je obsazené.
První pomoc na originu: timeouty a limity spojení
Pokud vás drží otevřené právě teď, nejrychlejší páka je přestat být trpěliví. Všechny tyto útoky stojí na tom, že váš server toleruje požadavek, který se nikam neposouvá.
V nginxu utáhněte hodiny a zastropujte souběžnost na klienta:
client_header_timeoutaclient_body_timeout— jak dlouho smí klient posílat hlavičky a tělo (výchozí 60 s u obou; pro většinu webů jsou realistické jednotky sekund).send_timeout— jak dlouho smí klient váznout mezi čteními odpovědi, což je přesně to, co dusí slow-read útoky.keepalive_timeout— jak dlouho smí nečinné spojení přežívat po dokončeném požadavku.limit_connse zónou klíčovanou nabinary_remote_addr— tvrdý strop souběžných spojení na zdrojovou adresu.
V Apachi je přímou odpovědí mod_reqtimeout, který se v moderních verzích dodává zapnutý; nastavte explicitní timeout pro hlavičky a tělo spolu s minimální datovou rychlostí, aby se pramínek přestal počítat jako pokrok. Zvýšení MaxRequestWorkers koupí rezervu a přechod z preforku na MPM event změní tvar problému, ale ani jedno není řešení samo o sobě — útočníka stojí přidání spojení nesrovnatelně méně než vás přidání workerů.
Dvě věci navíc, které pomůžou a stojí za to tak jako tak: postavte před každý aplikační server s vláknem na spojení reverzní proxy, aby čekání pohltila ona a předávala dál jen kompletní požadavky, a zajistěte, aby se těla požadavků plně bufferovala, než dorazí k aplikaci.
A pak to celé berte jako první pomoc. Zdražuje to útok, neukončuje.
Proč ladění originu nestačí
Agresivní timeouty narážejí na fakt skutečného internetu: část vašich legitimních klientů je opravdu pomalá. Telefon se slabým signálem, zákazník nahrávající velký soubor, webhook z přetížené linky, prohlížeč na hotelové Wi-Fi — všichni můžou vypadat jako pomalé odkapávání, pokud nastavíte pětisekundový timeout na tělo a přestanete o tom přemýšlet. Utáhněte to příliš a odepření služby jste tiše implementovali sami, přesně proti uživatelům, kteří to nejhůř zopakují.
Limity spojení na IP mají tentýž problém z druhé strany. Dvacet spojení z jedné adresy je pro skript agresivní a pro kancelář, NAT mobilního operátora nebo univerzitu úplně běžné. A distribuovaný pomalý útok — tatáž technika rozprostřená přes botnet, jedno dvě spojení na stroj — nespustí limit na IP vůbec, protože žádný jednotlivý zdroj nedělá nic neobvyklého. Tahle varianta přichází na weby, které už jednou dostaly zásah a zpevnily se, a je důvod, proč sem patří stejná logika jako k jakémukoli jinému útoku na aplikační vrstvě: jednotlivý klient vypadá v pořádku a špatně je až celá populace.
Co to opravdu zastaví: ukončit a odbuferovat na edge
Systémové řešení je zařídit, aby váš origin nedokončený požadavek vůbec nikdy nedržel.
Když před webem stojí edge typu reverzní proxy, každé klientské spojení končí tam. Edge pomalého klienta vyčká na vlastní událostmi řízené infrastruktuře — kde nečinné spojení stojí pár kilobajtů místo celého workera — a spojení k vašemu originu otevře teprve ve chvíli, kdy drží kompletní požadavek. Z pohledu vašeho serveru každý požadavek dorazí okamžitě a hned skončí. Není tu žádný stav, který by pomalý klient obsadil, protože pomalí klienti jsou starost někoho jiného, na železe stavěném na to držet jich miliony.
Tahle jediná vlastnost udělá většinu práce. Zbytek přidá to, co edge vidí a váš origin ne:
- Behaviorální analýza celé populace — jeden klient posílající hlavičku po devíti sekundách je nezajímavý; deset tisíc klientů ve stejném rytmu je signatura, a tu vidí jen něco, co je sleduje všechny naráz.
- Fingerprinting klienta — signatury na úrovni TLS a HTTP odhalí nástroj za spojením bez ohledu na to, za co se vydává, což je způsob, jak odlišit odkapávající skript od skutečně pomalého telefonu.
- Limity souběžnosti a rozpočty požadavků na klienta aplikované na edge a klíčované na něco lepšího než sdílenou IP adresu.
- Cachování na edge — každý cache hit je požadavek, který origin nemusí držet otevřený vůbec, a proto je CDN zároveň pancíř proti útokům.
Je to tentýž argument jako u always-on mitigace obecně, s jednou navíc podstatnou zvláštností: pomalé útoky jsou dost tiché na to, aby se protáhly pod aktivačními prahy, na kterých stojí on-demand scrubbing. Útok, který nikdy nevyrobí špičku v provozu, nemusí nikdy spustit přesměrování, které vás mělo zachránit.
Jak pomalé útoky řeší Itnetic
Itnetic je edge typu reverzní proxy, takže popsaná systémová obrana je výchozí stav, ne nastavení: spojení končí na pointu of presence nejbližším klientovi, požadavky se bufferují a váš origin vidí vždy jen kompletní požadavky z naší sítě. Slowloris spojení se drží proti infrastruktuře stavěné na to držet spojení otevřená a váš server se o něm nedozví.
K tomu:
- Always-on filtrace. Není žádný práh k překročení ani vypínač k přepnutí, což je nejdůležitější právě u útoků, jejichž celý návrh spočívá v tom zůstat pod prahem.
- Adaptivní detekce na vrstvě 7 staví behaviorální baseline pro každý host a endpoint, takže sladěný rytmus odkapávajícího botnetu vyčnívá, i když jeho objem ne — a load shedding drží edge svižný, zatímco filtruje.
- Logy jednotlivých požadavků zaznamenávají status, latenci, detaily TLS, fingerprint klienta i pravidlo a verdikt za každým rozhodnutím, takže pomalý útok zanechá stopu, kterou access log na originu ze své podstaty zanechat nemůže — viz logy a analytika a časová osa útoku, která označí sekundu, kdy se mitigace zapnula.
- API zóny udrží strojové klienty funkční: omezovaní volající API dostanou
429sRetry-Aftermísto výzvy, kterou nemají jak vyřešit — úvaha je v článku jak chránit API před DDoS útoky. - Vlastní WAF pravidla běží nejdřív v režimu jen pro log, takže vidíte, co by pravidlo na živém provozu zachytilo, ještě než začne cokoli blokovat — bezpečný způsob, jak utáhnout limity, aniž byste svého nejpomalejšího legitimního klienta objevili tvrdou cestou.
- Útočný provoz se nikdy neměří proti kvótám přenosu (ceník) a tarif Starter je zdarma. Nasazení jsou dva DNS záznamy a asi pět minut.
Jednu věc udělejte bez ohledu na dodavatele: zamkněte origin. Mitigace na edge vidí jen provoz, který přes edge jde, a origin IP, která pořád odpovídá internetu napřímo, se dá ukapat k smrti odkudkoli — checklist je v článku jak skrýt IP adresu originu.
Když se to děje právě teď
Zastropujte souběžná spojení na zdroj a stáhněte timeouty na hlavičky, tělo a odesílání na hodnoty, které skutečný prohlížeč pohodlně splní; často to samo obnoví provoz na tak dlouho, abyste stihli přemýšlet. Tvar útoku si potvrďte počítáním otevřených spojení a bajtů na spojení, ne pohledem na přenos. Pak dostaňte origin za edge, který bude spojení ukončovat za vás, a zafirewallujte ho tak, aby stará adresa přestala odpovídat.
Celý postup při incidentu je v článku jak zastavit DDoS útok. Až bude po všem, opravu si ověřte, místo abyste jí věřili — jak otestovat DDoS ochranu obsahuje i kontroly chování spojení, které tuhle třídu útoků chytí dřív, než chytí ona vás.