Přeskočit na obsah
Tech-Blog Chatujme.cz Chatujme.cz
Internet a sítě

Nová ochrana proti útoku Slowloris v Caddy 2.11.6 utínala některé streamy po minutě

Webový server Caddy zavedl ve verzi 2.11.6 minutové limity nečinnosti, které mají odříznout zaseknutá spojení. Ukončovaly ale i zdravé proudy server-sent events a reverzní proxy přes HTTP/2 kvůli nim padala. Opravy vyšly ve verzi 2.11.7 o dva dny později.

· 15 zhlédnutí

Webový server Caddy dostal 1. října 2026 ve verzi 2.11.6 novou výchozí ochranu proti útoku Slowloris. Do dvou dnů se ukázalo, že kromě zaseknutých spojení utíná i zdravá: proudy server-sent events otevřené požadavkem POST končily přesně po minutě a reverzní proxy přes HTTP/2 uměla shodit celý server. Verze 2.11.7 vyšla 3. října a správci v ní každému, kdo už přešel na 2.11.6, doporučují aktualizovat.

Outloň malý s velkýma očima se drží kovové mřížky
Outloň malý. Anglicky se outloňům říká slow loris. Foto: David Haring / Duke Lemur Center, Wikimedia Commons (CC BY-SA 3.0)

Co měla nová ochrana dělat

Slowloris je útok, který nepotřebuje velký provoz. Útočník otevře k serveru hodně spojení a v každém posílá data tak pomalu, aby je server nezavřel. Když volná spojení dojdou, ostatní návštěvníci se k serveru nedostanou.

Proti tomu přidal Kévin Dunglas pull requestem 7913 dva limity nečinnosti: read_body_idle pro čtení těla požadavku a write_idle pro zápis odpovědi. Starší limity platí pevně na celý přenos, u nových se odpočet vynuluje po každém úspěšném čtení nebo zápisu. Zaseknuté spojení server odřízne, pomalé, ale postupující nechá být. Podle popisu změny to odpovídá direktivám client_body_timeout a send_timeout v nginxu. Volitelně jde nastavit i minimální rychlost přenosu v bajtech za sekundu, obdobu parametru MinRate z modulu mod_reqtimeout v Apachi.

Oba limity mají podle dokumentace výchozí hodnotu jedna minuta. Autor to zdůvodnil tím, že jde o nová pole, takže na jiné hodnotě nemohla záviset žádná existující konfigurace. Do hlavní větve se změna dostala 22. srpna, ven s verzí 2.11.6.

Stream končil přesně po šedesáti sekundách

Ráno 2. října nahlásil uživatel chripo pád serveru na Ubuntu 22.04. Výpis ukazoval dereferenci nulového ukazatele ve chvíli, kdy Caddy nastavoval lhůtu pro čtení v knihovně HTTP/2 jazyka Go. O tři hodiny později přišlo druhé hlášení i s návodem, jak chybu zopakovat. Jeho autor postavil za Caddy malý server, který každých deset sekund pošle do proudu server-sent events krátkou zprávu. Požadavek GET běžel, dokud ho klient sám neukončil. Požadavek POST s tělem {} skončil po 60,01 sekundy a curl ohlásil přenos uzavřený uprostřed dat.

Na tenhle případ ochrana mířit neměla. Tělo požadavku přišlo celé a hned, klient byl v pořádku a nic nestálo. Takhle přitom otvírají stream klienti, kteří místo vestavěného EventSource používají funkci fetch: pošlou POST s JSONem a odpověď pak čtou průběžně. Hlášení vyzkoušelo i ostatní varianty se stejným serverem v pozadí. GET, POST s prázdným tělem, HTTP/2 i HTTP/3 potíž neměly a verze 2.11.4, předchozí vydání na GitHubu, taky ne.

Lhůta přežila požadavek

Příčinu dohledal autor hlášení až ke konkrétním řádkům. Čtečka těla nastavovala před každým čtením novou lhůtu a po konci těla ji nezrušila. U HTTP/1.1 se taková lhůta nastavuje celému spojení. Jakmile tělo dojde na konec, pustí knihovna net/http na spojení čtení na pozadí, kterým hlídá, jestli klient neodešel. Když pak stará lhůta vyprší, vypadá to pro ni jako odpojený klient, a zruší kontext celého požadavku. U HTTP/2 a HTTP/3 má lhůtu každý stream zvlášť a po konci těla už nic nečte, proto se tam chyba neukázala.

Obě hlášení spojil Zen Dodd do jednoho zápisu a opravil je pull requestem 8107. Čtečka si teď pamatuje, že tělo skončilo, a lhůtu po jeho konci znovu nenastaví. Po návratu obsluhy požadavku navíc ztratí přístup k objektu, přes který lhůty nastavuje. Právě ten byl u HTTP/2 v tu chvíli už uvolněný a volání na něj končilo pádem.

Tichý stream přes HTTP/2

Třetí chybu našel 3. října Weidi Deng, člen organizace Caddy na GitHubu, a necelé dvě hodiny poté ji sám opravil. Dokumentace slibuje, že limit write_idle nevadí odpovědím s přestávkami mezi zápisy, jako jsou server-sent events, dokud každý zápis postupuje. Platilo to jen pro HTTP/1.1 a HTTP/3, kde se lhůta zápisu nastavuje spojení, respektive streamu. U HTTP/2 vypršená lhůta v knihovně Go stream ukončí chybou. Proud, který mlčel déle než minutu, tak skončil, i když server nic nezdržovalo.

Co dělat do aktualizace

Kdo zůstává na 2.11.6, může limit pro čtení vypnout v globálních volbách záporným číslem. Postup uvádí dokumentace i autor druhého hlášení:

{
	servers {
		timeouts {
			read_body_idle -1s
		}
	}
}

Vyjmout jen jednu cestu nejde. Blok timeouts u jednotlivé cesty podle hlášení hodnoty nula a menší ignoruje a serverová lhůta se nastavuje až po něm. Tiché streamy přes HTTP/2 se dají ochránit stejným zápisem s write_idle. Správci ale doporučují rovnou 2.11.7, kde ochrana zůstává zapnutá a všechny tři chyby jsou opravené.

Co dalšího 2.11.7 přináší

Verze přidává podporu hlavičky Incremental z RFC 10036, o které jsme psali v článku Hlavička Incremental říká prostředníkům, ať HTTP zprávu posílají dál průběžně. Když ji nese odpověď ze serveru v pozadí, reverzní proxy ji předává dál hned a nečeká na celou zprávu. Pokud by tomu bránilo nastavení vyrovnávací paměti, odpoví Caddy kódem 501, jak RFC vyžaduje, místo aby odpověď potichu držel. Vyhledání certifikátu při navazování spojení TLS je podle poznámek k vydání zhruba dvakrát rychlejší a místo patnácti alokací paměti stačí deset. Zástupné hodnoty pro chybějící cookie jsou zase prázdné, ve verzi 2.11.6 se místo nich vypisoval doslovný text {http.request.cookie.*}.

Verze 2.11.6 přitom měnila víc. Poznámky k ní upozorňují na nekompatibilní změny, většinou kvůli zabezpečení. Hlavičky požadavku mají nově výchozí strop 16 KiB místo jednoho megabajtu z knihovny Go, takže požadavek s obří cookie dostane kód 431, a sestavení vyžaduje Go 1.26. Správci v poznámkách k oběma verzím píšou i o tom, že jazykové modely zlevnily příspěvky všech úrovní kvality a projekt je prochází, jak rychle stíhá. Hlášení i pull requesty v projektu mají oddíl, kde autor uvádí, jestli mu pomáhala umělá inteligence. Autor druhého hlášení to uvedl, autor opravy napsal, že ne.

Zdroje

Internet a sítě

Caddy HTTP/2 server-sent events Slowloris

← zpět na výpis