Log4j sebezhetőségeket felderítő segédletet jelentett be a GitHub
2021-12-15T16:42:06+01:00
2022-01-05T08:20:14+01:00
2022-07-20T07:21:56+02:00
  • Mutasd a teljes hozzászólást!
  • Azért lokálisan futó alkalmazásoknál is nagy gondot jelenthet, legyen egy banki vagy más szolgáltató, hogy innentől kezdve hálózaton belülről sebezhető a szerver vagy az alkalmazás, esetleg később egy trójai elhelyezhető a rendszerben. Elég arra gondolni, hogy küldenek emailben valami trójait, ami végig nézi a hálózaton elérhető gépeket, és amit tud megtámad, adatot lop, majd kinyit valamit valami szerveren.
    Mutasd a teljes hozzászólást!
  • 1. Ez a sebezhetőség akkor kerülhet kihasználásra (általában szoftveres robotok által), ha olyan komponensként van alkalmazva a log4j, amely közvetlenül naplóz és hívást delegál egy olyan rendszerben, ami közvetlenül elérhető az internetről, bármiféle hálózati védelem nélkül. Például ha a webszerver nincs router vagy más hálózati védelem mögött. A banki, és egyéb ipari, Java-s alkalmazások esetén ez elég ritka, persze nem kizárólagos, de aki egy kicsit is ért a webes technológáikhoz, az alapból előre tervezi meg a saját hálózatát, főképp akkor, amikor érzékeny adatokról lehet / van szó.

    Ha nem direktben lóg a rendszer a neten, de usertől kap inputot akár N-edik rendszerként a sorban, akkor is lehet sebezhető. Ha a hálózati forgalmat lekapcsolja a firewall, de a DNS lookupokat kiengedi (amennyiben a Java ezt elvégzi), akkor is sebezhető, mert a DNS requestekben tud szenzitív adatot szivárogtatni. A kimenő hálózati kapcsolatokat hamar le szokták tiltani, de a DNS esetében ez nem ilyen triviálisan igaz. Picit elnagyolása a problémának ha csak azt gondoljuk sebezhetőnek, ami direktben a neten lóg.
    Mutasd a teljes hozzászólást!
  • Sírnak az ügyfelek, az IT-s rendszergazdák és üzemeltetők meg egyfolytában telefonálnak, hogy ők olvastak ezt meg azt erről az egész loig4j-s sebezhetőségről, és akkor most adjunk nekik hotfix-et, vagy adjunk workaround megoldást, vagy bármit, mert itt most nemzethalál van és vége mindennek.

    Gyakorlati oldalról néhány egyszerű megjegyzés a problémával kapcsolatban.

    1. Ez a sebezhetőség akkor kerülhet kihasználásra (általában szoftveres robotok által), ha olyan komponensként van alkalmazva a log4j, amely közvetlenül naplóz és hívást delegál egy olyan rendszerben, ami közvetlenül elérhető az internetről, bármiféle hálózati védelem nélkül. Például ha a webszerver nincs router vagy más hálózati védelem mögött. A banki, és egyéb ipari, Java-s alkalmazások esetén ez elég ritka, persze nem kizárólagos, de aki egy kicsit is ért a webes technológáikhoz, az alapból előre tervezi meg a saját hálózatát, főképp akkor, amikor érzékeny adatokról lehet / van szó.

    2. Attól még, hogy például a docker, vagy maga az alkalmazás szerver használja a log4j-t, az még nem jelenti azt, hogy közvetlenül támadható, és kihasználható a sebezehetőség. Nyilván, ha például elérhető kívülről például egy webszerver admin / settings port-ja, akkor az baj, de hát az meg alap, hogy ilyet nem hagyunk nyitva. A docker-t sem szokás nyiltan elérhetővé tenni.

    3. A 2.16-os version már javította a problémát, de maradt még benne egy bug (egy rekurzív hívás StackOverflowError-ot dobott), ezt lehetett is könnyen emulálni, de az újabb - 2.17 - már ezt is javítja.

    4. Bár a probléma nagy, de csak azért, mert alapból sok rendszer - és elsősorban a Java alkalmazások - standard-ként ezt használják naplózásra. Viszont a probléma elhárítása elég könnyű, különösen maven típusú struktúrákban. Tehát a saját projektjeinkben ez nem okoz gondot. A problémát a külsős rendszerek - például az alkalmazás szerverek - által használt régi verziók jelenthetik, ezeket eléggé körülményes lecserélni, de azért - példuál a Wildfly esetében - nem lehetetlen. Kipróbáltam, mükődik, melós dolog, tesztelni kell, de azért megoldható.
    Szóval itt a problémát elsősorban a log4j elterjedése, és a hibás lib-ek cseréje jelenti, de ez azért nem egy eget rengető dolog. Az már más kérdés, hogy adott cégnél, milyen policy van, és hogy ezeknél mennyi idő az átfutása, egy ilyen javításnak, de én most csak az dev oldaláról közelítettem meg a problémát.

    Kb. ennyit erről, már ami a hétköznapi gyakorlatot érinti.
    Mutasd a teljes hozzászólást!
  • Mondjuk erre én azt a megoldást tartom a legkultúráltabbnak, hogy a lib, amit használok valamilyen callback API-t hív vissza logolás céljából, amit én implementálok.

    Az SLF4J FAQ-ban pont van erre egy kész válasz: SLF4J FAQ

    Röviden: ha a könyvtáraddal saját log absztrakció jön, azt neked kell karban tartani. Ezen felül a könyvtárad felhasználóinak is extra teher, hogy meg kell oldani az "összedrótozást" a könyvtárad és a log megoldásuk között. Ha központosított API-on keresztül megy a logolás, akkor ez mind megúszható, és "csak működik".

    Az SLF4J megoldása kb. olyasmi, mintha C++-ban mindenki megegyezne egy közös header halmazban, ami a "Logging" névteret és a publikus API-át tartalmazza. Az, hogy ezeket az elemeket ténylegesen mi implementálja, majd build időben derülne ki, akkor kellene linkelni valamelyik logging implementációt a binárishoz. (Most attól tekintsünk el, hogy a C++-ban néha az implementáció is kényszerű módon headerbe kell hogy kerüljön, és tegyünk úgy mintha a header tiszta interface, a .cpp fájl pedig tiszta implementáció lenne.)

    (Persze a C++-hoz nem értek. A fentieket nem úgy kell érteni, hogy C++-ban is ez a helyes út, csak hogy a Javában a fenti meggondolások alapján alakult ki a jelenlegi helyzet.)
    Mutasd a teljes hozzászólást!
  • Vélemény. Manapság a performanciára leginkább a megfelelő memory access tervezéssel lehet felkészülni. Egy naív memóriaszerkezet tele van öröklődéssel, virtuális függvényekkel, ide-oda mutogató pointerekkel, map-ekkel és vector-okkal. Ha igazán performanciára van szükség, akkor bejön a képbe a data oriented design, (entity component system-ek), olyan trükkök mint az arena memory allocation, és úgy általában tömbök és azokon végigmenő keveset branchelő kódok.
    Mutasd a teljes hozzászólást!
  • "naív C++ STL"

    Ez elírás vagy vélemény?

    Ha vélemény, akkor osztom
    Mutasd a teljes hozzászólást!
  • "

    Logolni nem csak te akarsz, hanem a komponensek is amiket használsz. 

    "

    Mondjuk erre én azt a megoldást tartom a legkultúráltabbnak, hogy a lib, amit használok valamilyen callback API-t hív vissza logolás céljából, amit én implementálok. Mondjuk mostanában nem Javazok, már nem annyira vagyok képben az ottani usecasekkel, de a nagy C++ libek, amiket használunk pl. a melóhelyen általában biztosítanak interface-eket, ahol minden IO jellegű dolgot megvalósítahatunk, ahogy akarunk. Nem csak logolást, de még filesystem elérést is jellemzően ilyen callback interfaceken keresztül érnek el ezek a libek. (Ezek egyébként valóban heavy liftinget végző libek, több évtized emberévnyi munkával és mély speciális domain tudással lehetne őket csak kiváltani.)

    Amúgy a témához: közhely, de a dependencyk-nek és behozott absztrakcióknak komoly költsége lehet mind performance, mind security, mind rugalmasság szempontjából. Nem kiforrott, nem jól megértett nagyon komplex és rossz performancia mutatókkal bíró dependencyk behozása, leakelő absztrakciók behozása sokat árthat egy projektnek. Persze egy jó mérnöknek meg kell találnia a megfelelő tradeoffokat. Én egy kicsit a konzervatív irányba tévedek még mindig, bár a fiatal korom extrémebb depednedency kerülését azért levetkőztem.

    2 féle dologra szeretek dependency-t behozni: ha triviálisan izolálható és bármikor lecserélhető a cucc. (pl. header-only képbeolvasó lib (a header-only az azért jó, mert nincs tranzitív dependency-je és nincs fejfájás a buildeléssel, ami C++ környezetben még a mai napig probléma)). Ezek könnyű dontések. A nehezebb döntések a következők: Nagyon sok munkával megcsinálható dolgok, amik viszont komoly kihatással vannak az alkalmazásra. Ilyenből egy projektben szerencsére kevés kell, de azokat nagyon körbe kell járni és nagyon sok szempontot figyelembe kell venni: megnézni, hogy kiforrott-e, open-source-e (tujuk-e debuggolni, át tudjuk-e írni ha nagyon gáz van). Mivel minden absztrakció leakel (ha más nem a bugok vagy performance problémák miatt), ezért fontos, hogy mennyire tudjuk megérteni a működését, stb... Általában érdemes kerülni a framewörk-öket, és preferálni a klasszikusabb lib-eket, ami nem akarja diktálni az alkalmazás architektúráját. Egyébként extrémebb performanciát igénylő területeken általában konzervatívabb dependency behozás dívik, nem véletlenül, mert pl. egy játékban egy naív C++ STL használat is túl lassú lenne a kód sok részében. Nyilván ha az ember nem annyira szereti a dependency-ket orrba szájba behozogatni, meg szeret high-performance kódot írni, akkor nem az a jó megoldás, hogy ezt olyan környezetben teszi, ahol ennek nincs értelme és csak lassítja a fejlesztést, hanem keres olyan munkaelyet, feladatot, ahol ennek van értelme. (Ezért nyergeltem át C++ területre.)
    Mutasd a teljes hozzászólást!
  • és pl., egy logot megcsinálnának maguknak

    Hát még ez se így működik. Logolni nem csak te akarsz, hanem a komponensek is amiket használsz. A Java ökoszisztéma nagy nehezen legalább odáig eljutott, hogy a log API-okat a következő népszerű opciókra leszűkítette:
    * Log4J (régi bevált név)
    * java.util.logging a.k.a. JUL (ez a szabvány, csak hát jó szokás szerint előbb szabványosítottak mint ahogy összeszedték volna az összes use-case-t)
    * Commons Logging (ez nem logol, csak próbálja megkeresni a ténylegesen elérhető loggert, ezért library-kban szeretik)
    * SLF4J (ez is csak egy API, kell alá egy implementáció, de kevésbé törékeny és modernebb mint a Commons Logging)

    Ezek között az átjárás mindenféle bridge library-kkal összetákolható, így az elvi esélyed megvan hogy minden ugyanúgy és ugyanoda logoljon, bármit is gondolt az adott library készítője.

    Na most ha ehhez a négyhez hozzárakod a Log4KisJ-t mert az összes többi büdös, akkor esélyed se lesz külsős könyvtárakhoz jól hozzáilleszteni, vagy legalább is szívhatsz a saját bridge megoldásaiddal. Persze lehet mondani hogy minek nekem külső lib, mindent megoldok a nulláról én, csak JÓL, csak éppen nem túl reális egy mai enterprise környezetben.
    Mutasd a teljes hozzászólást!
  • És mi lenne a megoldás? Mindent is megírni cégen belül? Rég túl vagyunk már azon, hogy egy programozó mindenhez értsen, amit fel kell használjon a munkájához. Nem így tűnik, de a logolás is egy igen komplex dolog, a programozók nagyon kis része rendelkezik olyan tudással, hogy megírja olyan minőségben, ahogy a log4j meg van írva.

    Az, hogy meglévő libeket felhasználsz egyáltalán nem zárja ki, hogy magas szintű programozást végezz. Csak annyit jelent, hogy nem mindent te írsz nulláról, ezért képes vagy megoldást szállítani.
    Mutasd a teljes hozzászólást!
  • Nem tudom a jo megoldast.

    Ugye jol jon egy komponens, mert lenyegesen felgyorsitja a fejlesztest, de ha:
    - ingyenes, meddig lesz az, meddig lesz tamogatva stb...
    - fizetos, mikent fog valtozni az ara, meddig lesz ra support stb...

    Ami biztos, hogy bug mindig lesz. Az viszont teny, hogy ha egy ingyenes komponenst hasznalsz, akkor ezzel vszinuleg nagyobb rizikot valalsz, mint egy fizetos eseten, de lehet, hogy ez sem minden eseten igaz.

    Szoval oszinten szolva, nem tudom.

    Viszont egyetertek BlaZemberrel, hogy

    A nem normális az, hogy egy egyirányú kapcsolatból most visszaköpködnek rengetegen, mert a 0 fabatkába kerülő egyébként igen hasznos libben hiba volt. De a javításban kevesen vettek és vesznek részt, továbbra is csak ingyen várják a javítást is, és elégedetlenkednek.
    Mutasd a teljes hozzászólást!
  • "A nem normális az, hogy egy egyirányú kapcsolatból most visszaköpködnek rengetegen,"

    Igy van.

    Ezért kellene elgondolkodni a cégeknek. Es több munkát rakni a projektekbe.

    Tákoljuk össze ingyen komponensekbol. Az akkor is tákolás, ha adunk a technologiáknak jól hangzó neveket. Ez nem fejlesztés, nem programozás.
    Mutasd a teljes hozzászólást!
  • Ez az egesz IT egy nagy bughalmaz. Nem tudom, hogy van esetleg mar iparag, amiben ilyen mertekben ez elofordul(hat). A torekvest viszont latom ra.
    Mutasd a teljes hozzászólást!
  • Nem az a baj, hogy nem tudnák megérteni. Megértenék, ha kellene vele foglalkozzanak, vagy ha csak kapnának rá időt. De nem kapnak, mert pénzt kell termelni. És ez szerintem normális. A nem normális az, hogy egy egyirányú kapcsolatból most visszaköpködnek rengetegen, mert a 0 fabatkába kerülő egyébként igen hasznos libben hiba volt. De a javításban kevesen vettek és vesznek részt, továbbra is csak ingyen várják a javítást is, és elégedetlenkednek.
    Mutasd a teljes hozzászólást!
  • "Nekem hihetetlenül abszurdnak hat, hogy (itt most konkrétan a log4j használata) megszámlálhatatlanul sok pénzt spórolt cégeknek, meg energiát fejlesztőknek"



    Talán elgondolkodnak majd a nagy cégek, hogy ha igazi programozókat vennének fel, és pl., egy logot megcsinálnának maguknak, vagy legalább átnézetnék olyannal aki érti is. Akkor talán kicsit jobb lenne a világ, és a hw gyorsulás/növekedés nem csak a programozó alkalmatlanágát kompenzálná.
    Mutasd a teljes hozzászólást!
  • Mi a hipotetikus? Hogy a JUL, ACL jó lenne, ha proxyzna egy nem létező log4j metódusnak? Az tényleg hipotetikus, mert ezek jóval régebbi API-k, mint a log4j2 maga :) Ezek nem kompatibilis API-k. Nem proxyzás történik, hanem bridgelés. 

    Mondjuk lehetne egy olyan elvet megalkotni, hogy egy program csak egy dolgot csináljon, de azt jól, világosan dokumentálva, és speciális trükkök nélkül (vagy ha már vannak ilyen speciális trükkök, legalább a programozó tudatos döntése legyen, ha használni akarja őket).

    Én nem látom ezzel mit szeretnél mondani. A log4j csak logolással foglalkozik, és ehhez nyújt olyan funkciókat, amik a logoláshoz hasznosak. A JNDI lookup is ilyen, nem önmagában ennek a ténye a probléma. Pont a deny-by-default policy a megoldás, amit meg is valósítottak, és valósítanak folyamatosan a felderített esetekre.
    Mutasd a teljes hozzászólást!
  • Igen, de ez az egész csak egy hipotetikus okoskodás, szakkifejezéssel élve veszett fejsze nyele...

    Itt már csak arról beszélhetünk, hogy milyen tanulságot vonhatunk le ebből jövendő tervezéshez/fejlesztéshez.

    Mondjuk lehetne egy olyan elvet megalkotni, hogy egy program csak egy dolgot csináljon, de azt jól, világosan dokumentálva, és speciális trükkök nélkül (vagy ha már vannak ilyen speciális trükkök, legalább a programozó tudatos döntése legyen, ha használni akarja őket).
    Mutasd a teljes hozzászólást!
  • Nem érted amit BlaZember már többször leírt. Vannak meglévő logging API-ok, amikbe nem tudsz belerakni új metódust, illetve ha bele is rakod, a meglévő kód nem fogja elkezdeni varázslatosan hívni.

    Ott van például a java.util.logging.Logger. Van neki millió metódusa, de egyikben sincs lookup funkciót ki- és bekapcsoló paraméter. Aki úgy dönt, hogy ezt az API-t használja (ami mellesleg a hivatalos API olyan értelemben, hogy egyedül ez a standard könyvtár része), annak totál mindegy hogy te a logging implementációdba milyen extra csodát vezetsz be. Ezen az API-on keresztül egyszerűen nincs módja jelezni ilyen extra igényeket.
    Mutasd a teljes hozzászólást!
  • Ha már egy hipotetikus szituációt elemzünk, akár ezek a proxy-osztályok is tudhatnának két metódust proxyzni.
    Mutasd a teljes hozzászólást!
  • Azért, mert akkor csak a direktben log4j-n függő kód tudná használni, ami a korábban elhangzottak miatt nem elégséges. Nem ez a jó megoldás, bármennyire is jónak tűnik elsőre.
    Mutasd a teljes hozzászólást!
  • > A log4j-ben van egy általános lookup megoldás
    Kezdünk közeledni a probléma gyökeréhez: miért nincs külön `info` és `info_with_lookups` metódus? Az utóbbihoz oda lenne írva, hogy hát bizony hatszoros a CPU-igény, és potenciálisan veszélyes.
    Mutasd a teljes hozzászólást!
  • A log4j-ben van egy általános lookup megoldás, ami használhat logger context-től kezdve kutyafüléig sokmindent. Ezek közé került be jórég a JNDI is, mint lehetőség. Valós, és hasznos felhasználási esetekkel. A JNDI nem csak LDAP-ból, hanem kismillió másból tud feloldani, pl egy alkalmazás szerver esetén a JNDI contextjéből infókat a logba tenni. És ahogy írtam, a saját API csomó esetben nem működne, ahol a JNDI-nek továbbra is van létjogosultsága. Ezt nem API szinten kell megoldani, hanem ahogy most csinálták, hogy egyrészt default ki van kapcsolva, másrészt deny-by-default policy stb. Jah, rájöhettek volna erre a fejlesztők korábban is...

    De mondok egy még durvábbat. A JNDI lookup 2013 óta benne van a log4j2-ben. A log4j egyrészt igen elterjedt, másrészt open-source. Az open-source software-eket szeretjük, látjuk a forrását, sok esetben ingyen van. Csillió dollárokat termelő alkalmazások futnak rajta. Kismillióan használták, és használják valós esetekben a JNDI képességeit. Közülük senki nem bírta végiggondolni 8 év alatt, hogy ez egy biztonsági rés, nem bírt belenézni a forrásba, kipróbálni? Ezeknek az open-source libeknek a közösségi fejlesztésben kell és kéne legyen az ereje. Nekem hihetetlenül abszurdnak hat, hogy (itt most konkrétan a log4j használata) megszámlálhatatlanul sok pénzt spórolt cégeknek, meg energiát fejlesztőknek, és most 1-2 fejlesztőt akarunk elverni a hibájáért olyan esetben, amikor túlzás nélkül is milliók használták a megoldásukat. Köszi a munkátokat, ingyen használjuk, de ha bug van benne, akkor leidiótázunk. Nincs ezzel a modellel valami probléma? :)
    Mutasd a teljes hozzászólást!
  • Csak azt gondolom, hogy pont az a gond, hogy túl sok dologra gondoltak... Ha már nagyon segíteni akartak a jndi-usereken, csinálhattak volna egy külön `String jndi_ldap_query(String url)` metódust, amit a derék hívó szándékosan hívhat meg, ha akar:

    String url= "hackme.com"; log.info("jndi_ldap('%s')='%s'", url, log.jndi_ldap_query(url));
    Mutasd a teljes hozzászólást!
  • Ehhez nem kell lookup, ezeket az infókat a program elő tudja szedni, és ki tudja logolni, külső hívás nélkül is. Log üzenetenként parancsokat futtatni az tényleg erős lenne azért, minden szempontból :)
    Mutasd a teljes hozzászólást!
  • Ha csak az első argumentumra ("format string") megy ez a behelyettesítés, akkor igazából ez user error.

    Az a baj, hogy ez a behelyettesítés API supportot (varargs) is kíván. Rengeteg lib nem konkrét logger implementáción, még csak nem is slf4j-n (ami szintén támogatja a placeholdereket), hanem JUL-on, vagy ACL-on ül (pl asszem a tomcat), amik nem támogatják a placeholdereket. Ezeket a loggereket át lehet bridge-elni másik logger implementációra, pl log4j-re. Mi is pl JUL-t használunk, mert alapból no-dependency jar-t gyártunk. Ilyenkor a log4j egy kész stringet kap, amiből szépen feloldja a lookup hivatkozásokat, ha van mit. Persze ennek kihasználásához az kell, hogy a nem log4j-t használó lib által átadott log üzenetbe belekerüljön a user string. De ezek a libek is tudnak dolgozni user adattal, így általuk is kerülhet logolásra ilyen rossz szándékú string.

    Erre is igaz amit mondtál, hogy külön-külön hasznos, és különböző emberek által, különböző megfontolásokkal dolgozó megoldások kerültek összekötésre, és ezek együttállása tud biztonsági rést nyitni. Simán lehet, hogy a log4j srácok még arra is gondoltak, hogy "áh, úgyse fogjuk feloldalni placeholderből a távoli JNDI lookupot". Aztán valaki rájuk bridge-gel... :)

    Lib gyártásnál ezek nagyon nehéz dolgok, mert megírsz ma egy libet, készítesz hozzá egy API-t, és holnap olyanok fogják használni, olyan módon, amit akár el se tudsz képzelni. Ezért ezerszer meg kell gondolni, hogy mit engedsz ki a hívó felé, mit csinálsz az inputoddal, és még úgy is érhetnek meglepetések. Szerintem itt is ez történt, nem az, hogy nemtörődöm hülyék készítették. Ha megnézzük a log4j egyéb paramétereit, amekkora és amilyen minőségű mérnöki munka ment bele, bőven gondolkodtak, akik csinálták. De hát mindenre ők se gondolhatnak sajnos.
    Mutasd a teljes hozzászólást!
  • `hostname; uname; id -a` pl. jól jöhet egy naplósorban.
    Mutasd a teljes hozzászólást!
  • Valóban, nem feltétlenül kell eszébe jusson a fejlesztőnek, hogy a log4j-hívó programok esetleg a felhasználótól jövő stringeket is logolni akarják...

    Ami nekem nem világos az az, hogy melyik fázisban megy ez a behelyettesítés. A Log4J API-ban ugyanis meg lehet adni placeholdereket:

    log.info("User {} said {}", userName, message);
    Ha csak az első argumentumra ("format string") megy ez a behelyettesítés, akkor igazából ez user error. Ugyanúgy, ahogy nem hagyod hogy C-ben a printf format stringjét külső forrásból befolyásolják, itt sem kéne dinamikusan építeni az első paramétert, és nincs gond.

    Ha viszont a placeholderek feloldása után megy a behelyettesítési fázis, akkor az tényleg WTF. Ahogy mondod, a külvilágból érkező adatokat logolni elég gyakori use case...

    Még azt kellene tesztelni, hogy a `rm -rf ~` is működik-e, hiszen az is nagyon praktikus szolgáltatás lenne.

    Tényleg? Mire?

    Értem én, hogy szarkazmus szeretett volna lenni, de a JNDI lookupokra tényleg van hasznos use case, még ha bevallottan eléggé rétegigény is.
    Mutasd a teljes hozzászólást!
  • Valóban, nem feltétlenül kell eszébe jusson a fejlesztőnek, hogy a log4j-hívó programok esetleg a felhasználótól jövő stringeket is logolni akarják... Még azt kellene tesztelni, hogy a `rm -rf ~` is működik-e, hiszen az is nagyon praktikus szolgáltatás lenne.

    Szerk: még ha csináltak volna egy külön metódust, hogy 'log_advanced_slow_dangerous' akkor talán oké lenne, de így...

    Szerk: 2.17.0 a pillanatnyi érték
    Log4j – Changes
    Mutasd a teljes hozzászólást!
  • Ingyensör a diploma nélkülieknek, diplomásoknak pezsgö.



    ps: "diploma" definició kicserélhető
     linux/windows
     natív/mannagelt
     izom/keretrendszer
     desktop/web app



    Mutasd a teljes hozzászólást!
  • Végül jöhet az örökélet, meg ingyensör :)
    Mutasd a teljes hozzászólást!
abcd