Röviden összefoglalva. Ebben a cikkben azt vizsgáljuk meg, hogy a védelmi sémák (hardening schemes) hogyan működnek az öt népszerű Linux disztribútorban. Mindegyikhez alapértelmezett kernel konfigurációt vettünk, letöltöttük az összes csomagot, és elemeztük a védelmi sémákat a beágyazott bináris fájlokban. A következő disztribúciókat vizsgáljuk: OpenSUSE 12.4, Debian 9, CentOS, RHEL 6.10 és 7, valamint Ubuntu 14.04, 12.04 és 18.04 LTS.
Az eredmények megerősítik, hogy még a legalapvetőbb sémákat, mint a stack canaries és a position-independent code, még mindig nem mindenki használja. A helyzet még rosszabb a fordítók esetében, amikor a stack clash-hez hasonló sebezhetőségek elleni védelemről van szó, amelyek januárban kerültek a figyelem középpontjába a közzétett . De nem minden olyan reménytelen. A binárisok jelentős részében alapvető védelmi módszerek vannak implementálva, és ezek száma verzióról verzióra növekszik.
A vizsgálat során a legnagyobb számú védelmi módszert az Ubuntu 18.04-ben valósították meg, az operációs rendszer és az alkalmazások szintjén, ezt követi a Debian 9. Másrészről, az OpenSUSE 12.4, CentOS 7 és RHEL 7 szintén implementálta az alapvető védelmi sémákat, míg a stack clash ellen védő intézkedések még szélesebb körben alkalmazásra kerültek, sokkal sűrűbb alapértelmezett csomagkészlettel.
Bevezetés
Nehéz magas színvonalú szoftvert biztosítani. Annak ellenére, hogy rengeteg fejlett eszköz áll rendelkezésre a kód statikus elemzésére és futásidőbeli dinamikus elemzésére, valamint jelentős fejlődés történt a fordítók és programozási nyelvek fejlesztésében, a modern szoftverek továbbra is szenvednek a sebezhetőségektől, amelyeket folyamatosan kihasználnak a kiberbűnözők. A helyzet még rosszabb az olyan ökoszisztémákban, amelyek elavult kódot tartalmaznak. Ilyen esetekben nemcsak a potenciális következő hibák keresésével küzdünk, hanem szigorú visszafelé kompatibilitási keretekkel is korlátozva vagyunk, amelyek gyakran megkövetelik, hogy megőrizzük a korlátozott, sőt rosszabb esetben sebezhető vagy hibáktól sújtott kódot.
Itt lépnek színre a védő- vagy megerősítő módszerek (hardening). Néhány hiba típusát nem tudjuk megakadályozni, de megnehezíthetjük a kiberbűnözők dolgát, és részben megoldhatjuk a problémát azzal, hogy megakadályozzuk vagy megnehezítjük azt. kihasználás ezek a hibák. Ez a védelem minden modern operációs rendszerben megtalálható, azonban a módszerek összetettsége, hatékonysága és teljesítménye jelentősen eltér: a stack canaries-től a teljes védelmi megoldásokig és . Ebben a cikkben megvizsgáljuk, hogy milyen védelmi módszereket alkalmaznak a legnépszerűbb Linux disztribúciók alapértelmezett konfigurációjában, valamint tanulmányozzuk azoknak a bináris fájloknak a tulajdonságait, amelyeket minden disztribúció csomagkezelő rendszere terjeszt.
CVE és biztonság
Mindannyian láttunk már olyan cikkeket, amelyek címei például „Az év legsebezhetőbb alkalmazásai” vagy „Az év legsebezhetőbb operációs rendszerei”. Ezekben általában statisztikákat közölnek a sebezhetőségekről , amelyet a következő forrásból szereznek be tól és más forrásokból. Ezeket az alkalmazásokat vagy operációs rendszereket a CVE-k száma alapján rangsorolják. Sajnos, bár a CVE-k nagyon hasznosak a problémák nyomon követésére és a szolgáltatók, valamint a felhasználók tájékoztatására, nem sokat mondanak a szoftver valós biztonságáról.
Példaként nézzük meg az elmúlt négy év CVE-jének általános számát a Linux kernellel és az öt legnépszerűbb szerver disztribúcióval, nevezetesen az Ubuntut, Debian-t, Red Hat Enterprise Linux-t és az OpenSUSE-t.

1. ábra
Mit mond nekünk ez a grafikon? Jelenti-e, hogy a több CVE sebezhetőbbé tesz egy disztribúciót, mint a másikat? Erre a válasz nem. Például ebben a cikkben látni fogja, hogy a Debian szigorúbb védelmi mechanizmusokat alkalmaz, összehasonlítva mondjuk az OpenSUSE-val vagy a RedHat Linux-szal, mégis a Debian-nak több CVE-je van. Ez azonban nem feltétlenül jelenti a csökkentett biztonságot: a CVE-k megléte nem mondja meg, hogy egy sebezhetőség kihasználható-e. A súlyossági pontszámok képet adnak arról, hogy mennyire valószínű a sebezhetőség kihasználása, de végső soron a kihasználhatóság nagymértékben függ a védelmi intézkedésektől, amelyek a vonatkozó rendszerekben találhatóak, valamint a támadók erőforrásaitól és képességeitől. Továbbá, a CVE-jelentések hiánya semmit sem mond a többi nem nyilvántartott vagy ismeretlen A sebezhetőségekről. A CVE-k közötti különbséget nem feltétlenül a szoftver minősége magyarázza, hanem más tényezők, például a tesztelésre szánt erőforrások vagy a felhasználói bázis mérete. Példánkban a Debian esetében a magasabb CVE szám egyszerűen arra utalhat, hogy a Debian több szoftvercsomagot kínál.
Természetesen a CVE rendszer hasznos információt nyújt, amely lehetővé teszi a megfelelő védelmek létrehozását. Minél jobban megértjük a programhibák okait, annál könnyebb meghatározni a potenciális kihasználási módszereket és kidolgozni a megfelelő mechanizmusokat. észlelésére és reagálására. A 2. ábrán láthatóak a sebezhetőségek kategóriái az összes disztribúció esetében az elmúlt négy évben (). Azonnal látható, hogy a legtöbb CVE az alábbi kategóriákba tartozik: szolgáltatásmegtagadás (DoS), kód végrehajtás, túlcsordulás, memória sérülés, információszivárgás (exfiltráció) és jogosultságok növelése. Habár sok CVE-t több kategóriában is figyelembe vesznek, összességében azok a problémák évről évre változatlanok maradnak. A cikk következő részében értékeljük a különböző védelmi intézkedések alkalmazását az említett sebezhetőségek kihasználásának megelőzésére.

2. ábra
Feladatok
Cikkünkben arra a kérdésre szeretnénk válaszolni,
- Mennyire biztonságosak a különböző Linux disztribúciók? Milyen védelmi mechanizmusok léteznek a kernelben és a felhasználói térben futó alkalmazásokban?
- Hogyan változott az idő múlásával a védelmi mechanizmusok elfogadása a különböző disztribúciók esetében?
- Mik az egyes disztribúciók csomag- és könyvtárfüggőségei?
- Milyen védelmek vannak az egyes binárisoknál?
Disztribúciók kiválasztása
Kiderült, hogy nehéz pontos statisztikát találni a disztribúciók telepítéseiről, mivel a letöltések száma gyakran nem utal a valódi telepítések számára. Mindazonáltal az Unix-opciók a legtöbb szerverrendszert alkotják (a web szervereken 69,2%, a W3techs és más források szerint), és a részesedésük folyamatosan növekszik. Így a kutatásunk során a kínálatban lévő disztribúciókra összpontosítottunk. . Különösen a következő operációs rendszereket választottuk ki:
Disztribúció/kiadás
Kernel
Build
OpenSUSE 12.4
4.12.14-95.3-default
#1 SMP Wed Dec 5 06:00:48 UTC 2018 (63a8d29)
Debian 9 (stretch)
4.9.0-8-amd64
#1 SMP Debian 4.9.130-2 (2018-10-27)
CentOS 6.10
2.6.32-754.10.1.el6.x86_64
#1 SMP Tue Jan 15 17:07:28 UTC 2019
verzióban
3.10.0-957.5.1.el7.x86_64
#1 SMP Fri Feb 1 14:54:57 UTC 2019
Red Hat Enterprise Linux Server 6.10 (Santiago)
2.6.32-754.9.1.el6.x86_64
#1 SMP Wed Nov 21 15:08:21 EST 2018
Red Hat Enterprise Linux Server 7.6 (Maipo)
3.10.0-957.1.3.el7.x86_64
#1 SMP Thu Nov 15 17:36:42 UTC 2018
Ubuntu 14.04 (Trusty Tahr)
4.4.0–140-generic
#166~14.04.1-Ubuntu SMP Sat Nov 17 01:52:43 UTC 20…
Ubuntu 16.04 (Xenial Xerus)
4.15.0–1026-gcp
#27~16.04.1-Ubuntu SMP Fri Dec 7 09:59:47 UTC 2018
Ubuntu 18.04 (Bionic Beaver)
4.15.0–1026-gcp
#27-Ubuntu SMP Thu Dec 6 18:27:01 UTC 2018
1. táblázat
Elemzés
Megvizsgáljuk az alapértelmezett kernelbeállításokat, valamint az egyes disztribúciók csomagkezelőjén keresztül elérhető csomagok tulajdonságait. Így csak a disztribúciók alapértelmezett tükréből származó csomagokat vesszük figyelembe, figyelmen kívül hagyva a nem stabil tárolókból (például a Debian ‘testing’ tükréből) és harmadik fél csomagjait (például a standard tükrökből származó Nvidia csomagokat). Ezenkívül nem vizsgáljuk a felhasználói kernel-összeállításokat vagy a megerősített biztonsági konfigurációkat.
Kernelkonfiguráció elemzése
Az elemző scriptet a következő alapján alkalmaztuk: . A megnevezett disztribúciók alapértelmezett védelmi beállításait vesszük figyelembe, és összehasonlítjuk azokat a (KSPP) ajánlásokkal. Minden konfigurációs paraméterhez a 2. táblázat leírja a kívánt beállítást: a pipa azoknál a disztribúcióknál van, amelyek megfelelnek a KSSP ajánlásoknak (a kifejezések magyarázatát lásd: ; a jövőbeli cikkekben beszámolunk arról, hogyan alakultak ki sok ilyen védelmi módszer, és hogyan lehet feltörni a rendszert e módszerek hiányában).

Általában az új kernel verziók szigorúbb alapértelmezett beállításokkal rendelkeznek. Például a CentOS 6.10 és RHEL 6.10 2.6.32-es kernelén nem található meg a legtöbb kritikus funkció, amely az új kernel verziókban megvalósításra került, mint például , a szigorú RWX engedélyek, címek véletlenszerűsítése vagy a copy2usr védelem. Érdemes megjegyezni, hogy sok konfigurációs lehetőség, ami a táblázatban szerepel, hiányzik a régebbi kernel verziókból, és valójában nem alkalmazható – a táblázatban ez is az alapvető védelem hiányaként van feltüntetve. Hasonlóképpen, ha egy konfigurációs paraméter hiányzik az adott verzióból, és a biztonság érdekében azt a paramétert le kell tiltani, akkor azt ésszerű konfigurációnak tekintjük.
Még egy szempont az eredmények értelmezésekor: egyes kernelkonfigurációk, amelyek növelik a támadási felületet, egyidejűleg használhatók a biztonság érdekében. Ilyen példák az uprobes és kprobes, kernel modulok és BPF/eBPF. Ajánlásunk az, hogy ezeket a mechanizmusokat valós védelem biztosítására használjuk, mivel nem trivialisak a használatuk, és az előfutásuk azt jelzi, hogy a rosszindulatú személyek már beágyazódtak a rendszerbe. De ha ezek a beállítások engedélyezve vannak, a rendszergazdának aktívan figyelemmel kell kísérnie a visszaéléseket.
Tovább vizsgálva a 2. táblázat feljegyzéseit, látjuk, hogy a modern kernerek több lehetőséget kínálnak az ilyen sebezhetőségek kihasználása ellen, mint az információszivárgás és a verem/heaped túlcsordulás ellen. Ugyanakkor megjegyezzük, hogy még a legújabb népszerű disztribúciók sem valósították meg a bonyolultabb védelmet (például javításokkal ) vagy a kódtúlfuttatásos támadások elleni modern védelmet (például ). Ami még rosszabb, hogy még ezek a fejlettebb védelmi mechanizmusok sem védik meg a támadások teljes spektrumától. Ezért a rendszergazdák számára rendkívül fontos, hogy a racionális konfigurációkat olyan megoldásokkal egészítsék ki, amelyek lehetővé teszik a kihasználások észlelését és megelőzését futásidőben.
Alkalmazások elemzése
Nem meglepő, hogy különböző disztribúcióknak eltérő csomagtulajdonságai, fordítási beállításai, könyvtárfüggőségei stb. vannak. Az eltérések még a disztribúciók és a kevés függőséggel rendelkező csomagok (például coreutils Ubuntu vagy Debian alatt) között is léteznek. Az eltérések értékeléséhez letöltöttük az összes elérhető csomagot, kibővítettük azok tartalmát, és elemeztük a bináris fájlokat és függőségeiket. Minden csomag esetében nyomon követtük azokat a csomagokat, amelyektől függ, és minden bináris esetében nyomon követtük a függőségeit. Ebben a részben összefoglaljuk az eredményeket.
Disztribúciók
Összesen 361 556 csomagot töltöttünk le minden disztribúcióhoz, kizárólag az alapértelmezett tükörből származó csomagokat kiválasztva. Figyelmen kívül hagytuk az ELF végrehajtható fájlok nélküli csomagokat, mint például a forráskódok, betűtípusok stb. A szűrés után 129 569 csomag maradt, összesen 584 457 bináris fájllal. A csomagok és fájlok eloszlása a disztribúciók között látható a 3. ábrán.

3. ábra
Megelőlegezhetjük, hogy minél modernebb a disztribúció, annál több csomag és bináris fájl található benne, ami logikus. Ugyanakkor az Ubuntu és Debian csomagok sokkal több bináris fájlt tartalmaznak (mind végrehajtható, mind dinamikus modulok és könyvtárak), mint a CentOS, SUSE és RHEL, ami potenciálisan befolyásolja az Ubuntu és Debian támadási felületét (fontos megjegyezni, hogy a számok minden verzió binárisait tükrözik, ami azt jelenti, hogy egyes fájlok többször is elemzésre kerülnek). Ez különösen fontos, ha figyelembe vesszük a csomagok közötti függőségeket. Így egy csomag binárisában lévő sebezhetőség sok ökoszisztéma elemére hatással lehet, ahogy egy sebezhető könyvtár befolyásolhatja az összes bináris fájlt, amely importálja azt. Referenciaként nézzük meg a függőségek számának eloszlását a különböző operációs rendszerekben:
4. ábra
Szinte minden disztribúcióban a csomagok 60%-ának legalább 10 függősége van. Ezenkívül egyes csomagok függőségeinek száma jelentősen nagyobb (100 fölött). Ugyanez vonatkozik a csomagok visszafelé mutató függőségeire: ahogy várható volt, néhány csomagot sok más csomag használ a disztribúcióban, ezért a nem sok kiválasztott csomag sebezhetőségeinek magas kockázata van. Példaként az alábbi táblázatban 20 csomagot sorolunk fel a legmagasabb visszafelé mutató függőségekkel a SLES, Centos 7, Debian 9 és Ubuntu 18.04 rendszeren (minden cellában a csomag megnevezése és a visszafelé mutató függőségek száma szerepel).

3. táblázat
Érdekes tény. Bár az összes vizsgált operációs rendszer az x86_64 architektúrára épül, és a legtöbb csomag architektúrája x86_64 és x86-ként van meghatározva, a csomagok gyakran tartalmaznak más architektúrákhoz készült bináris fájlokat is, ahogy az az 5. ábrán látható.

5. ábra
A következő részben mélyebben megvizsgáljuk az elemzett bináris fájlok jellemzőit.
A bináris fájlok védelmére vonatkozó statisztikák
Az abszolút minimum, hogy meg kell ismerni az alapvető védelmi lehetőségeket a meglévő bináris fájlokhoz. Néhány Linux disztribúció olyan szkripteket tartalmaz, amelyek végrehajtják ezeket az ellenőrzéseket. Például a Debian/Ubuntu esetében létezik egy ilyen szkript. Íme egy példa a működésére:
$ hardening-check $(which docker)
/usr/bin/docker:
Helyfüggetlen végrehajtható: igen
Védelem a veremre: igen
Fortify Source funkciók: nem, csak védtelen funkciók találhatók!
Csak olvasható átkonfigurálások: igen
Azonnali kötés: igenA szkript öt :
- Helyfüggetlen végrehajtható (PIE): jelzi, lehet-e memória területet áthelyezni a program szövegezési részében a véletlenszerűsítés érdekében, ha az operációs rendszerben engedélyezve van az ASLR.
- Verem védelem: a verem kanárik engedélyezve vannak-e, hogy megvédjék a veremütközések támadásaitól.
- Fortify Source: a nem biztonságos funkciók (például strcpy) biztonságosabb megfelelőikre vannak-e cserélve, míg a futásidőben ellenőrzött hívások - a nem ellenőrzött megfelelőikre (például memcpy helyett __memcpy_chk).
- Csak olvasható átkonfigurálások (RELRO): az átkonfigurálási táblázat bejegyzései «csak olvasható» jelöléssel rendelkeznek-e, ha azok működtek az végrehajtás megkezdése előtt.
- Azonnali kötés: a futásidejű linker lehetővé teszi az összes átkonfigurálás engedélyezését a program végrehajtásának megkezdése előtt (ez megfelel a teljes RELRO-nak).
Elegendőek a fent említett mechanizmusok? Sajnos nem. Ismertek olyan módszerek, amelyek megkerülik az összes fent említett védelmet, de minél szigorúbb a védelem, annál magasabb a támadó küszöbe. Például, nehézkesebbek, ha a PIE és azonnali kötés működik. Hasonlóképpen, a teljes ASLR további munkát igényel a működő exploit létrehozásához. Azonban a rafinált támadók már felkészültek az ilyen védelmekkel szemben: ezek hiánya lényegében felgyorsítja a betörést. Ezért rendkívül fontos, hogy ezeket az intézkedéseket szükségesnek tekintsük. minimum.
Szerettük volna megvizsgálni, hogy hány bináris fájl védett a vizsgált disztribúciókban ezekkel, valamint még három módszerrel:
- A nem végrehajtható bit () megakadályozza a végrehajtást bármely területen, amely nem kell, hogy végrehajtható legyen, például a verem kupacában stb.
- meghatározza az útvonalat, amelyet a dinamikus betöltő használ a megfelelő könyvtárak kereséséhez. Az első kötelező kategória bármely modern rendszer számára: hiánya lehetővé teszi a támadók számára, hogy tetszésük szerint terheléseket írjanak a memóriába, és azokat végrehajtsák. A második esetben a hibás végrehajtási útvonal konfigurációk segítik a megbízhatatlan kód bevezetését, ami számos problémához vezethet (például , valamint ).
- A veremütközés elleni védelem megakadályozza az olyan támadásokat, amelyek a veremet más memória területekre (például a halomra) kényszerítik. Tekintettel a közelmúltban tapasztalt kihasználásokra, amelyek visszaéltek , helyénvalónak találtuk, hogy ezt a mechanizmust belefoglaljuk adatainkba.
Nos, további formalitások nélkül térjünk a számokhoz. A 4. és 5. táblázat tartalmazza a különböző disztribúciók végrehajtható fájljainak és könyvtáraiknak elemzésének összefoglalóját.
- Mint látható, az NX védelem szinte mindenhol megvalósul, néhány kivételtől eltekintve. Különösen megjegyezhető, hogy az Ubuntu és a Debian disztribúciók esetében valamivel alacsonyabb a használata a CentOS, az RHEL és az OpenSUSE disztribúciókhoz képest.
- A veremkanárik sok helyen hiányoznak, különösen a régi kernellel rendelkező disztribúciókban. Néhány előrelépés tapasztalható a Centos, RHEL, Debian és Ubuntu legújabb disztribúcióiben.
- Kivéve a Debian és Ubuntu 18.04, a legtöbb disztribúcióban gyenge a PIE támogatás.
- A veremütközés elleni védelem gyengén van megvalósítva az OpenSUSE, Centos 7 és RHEL 7 esetében, és gyakorlatilag hiányzik a többi disztribúcióból.
- Minden modern kernellel rendelkező disztribúciónak van valamilyen RELRO támogatása, az élen az Ubuntu 18.04 áll, a második helyen a Debian.
Ahogy már említettük, a táblázatban szereplő metrikák az összes bináris fájl verzióra vonatkozó átlagok. Ha csak a legfrissebb fájlverziókat nézzük, a számok eltérőek lesznek (például lásd ). Ezen kívül a legtöbb disztribúció általában a védelem megszámlálásakor csak néhány funkció védelmét ellenőrzi a bináris kódban, a mi elemzésünkben viszont a megerősített funkciók valós százalékát tüntetjük fel. Tehát ha egy binárisban 50 funkcióból 5 van védve, akkor 0,1-es értéket adunk meg, ami 10%-ot jelent a megerősített funkciók arányában.

4. táblázat. A végrehajtható fájlok védelmi jellemzői, amelyeket az 3. ábra mutat (a megfelelő funkciók megvalósításának aránya a végrehajtható fájlok összesítéséhez képest)

5. táblázat. A védelem jellemzői a 3. ábrán bemutatott könyvtárak számára (az adott funkciók megvalósítása a könyvtárak összes számának százalékában)
Van előrelépés? Határozottan van: ez látható az egyes disztribúciók statisztikáiból (például ), valamint a fent bemutatott táblázatokból. Példa gyanánt a 6. ábrán látható a védelmi mechanizmusok bevezetése három egymást követő Ubuntu LTS 5 disztribúcióban (a verembeli ütközések védelmére vonatkozó statisztikát kihagytuk). Megjegyezzük, hogy verzióról verzióra egyre több fájl támogatja a verembe ágyazott figyelmeztetéseket, és folyamatosan egyre több bináris fájl kerül kiadásra teljes RELRO védelemmel.
6. ábra
Sajnos a különböző disztribúciókban számos végrehajtható fájl továbbra sem rendelkezik a fent említett védelmekkel. Például, ha megnézzük az Ubuntu 18.04-et, észlelhetjük a ngetty binárist (a getty helyett), valamint az mksh és lksh héjakat, a picolisp értelmezőt, az nvidia-cuda-toolkit csomagokat (népszerű csomag GPU-gyorsított alkalmazásokhoz, mint a gépi tanulási keretek) és a klibc-utils-t. Hasonlóképpen, a mandos-client binárist (adminisztratív eszköz, amely lehetővé teszi az automatikus újraindítást titkosított fájlrendszerekkel rendelkező gépeknél), valamint az rsh-redone-client-t (az rsh és rlogin újraimplementálása) NX védelem nélkül szállítják, bár SUID jogokkal rendelkeznek :(. Ezenkívül több SUID-binaryban nincs alapvető védelem, mint például a verembe ágyazott figyelmeztetések (például a Xorg.wrap bináris fájl az Xorg csomagból).
Összegzés és záró megjegyzések
Ebben a cikkben kiemeltük a modern Linux disztribúciók néhány biztonsági jellemzőjét. Elemzésünk alapján a legutóbbi Ubuntu LTS (18.04) disztribúció átlagosan a legmagasabb szintű védelmet nyújtja az operációs rendszer és az alkalmazások szintjén, a viszonylag új kernelű disztribúciók között, mint például Ubuntu 14.04, 12.04 és Debian 9. Az átvizsgált CentOS, RHEL és OpenSUSE disztribúciók azonban alapértelmezés szerint sűrűbb csomagkészlettel rendelkeznek, és a legújabb verziókban (CentOS és RHEL) a stack collision defense megvalósításának magasabb arányát mutatják be a Debian alapú versenytársaikhoz (Debian és Ubuntu) képest. A CentOS és RedHat verziók összehasonlításakor nagyszámú fejlesztést tapasztalunk a stack canaries és RELRO bevezetésében a 6-osról a 7-es verzióra, de átlagosan a CentOS több funkcióval rendelkezik, mint a RHEL. Összességében minden disztribúciónak külön figyelmet kell fordítania a PIE védelemre, amely, Debiant 9 és Ubuntu 18.04 kivételével, a vizsgálati adathalmazunkban a bináris fájlok kevesebb mint 10%-ában valósult meg.
Végül érdemes megjegyezni: bár manuálisan végeztük a kutatást, számos biztonsági eszköz áll rendelkezésre (például, , , ), amelyek elemzéseket végeznek és segítenek elkerülni a nem biztonságos konfigurációkat. Sajnos a megfelelő konfigurációban alkalmazott erős védelem sem garantálja az exploitok hiányát. Ezért határozottan hisszük, hogy elengedhetetlen a , a kihasználási mintákra összpontosítva és azok megelőzésével.
Forrás: habr.com
