Prieš kelis metus tiekimo grandinės atakos buvo tema, apie kurią diskutuodavo tik saugumo specialistai konferencijose. Šiandien tai jau kasdienė realybė, apie kurią kalba ir CTO, ir DevOps inžinierius, ir net paprastas vartotojas, kuris net nesusimąsto, kad jo naudojama programa gavo kenkėjišką kodą per visiškai teisėtą atnaujinimo kanalą. Problema tame, kad šiuolaikinė programinė įranga niekada nėra kuriama vienur – ji sudėta iš dešimčių, šimtų, kartais tūkstančių svetimų komponentų, bibliotekų ir paslaugų. Ir kiekvienas iš tų elementų yra potenciali durų anga.
Kas slypi už šio termino
Tiekimo grandinės ataka – tai situacija, kai užpuolikas nesistengia tiesiogiai pralaužti jūsų sistemos, o vietoj to atakuoja jūsų tiekėją, partnerį ar įrankį, kuriuo pasitikite. Užpuolikas įneša kenkėjišką kodą į komponentą, kuris vėliau bus paskirstytas visiems, kas naudoja tą produktą. Rezultatas – vienas įsilaužimas atveria duris į šimtus ar tūkstančius organizacijų vienu ypu.
Analogija, kuri dažnai naudojama: jei norite apnuodyti miestą, nebandysite eiti pas kiekvieną gyventoją atskirai – užnuodysite vandens tiekimo šaltinį. Tas pats principas veikia IT pasaulyje. Kodėl gaišti laiką bandant įsilaužti į konkrečią banko sistemą, kai galima įsilaužti į IT paslaugų teikėją, kuris administruoja šimtą tokių bankų?
Svarbu suprasti, kad ši sąvoka apima kelis skirtingus vektorius:
- Programinės įrangos tiekimo grandinė – kenkėjiškas kodas įterpiamas į kompiliavimo procesą, bibliotekas ar atnaujinimų serverius.
- Aparatinės įrangos grandinė – kompromituojami komponentai gamybos ar transportavimo etape.
- Trečiųjų šalių paslaugų grandinė – atakuojami rangovai, IT administratoriai ar debesijos paslaugų teikėjai, turintys prieigą prie jūsų infrastruktūros.
- Open source ekosistema – kenkėjiškas kodas patenka į viešai naudojamus paketus per npm, PyPI, Maven ir panašius repozitorijus.
Kodėl šis metodas tapo tokia patrauklia strategija
Atsakymas paprastas – efektyvumas. Šiuolaikinės organizacijos naudoja begalę trečiųjų šalių sprendimų. Vidutinė įmonė gali turėti nuo kelių šimtų iki kelių tūkstančių priklausomybių savo projektuose, ir dažnai net nežino tikslaus skaičiaus. Kai tikrini vieną didelį taikinį, jis gali turėti gerai apsaugotą perimetrą, aktyvią SOC komandą ir modernius EDR sprendimus. Bet ar tokį pat lygį saugumo turi maža programinės įrangos kompanija, kurios biblioteka naudojama tavo produkto viduje?
Kitas svarbus faktorius – pasitikėjimas. Kai kodas pasirašytas skaitmeniniu sertifikatu, kai jis ateina iš žinomo tiekėjo per oficialų atnaujinimo kanalą, dauguma saugumo sistemų jį praleidžia kaip patikimą. Užpuolikams tai auksinė galimybė – jie ne tik gauna prieigą, bet ir gauna ją su „patikimo” statusu, apeidami dauguma tradicinių aptikimo mechanizmų.
Ekonominis aspektas taip pat neatsiejamas nuo šios tendencijos. Vieno tiekimo grandinės kompromitavimo kaina, palyginti su potencialiu atsipirkimu, yra neįtikėtinai maža. Investuoji į vieną taikinį, gauni prieigą prie šimtų.
Atvejai, kurie pakeitė supratimą apie riziką
Kalbant apie SolarWinds incidentą 2020 metais, neįmanoma nepaminėti, kaip jis pakeitė visos industrijos požiūrį į problemą. Užpuolikai (priskiriami valstybės remiamai grupei) įsilaužė į SolarWinds kūrimo aplinką ir įterpė kenkėjišką kodą į Orion platformos atnaujinimus. Tas kodas buvo skaitmeniniu būdu pasirašytas ir platinamas per visiškai teisėtus atnaujinimo kanalus mėnesius, prieš kas nors tai pastebėjo. Nukentėjo apie 18 000 organizacijų, tarp jų – JAV federalinės agentūros.
Kitas ryškus pavyzdys – „event-stream” incidentas npm ekosistemoje. Populiarios JavaScript bibliotekos priežiūrą perėmė nežinomas asmuo, kuris vėliau įterpė kenkėjišką kodą, nukreiptą į konkrečią kriptovaliutų piniginę naudojančią programą. Šis atvejas parodė, kaip lengvai galima perimti mažos, bet plačiai naudojamos bibliotekos kontrolę.
Negalima nepaminėti ir „3CX” incidento, kai VoIP programinės įrangos gamintojas tapo tiekimo grandinės atakos tarpine stotele – jo pačio kompiliavimo procesas buvo užkrėstas per kitą, ankstesnę tiekimo grandinės ataką. Tai idealiai parodo, kad grandinė gali turėti kelis lygmenis, ir vienas kompromitavimas gali kaskadu paveikti kitus produktus.
Taip pat verta prisiminti „Codecov” incidentą, kai užpuolikai modifikavo bash script naudojamą CI/CD procesuose ir gavo prieigą prie aplinkos kintamųjų (tarp jų – slaptažodžių ir API raktų) šimtuose klientų aplinkų.
Kaip tai vyksta techniniu lygmeniu
Praktikoje tiekimo grandinės ataka retai vyksta vienu paprastu būdu – dažniausiai tai kelių žingsnių procesas, kuris gali užtrukti mėnesius planavimo.
Vienas populiariausių metodų – „dependency confusion” arba priklausomybių sumaišymas. Užpuolikas sukuria paketą su tokiu pačiu pavadinimu, kaip vidinis, privatus paketas, naudojamas organizacijos viduje, tik įkelia jį į viešą repozitoriją su didesniu versijos numeriu. Kai kūrimo sistema bando atsisiųsti priklausomybę, ji gali automatiškai pasirinkti „naujesnę” versiją iš viešo šaltinio, net nesuprasdama, kad tai kenkėjiškas kodas.
Kitas metodas – „typosquatting”, kai kuriami paketai su pavadinimais, primenančiais populiarius (pvz. „reqeusts” vietoje „requests”). Nekantrus arba klaidingai parašiusi pavadinimą kūrėja komanda netikėtai instaliuoja kenkėjišką kodą.
Sudėtingesnis būdas – kompiliavimo grandinės kompromitavimas. Čia užpuolikas nesistengia keisti pačio kodo repozitorijoje, o vietoj to atakuoja CI/CD pipeline’ą arba build serverį. Tokiu atveju net švarus, peržiūrėtas kodas gali būti paverstas kenkėjišku vykdomu failu jau kompiliavimo metu, ir jokia kodo peržiūra to neatskleis, nes source kode viskas atrodo tvarkinga.
Taip pat egzistuoja atakos, orientuotos į paketo priežiūrėtoją (maintainer) – užpuolikai gali pasiūlyti „pagalbą” prižiūrėti seną, apleistą projektą, laikui bėgant įgydami priežiūrėtojo teises ir tada tyliai įterpdami kenkėjišką logiką.
Open source ekosistemos rizika, kurios dažnai nepastebime
Didžioji dalis šiuolaikinės programinės įrangos yra sukurta iš atvirojo kodo blokelių. Tai neįtikėtinai efektyvu – kam rašyti autentifikacijos logiką nuo nulio, kai jau egzistuoja gerai testuota biblioteka? Bet ši efektyvumas turi savo kainą.
Vidutinis projektas gali turėti tiesioginių priklausomybių sąrašą, kuris išsišakoja į šimtus tranzityvinių (netiesioginių) priklausomybių. Tu galbūt patikrinai penkis paketus, kuriuos aiškiai įrašei į savo package.json ar requirements.txt failą, bet tie penki paketai savo ruožtu remiasi dešimtimis kitų, apie kurias tu nė nesusimąstei.
Didžioji dalis atvirojo kodo projektų prižiūrimi savanoriškai, dažnai vieno ar dviejų žmonių, be finansavimo, be formalaus saugumo audito. Tai reiškia, kad kritinės infrastruktūros komponentai (kaip buvo su „log4j” atveju) gali remtis kodu, kurį prižiūri žmogus, dirbantis su tuo savo laisvalaikiu, po darbo, kartais be jokio atlygio.
Log4Shell (log4j pažeidžiamumas) 2021 metais parodė, kaip viena nedidelė logging biblioteka, naudojama beveik visur Java ekosistemoje, gali sukelti globalią saugumo krizę. Tai nebuvo klasikinis tiekimo grandinės kompromitavimas, bet parodė, kaip glaudžiai visi esame susiję per bendrus komponentus.
Ką realiai galima daryti apsisaugant
Nėra magiško sprendimo, kuris pilnai eliminuotų riziką, bet yra konkretūs žingsniai, kurie žymiai sumažina paviršių atakoms.
Software Bill of Materials (SBOM). Turėk pilną, aktualų sąrašą visų komponentų, naudojamų tavo produkte – tiesioginių ir tranzityvinių. Neįmanoma apsaugoti to, ko nežinai, kad turi. SBOM generavimas turėtų būti automatizuotas CI/CD proceso dalis, ne rankinis darbas kartą per metus.
Priklausomybių fiksavimas (pinning). Naudok lock failus (package-lock.json, poetry.lock, Gemfile.lock) ir venk automatinio priklausomybių atnaujinimo į naujausią versiją be peržiūros. „Latest” versija skamba patraukliai, bet gali reikšti neapžiūrėtą, potencialiai kompromituotą kodą.
Vidinis paketų registras. Jei organizacija naudoja privačias priklausomybes, būtinai sukonfigūruok savo build sistemą taip, kad ji pirmiausia tikrintų vidinį registrą, o ne automatiškai kreiptųsi į viešą, kur galimas dependency confusion.
Kodo pasirašymas ir verifikavimas. Naudok kriptografinį pasirašymą (pvz. Sigstore, cosign) tiek savo išleidžiamiems artefaktams, tiek priklausomybėms, kurias naudoji. Tikrink parašus prieš vykdymą, ne tik pasitikėk, kad failas atkeliavo iš „patikimo” šaltinio.
CI/CD grandinės griežtinimas. Build serveriai turėtų būti izoliuoti, su minimaliomis teisėmis, be nuolatinės prieigos prie produkcinės aplinkos. Naudok efemeriškus (ephemeral) build agentus, kurie sunaikinami po kiekvieno vykdymo – tai apsunkina persistenciją, jei užpuolikas ir gauna prieigą.
Reguliarus priklausomybių auditas. Įrankiai kaip Dependabot, Snyk, OWASP Dependency-Check ar Trivy turėtų būti standartinė dalis pipeline’o, automatiškai skanuojanti žinomus pažeidžiamumus (CVE) tavo priklausomybėse.
Trečiųjų šalių rizikos vertinimas. Kai pasirenki naują tiekėją ar paslaugų teikėją, klausk apie jų saugumo praktikas, incidentų valdymo planus ir prieigos kontrolę. Tai turėtų būti standartinė procedūra, ne formalumas.
Zero trust principo taikymas. Net vidinei infrastruktūrai netaikyk automatinio pasitikėjimo. Tiekėjo prieiga turėtų būti griežtai apribota tik tuo, kas būtina, su nuolatiniu monitoringu ir laiko ribojimu.
Kai grandinė tampa stipresnė nei bet kuris jos elementas
Tiekimo grandinės saugumas nėra vienkartinis projektas, kurį įgyvendini ir pamiršti – tai nuolatinis, iteratyvus procesas, kuris keičiasi tiek pat greitai, kiek keičiasi ir jūsų kodo bazė. Kiekviena nauja priklausomybė, kiekvienas naujas tiekėjas, kiekvienas naujas integracijos taškas – tai naujas potencialus svoris, kurį reikia įvertinti.
Realybė yra tokia, kad visiškai eliminuoti riziką neįmanoma, kol organizacija naudoja svetimą kodą (o šiandien tai daro visos). Tikslas nėra absoliutus saugumas, o rizikos valdymas – suprasti, kur tavo grandinė yra plonesnė, sekti tas vietas atidžiau, ir turėti planą, kaip reaguoti, kai (ne jei) kažkas nutiks.
Praktiškai tai reiškia investuoti į matomumą prieš investuojant į apsaugą – neįmanoma apginti to, ko nesupranti. Sukurk kultūrą, kur kūrėjai klausia „kodėl mums reikia šios bibliotekos”, o ne tik „ar ji veikia”. Skatink komandą tikrinti maintainer’io reputaciją, žiūrėti į paskutinio commit datą, vertinti, kiek žmonių aktyviai prižiūri projektą, prieš pridedant jį prie priklausomybių sąrašo.
Ir galiausiai – nepasitikėk vien technologija. Geriausia gynyba prieš tiekimo grandinės atakas yra kombinacija techninių priemonių (SBOM, pasirašymas, izoliacija) ir organizacinės kultūros, kurioje saugumas nėra vienos komandos problema, o visos organizacijos atsakomybė, pradedant nuo pirmo commit’o iki paskutinio deployment’o.






