To ale v některých situacích nestačí (pokud tomuhle článku dobře rozumím): https://www.lunasec.io/docs/blog/log4j-zero-day-update-on-cve-2021-45046/
K tomu jsou navíc mezi postiženými kousky se stavem "Patch Pending" chuťovky jako 27 produktů od VMware, kolem desítky od Symantecu a mnoho dalších. Z toho téměř každý se manuálně záplatuje trochu jinak. Zatím to útočníci většinou jen oťukávají a mapují, ale tento vektor útoku se už začíná objevovat u ransomware a velkých, státem sponzorovaných skupin.
Oracle se třeba zatím také vyjádřil pouze k asi polovině produktů, Oracle Cloud ještě také není plně patchovaný, návody mění bez upozornění, patche nefungují a SW rozbíjí. Jsou to zatuchlé rybníčky.
Na druhou stranu různé CI a CD online nástroje jsou teď hodně vytížené, čekání je značné i s placenými tarify.
Pak se podíváš do kódu log4j pro 2.16.0 a zjistíš, že třída org.apache.logging.log4j.core.lookup.Interpolator pořád používá jndi lookup, https://github.com/apache/logging-log4j2/blob/rel/2.16.0/log4j-core/src/main/java/org/apache/logging/log4j/core/lookup/Interpolator.java#L112-L119, takže ještě může existovat kombinace nastavení a vstupu, který projde.
Ukazuje se, že opravdu nejjednodušší a nejrychlejší řešení je odstranění třídy JndiLookup přímo z jarka a přebalit aplikaci, díky try catch v kódu to aplikaci vadit nebude. Zatím nám funguje na všech těch enterprise věcech, které ještě neposkytly patche a drtivá většina infrastruktury je už takhle opatchována v řadě českých bank.
Z dlouhodobého hlediska je asi vhodné log4j vyřadit z code base, vzhledem k tomu, že vstupní string masivně interpretuje a zpracovává, je možné, že těch zranitelností tam v budoucnu může být více, zejména pokud jde o uživatelský vstup, který se neescapuje a nesanitizuje.
Oracle doporucuje to normalne smazat (ale pouze od verze 2.11),
zip -q -d log4j-2.11.1.jar org/apache/logging/log4j/core/lookup/JndiLookup.class
Ta interpretace muze byt i uzitecna. My jsme to pouzili pro to aby bylo mozne do logu vlozit sessionId, REST API url, celkovou dobu trvani, a podobne veci. Bohuzel nikoho na prvni pohled nenapadne, ze by se takova vec dala zneuzit.
Ta interpretace muze byt i uzitecna. My jsme to pouzili pro to aby bylo mozne do logu vlozit sessionId, REST API url, celkovou dobu trvani, a podobne veci. Bohuzel nikoho na prvni pohled nenapadne, ze by se takova vec dala zneuzit.
K tomu ale nepotřebujete interpretaci. To se normálně řeší tak, že přidáte tyto informace do MDC (Mapped Diagnostic Context) a ty se pak vypisují spolu s logovací zprávou.
Pokud autory logovací knihovny nenapadne, že by se to dalo zneužít, je lepší se od takové knihovny držet co nejdál.
souhlasím, vzhledem k tomu, že autoři takovéhle chování považovali za správné a dnes se vyjadřují, že to tam chtěji vrátit až vyřeší jako to udělat bezpečně, raději pryč.
Ukazuje to i poměrně silnou neznalost knihoven, které se používají, tohle byla dokumentovaná vlastnost, jen nikdo (vč. mě) jí dopředu nečetl.
V kódu a dokumentace o všem mluví jako o message a o tom, že to je string, přitom s tím pracují jako se vstupem, který dále interpretují, to mi moc jako prostá message nepřipadá.
Stejný problém mimochodme nastavává i při zpracování textových logů a jejich parsování, běžně nástroje na parsování špatně interpretují některé znaky, překládají řídící znaky jako \r, \0 a jiné, to může vést také k pěkným zranitelnostem. Stejně tak aplikace, které sice do logů dávají json, ale skládají ho ze stringu (pro nginx docela oblíbené řešení v kubernetes).
open source podle mě podobným chybám nebrání, ale zjendodušuje jejich opravování. Takhle jsme byli schopní hned v pátek udělat analýzu a zvolit odpovídající obranu.
Zatímco u uzavřeného kódu bychom neudělali nic, placený support ani v tomhle případě neodpovídá hodiny, dny. Návod a patche ještě nejsou pro všechny placené komponenty a přitom open source je již opravený a vyřešený během hodin.
Po bitvě každý generálem, ale tady to na mne působí, že někdo porušil princip KISS, což bylo po zásluze potrestáno. Od logovací knihovny čekám, že bude zapisovat logovací hlášky, a to je vše. To s tím jndi: resolvingem na mne působí jako "extra chytristika".
Skóre 10.0 z 10.0 CVSS, odhad počtu zranitelných systémů v řádu vyšších stovek milionů, to je velmi slušné.
https://www.nukib.cz/download/uredni_deska/2021-12-15_RO-NUKIB-Log4Shell.pdf
Z dlouhodobého hlediska je asi vhodné log4j vyřadit z code base, vzhledem k tomu, že vstupní string masivně interpretuje a zpracovává, je možné, že těch zranitelností tam v budoucnu může být více, zejména pokud jde o uživatelský vstup, který se neescapuje a nesanitizuje.
To mi přijde trochu přehnané. Ono není totiž moc kam jinam jít. slf4j je šíleně zastaralé API. Jediný důvod, proč se Logback, log4j 1 ještě dnes používá je, že lidi používají stále to samé dokola. Logback je hodně používaný, protože je jako výchozí ve Spring Boot a spousta lidí ani netuší co používá.
Je kam jinam jít. slf4j nemá šíleně zastaralé API – „chybí“ mu jediná věc, a to je efektivnější zápis pro lazy získávání parametrů pro logovací zprávy. Což je pouhá optimalizace. Používá se to výjimečně, což je v pořádku, protože to logování posouvá směrem, kde je dnes Log4j 2 – že se během logování provádí kód, o kterém nikdo nic neví.
Takže není žádný problém slf4j používat. Je to pořád milionkrát lepší, než mít kvůli logovací knihovně v aplikaci vzdálené spuštění kódu. Navíc pokud by někdo tak šíleně toužil po těch lazy parametrech, nic mu nebrání napsat wrapper kolem slf4j a klidně ho zveřejnit jako opensource knihovnu. Bylo by to pár řádek kódu – místo psaní komentářů už byste ten wrapper měl napsaný.
Vy se pořád tváříte, že ta chyba Log4j 2 je ojedinělá chyba, která se může stát každému. Evidentně vás nezviklalo ani to, že se další velmi podobná chyba objevila hned po pár dnech. Pořád si odmítáte připustit, že to je chyba v návrhu Log4j 2, dokonce chyba v definici požadavků, co má logovací knihovna dělat. Že se takovéhle chyby objevily v Log4j 2 není náhoda. A riziko, že tam budou další podobné chyby, je velké. Logovací knihovna nemá text, který má zalogovat, nijak interpretovat, má ho co nejrychleji dostat do logu. Pokud na vstupu dostane něco jiného, než text, má to co nejbezpečněji převést na text a dále postupovat podle předchozí věty.
Jedná se pořád o tu samou chybu. Schválně jsem se koukal na počet chyb v logback a je to skoro to samé jako log4j2.
Těch věcí co slf4j nepodporuje je mnohem více. Např. CloseableThreadContext o tom se slf4j může jenom zdát, Message API, Garbage-free Steady State Logging.
Je celkem štěstí, že se ta chyba objevila v log4j 2 a ne v logback, protože si nedokážu představit, co by se dělo. kdyby to bylo opačně. Protože spousta lidí co používá logback ani o tom neví a nebyla by schopná to opravit.
Jedná se pořád o tu samou chybu.
Jenže pořád tu samou chybu se pokusili opravit, a neúspěšně. Dokonce se ukázalo, že nepomáhá ani konfigurační property, která měla tuhle super funkci vypnout.
Schválně jsem se koukal na počet chyb v logback a je to skoro to samé jako log4j2.
To myslíte vážně? Vážně naházíte na jednu hromadu chyby se skórem 1 i 10?
CloseableThreadContext
To jste si popletl. To neumělo log4j 1.x, slf4j s tím pak přišlo a nazvalo to MDC. Log4j 2 to jen okopírovalo.
Message API
Díkybohu, že to nepodporuje.
Garbage-free Steady State Logging
To zní docela úsměvně v kontextu této chyby. „Nevytvoříme žádný nový objekt, tedy s výjimkou případů, kdy si stáhneme úplně celou třídu odkudkoli z internetu“.
Je celkem štěstí, že se ta chyba objevila v log4j 2 a ne v logback, protože si nedokážu představit, co by se dělo.
To není štěstí, to je důsledek přístupu autorů.
Protože spousta lidí co používá logback ani o tom neví a nebyla by schopná to opravit.
Stejné je to ale i s Log4j 2. Ty knihovny jsou často součástí nějaké aplikace, kde provozovatel možná ví, kde má konfigurační soubor logování (často ani to ne).
Jenže pořád tu samou chybu se pokusili opravit, a neúspěšně. Dokonce se ukázalo, že nepomáhá ani konfigurační property, která měla tuhle super funkci vypnout.
To druhé CVE má score 3.5 a zrovna není snadné to udělat. Je to asi stejné jako kdybyste v logback změnil logger na JNDI. Takže i logback měl podporu pro JNDI.
To myslíte vážně? Vážně naházíte na jednu hromadu chyby se skórem 1 i 10?
OMG CVE-2017-5929 7.5 .
To jste si popletl. To neumělo log4j 1.x, slf4j s tím pak přišlo a nazvalo to MDC. Log4j 2 to jen okopírovalo.
Nepopletl zkuste si tam dát víc hodnot.
To zní docela úsměvně v kontextu této chyby. „Nevytvoříme žádný nový objekt, tedy s výjimkou případů, kdy si stáhneme úplně celou třídu odkudkoli z internetu“.
Holinky jako hodinky, obojí se natahuje. Ještě bych dodal varargs, protože slf4j umí jenom dva argumenty.
Stejné je to ale i s Log4j 2. Ty knihovny jsou často součástí nějaké aplikace, kde provozovatel možná ví, kde má konfigurační soubor logování (často ani to ne).
Měl jsem na mysli úplně jiný případ a to, že vyvíjí aplikaci a vůbec netuší co mají za použitý logger. Zejména u Spring Boot.
Nepopletl zkuste si tam dát víc hodnot.
Budou tam. A dál? S čím máte problém?
Ještě bych dodal varargs, protože slf4j umí jenom dva argumenty.
Aha, už chápu, v čem je problém. Vy jste nepostřehl, že není rok 2011 ale rok 2021. slf4j od verze 1.7.0, která vyšla v roce 2012, podporuje varargs (musela se kvůli tomu zvýšit minimální podporovaná verze Javy na 1.5).
Měl jsem na mysli úplně jiný případ a to, že vyvíjí aplikaci a vůbec netuší co mají za použitý logger. Zejména u Spring Boot.
Ano, vím, že jste myslel jenom na část aplikací a dělal z toho závěry pro všechny. To, že logování prostě funguje a vývojář to nemusí nijak speciálně řešit, považuji za dobré.
Budou tam. A dál? S čím máte problém?
Do jednoho MDCClosable jde dát jenom jedna hodnota, takže když jich chcete víc, tak jich musíte vytvořit několik MDCCloasable. Ale chápu, že vám to nevadí a stále místo try-with-resources používáte close() a finally, kvůli zpětné kompatibilitě a že to dělá něco co vy nevidíte.
Aha, už chápu, v čem je problém. Vy jste nepostřehl, že není rok 2011 ale rok 2021. slf4j od verze 1.7.0, která vyšla v roce 2012, podporuje varargs (musela se kvůli tomu zvýšit minimální podporovaná verze Javy na 1.5).
Spíš jste to totálně nepochopil vy. log4j 2 umí až 10 argumentu na rozdíl od pouhých dvou co umí slf4j , pro víc se musí použít varargs.
Do jednoho MDCClosable jde dát jenom jedna hodnota, takže když jich chcete víc, tak jich musíte vytvořit několik MDCCloasable.
A v čem je tedy ten problém?
Ale chápu, že vám to nevadí a stále místo try-with-resources používáte close() a finally
To chápete špatně.
Spíš jste to totálně nepochopil vy. log4j 2 umí až 10 argumentu na rozdíl od pouhých dvou co umí slf4j , pro víc se musí použít varargs.
Jasně, čím víc pruhů, tím víc Addidas. Už si vymýšlíte úplné nesmysly. Když napíšu logger.debug("…", arg1, arg2, arg3), je v drtivé většině případů úplně jedno, jestli je to metoda s varargs nebo se čtyřmi parametry. Pokud v některých velmi výjimečných případech vadí, že se tam vytváří nové pole, pořád si to pole můžete předalokovat předem.
CloseableThreadContext.put(key1, value1).put(key2, value2)
vs MDC.MDCCloseable closable1 = MDC.putCloseable(key1, value1), MDC.MDCCloseable closable2 = MDC.putCloseable(key2, value2)
Je to mapa/set resp. stack.
Jasně, čím víc pruhů, tím víc Addidas. Už si vymýšlíte úplné nesmysly. Když napíšu logger.debug("…", arg1, arg2, arg3), je v drtivé většině případů úplně jedno, jestli je to metoda s varargs nebo se čtyřmi parametry. Pokud v některých velmi výjimečných případech vadí, že se tam vytváří nové pole, pořád si to pole můžete předalokovat předem.
Neřekl bych ani popel, kdyby těch argumentu bylo aspoň 5 ale 2 je opravdu málo. A k čemu je mi předalokování pole, vždyť to vůbec nic neřeší? Je jedno jestli se mi alokuje dopředu nebo až po zavolání. Ve velmi výjimečných případech, mě to teda vadí pokaždé, proč se má dělat něco co se dělat nemusí?
CloseableThreadContext.put(key1, value1).put(key2, value2)
vs MDC.MDCCloseable closable1 = MDC.putCloseable(key1, value1), MDC.MDCCloseable closable2 = MDC.putCloseable(key2, value2)
Je to mapa/set resp. stack.
Vím. Ten problém teda spočívá v čem?
Neřekl bych ani popel, kdyby těch argumentu bylo aspoň 5 ale 2 je opravdu málo.
K tomu, aby metody mohly mít proměnlivý počet argumentů, slouží varargs. Vytvářet vedle toho další metody s pevně daným počtem parametrů kvůli jakési optimalizaci je antipattern.
Je jedno jestli se mi alokuje dopředu nebo až po zavolání.
Myslel jsme, že vám vadí, že se během logování alokuje nový objekt. Pokud vám nevadí ani to, netuším, co s tím vlastně máte za problém.
Ve velmi výjimečných případech, mě to teda vadí pokaždé, proč se má dělat něco co se dělat nemusí?
Ano, to je dobrá otázka, proč se mají do API logovací knihovny přidávat nějaké metody, které tam být nemusí. Na odpověď stále čekám.
K tomu, aby metody mohly mít proměnlivý počet argumentů, slouží varargs. Vytvářet vedle toho další metody s pevně daným počtem parametrů kvůli jakési optimalizaci je antipattern.
Tak určitě. Jaká si nepodstatné optimalizaci, něčeho co se používá tak často, že už to má vliv. Kecy o antipatternu si nechte pro někoho jiného, protože to samé je udělané i v JDK např. List.of().
Myslel jsme, že vám vadí, že se během logování alokuje nový objekt. Pokud vám nevadí ani to, netuším, co s tím vlastně máte za problém.
Mě vadí, že se alokuje nový objekt, když nemusí.