Du kan gennemgå hver eneste linje, dit eget team skriver, og alligevel sende en angribers kode i produktion. Det meste af det, der kører, er skrevet af fremmede, kom ind automatisk og er aldrig blevet læst af nogen på din side. Det er forsyningskæden — og at angribe den er i dag den effektive vej ind.

Hvad et forsyningskædeangreb er

Et forsyningskædeangreb på software kompromitterer noget, du afhænger af, i stedet for noget, du selv har bygget. Angriberen rører aldrig dine servere. De får deres kode ind i en pakke, et build-værktøj eller en opdatering, som du selv installerer frivilligt — og din egen automatik bærer den resten af vejen.

Det er ren aritmetik, der har flyttet det her fra specialistbekymring til hovedbekymring. At angribe en virksomhed direkte betyder at komme forbi det forsvar, den nu har, én virksomhed ad gangen. At kompromittere en pakke, ti tusind virksomheder afhænger af, betyder at gøre arbejdet én gang. Er bare en brøkdel af dem værd at bestjæle, er afkastet ikke i samme størrelsesorden.

Det er også stille. En ondsindet afhængighed knækker ikke buildet — en payload, der ødelagde noget, ville som regel blive fundet og fjernet samme dag. Den virker upåklageligt og gør én ting ekstra.

De fem veje ind

De er ikke lige sandsynlige, og de kan ikke forsvares lige godt. Sorteret groft fra mest almindelig til mest avanceret:

1. Udgiv noget ondsindet, og vent på en fejl

Det billigste angreb: registrér en pakke, en udvikler plausibelt kunne komme til at installere, og vent. Det findes i tre former, hver med sin mekanisme — typosquatting udnytter et forkert tastet navn, slopsquatting udnytter et navn, en AI-assistent har opfundet, og dependency confusion udnytter, at dit build spørger et offentligt registry om et internt navn.

Det, der binder dem sammen, er, at intet blev kompromitteret. Angriberen udgav en pakke under et navn, ingen havde taget. Der er ingen sårbarhed at lappe, og derfor er opdagelse det eneste forsvar.

2. Overtag en pakke, folk allerede stoler på

I stedet for at få dig til at installere noget nyt: tag kontrol over noget, du allerede har. Den sædvanlige vej er vedligeholderens konto — en genbrugt adgangskode, intet to-faktor-login, en phishing-mail god nok til at virke på en træt person. En hvilende pakke med en inaktiv vedligeholder er den udgave af det her, der er lettest at få øje på i forvejen.

Det lærerige tilfælde er ua-parser-js i 2021 — en pakke, der blev hentet millioner af gange om ugen. En angriber udgav ondsindede versioner fra vedligeholderens egen konto, og de var legitime udgivelser i enhver teknisk forstand: korrekt navn, korrekt konto, udgivet med vedligeholderens egne loginoplysninger ad den normale vej. Alt, der stolede på navnet, stolede på payloaden.

Det er argumentet for en lockfile. Det er versionslåsen, der redder dig her: en ondsindet ny udgivelse kommer ikke ind, før nogen bevidst opdaterer, og den opdatering er en diff, nogen kan kigge på. Den integrity hash, der står ved siden af, dækker det sjældnere tilfælde — et registry, et spejl eller en proxy, der leverer andre bytes for en version, du allerede har låst.

3. Bliv vedligeholderen

Den mest tålmodige udgave — og den, der bør bekymre dig mest. Vedligeholdere af open source er ofte ulønnede, overbebyrdede og på udkig efter hjælp. Det er ikke en svaghed, man skal se ned på; det er det vilkår, det meste af den software, du kører, bliver produceret under. Det er også en social angrebsflade.

event-stream i 2018 er den enkle udgave: den oprindelige vedligeholder, som ikke længere brugte pakken, overdrog den til en frivillig, der havde bidraget hjælpsomt. Den frivillige tilføjede derefter en ny afhængighed, og payloaden lå i den — rettet mod én bestemt kryptovaluta-tegnebog. Ingen af dem, der stolede på event-stream, havde hørt om den pakke, der faktisk angreb dem.

xz-utils i 2024 (CVE-2024-3094) er den udgave, der bør ændre, hvordan du tænker om det her. En konto brugte cirka to år på at lave ægte, nyttige bidrag til et komprimeringsbibliotek, opnåede gradvist status som medvedligeholder og indførte derefter en bagdør i release-tarballs — ikke i kildekode-repositoryet — rettet mod SSH-dæmonen på de distributioner, der havde patchet den til at linke biblioteket indirekte. Den blev fundet, før den nåede de fleste stabile distributioner, og den blev fundet ved et tilfælde: en ingeniør, der undersøgte et halvt sekunds forsinkelse i en login-måling.

Vær ærlig om, hvad det betyder. Ingen afhængighedsscanner ville have fanget den den dag, den blev udgivet. Koden lå i en betroet pakke, udgivet af en betroet vedligeholder, og der fandtes ingen advarsel. Det, der fanger den klasse, er det natlige gentjek, efter advarslen lander — målt i timer, ikke i de uger, der går til din næste planlagte scanning.

4. Kompromittér buildet i stedet for koden

Kildekoden kan være ren, og artefaktet alligevel ondsindet, hvis dét, der laver kildekode om til artefakt, er kompromitteret.

SolarWinds i 2020 er det kanoniske tilfælde: build-systemet blev ændret, så ondsindet kode blev indsat under kompileringen. Resultatet blev signeret med virksomhedens egen nøgle og distribueret gennem den normale opdateringskanal. Kunderne verificerede signaturen, og signaturen var ægte.

Codecov i 2021 er den udgave, de fleste virksomheder reelt bør bekymre sig om, for den krævede ingen statslig indsats. Et udbredt CI-script blev ændret, så det sendte miljøvariablerne fra hvert build, der kørte det, til en angribers server. CI-miljøer indeholder cloud-nøgler, registry-tokens og deployment-nøgler — så udbyttet var en høst af andre virksomheders hemmeligheder.

Derfor fortjener pipeline-konfigurationen samme gennemgang som applikationskode. Den kører med flere rettigheder end din applikation.

5. Vedligeholderen vender sig

Sjældent, og værd at nævne, fordi det bryder den mentale model. I 2022 tilføjede forfatteren til node-ipc kode til sin egen pakke, der overskrev filer på maskiner med bestemte IP-adresser, som politisk protest. Ingen kompromittering, ingen angriber — den person, pakken stolede på ved design, besluttede at gøre skade.

Det kan du ikke forsvare dig imod med bedre autentificering. Det er et argument for at fastlåse versioner og for ikke at opdatere afhængigheder i blinde, hvilket er en mindre behagelig konklusion, end det lyder.

Hvorfor det er reelt svært at forsvare

Tre egenskaber, og det er værd at se dem i øjnene:

Du kan ikke læse det. En typisk applikation opløses til flere hundrede pakker, og de fleste af dem har ingen på dit team valgt. At gennemgå dem er ikke et spørgsmål om disciplin; det kan ikke lade sig gøre.

Den farlige kode kører ofte, før du bruger den. Flere økosystemer udfører installationsscripts, så en pakke kan handle uden nogensinde at blive importeret. Vinduet mellem "npm install" og "hov, hvad er det her" er dér, hvor payloaden allerede har kørt.

Tillid er transitiv, men opmærksomhed er ikke. Du valgte fyrre pakker. De trak seks hundrede med. Din tillid til de fyrre var en beslutning; de seks hundrede arvede den, uden at nogen besluttede noget.

Hvad der reelt sænker risikoen

I omtrentlig rækkefølge efter, hvor meget de giver pr. indsats:

  • Vid, hvad du kører. Hvert eneste forsvar herunder er utilgængeligt, indtil du kan opremse dine afhængigheder. Det er det praktiske formål med en SBOM — ikke selve dokumentet, men evnen til at svare på "er vi ramt?" på minutter.
  • Commit en lockfile med integritets-hashes. Præcise versioner, registreret i versionsstyringen, så en ændring dukker op i en pull request og ikke på én maskine. Den ene vane lukker genudgivelses-angrebet og gør builds reproducerbare.
  • Kør ikke installationsscripts, du ikke har bedt om. npm ci --ignore-scripts i CI fjerner udførelsesvejen før import for alt, der ikke reelt har brug for den.
  • Begræns, hvad en kompromittering kan nå. Kortlivede cloud-nøgler via OIDC i stedet for langlivede nøgler i repository secrets; færrest mulige rettigheder på pipeline-tokens; adskil bygning og udrulning. Codecov var alvorlig, fordi CI lå inde med det hele.
  • Opdag hurtigt, og udskift så nøglerne. Gå ud fra, at noget slipper igennem. Det tal, der betyder noget, er tiden fra en ondsindet pakke bliver udgivet, til du ved, den er i dit træ — og derefter, om du får udskiftet de nøgler, der blev eksponeret.
  • Opdatér bevidst, ikke i blinde. Automatiske afhængighedsopdateringer er samlet set en gevinst, men at merge dem ulæst genindfører præcis det problem, de skulle løse.

Hvad en scanner kan og ikke kan

Værd at sige rent ud, for "forhindrer forsyningskædeangreb" er en påstand, branchen bruger løst, og den overlever ikke mødet med xz-utils-forløbet.

En scanner kan ikke forhindre, at en pakke er ondsindet. Udgivelsen sker på et registry, vi ikke kontrollerer, på et tidspunkt angriberen vælger. Intet, der er installeret hos dig, ændrer på det.

Det, den kan, er at stoppe dig i at sende den i produktion — og at forkorte vinduet. Det er en smallere påstand og en mere brugbar. Konkret: fange en kendt ondsindet eller typosquattet pakke, før den bliver merget; markere en helt ny pakke, ingen har indestået for; finde den nøgle, der lækkede, mens rettelsen stadig er billig; og — det, der betyder noget over for de tålmodige angreb — gentjekke det, du allerede kører, mod det, verden fandt ud af i nat, så afstanden mellem offentliggørelse og din viden er timer.

Ved et angreb som xz-utils er netop det sidste hele det forsvar, en almindelig virksomhed har. Det realistiske mål er ikke at være uangribelig. Det er ikke at være den virksomhed, der stadig kører den tre uger efter, at alle andre vidste besked.

Hvad er et forsyningskædeangreb på software?

Et forsyningskædeangreb på software kompromitterer noget, du afhænger af, i stedet for noget, du selv har bygget — en pakke, et build-værktøj eller en opdatering, som du installerer frivilligt, så din egen automatik bærer angriberens kode resten af vejen. Det er effektivt for angribere, fordi det at kompromittere én udbredt pakke rammer hver eneste virksomhed, der afhænger af den, og det er stille, fordi en payload, der knækkede buildet, ville som regel blive fundet og fjernet samme dag.

Hvilke typer forsyningskædeangreb findes der?

Fem veje dækker de fleste forsyningskædeangreb. En angriber udgiver noget ondsindet og venter på en fejl, hvilket dækker typosquatting, slopsquatting og dependency confusion. De overtager kontoen bag en pakke, du allerede stoler på. De bruger måneder på at blive en legitim vedligeholder, som det skete med xz-utils. De kompromitterer build-systemet i stedet for kildekoden, som med SolarWinds og Codecov. Eller vedligeholderen selv vender sig, som med node-ipc. Den første er langt den mest almindelige; den tredje er den sværeste at opdage.

Kan en scanner forhindre et forsyningskædeangreb?

Nej. En scanner kan ikke forhindre et forsyningskædeangreb, og det er værd at være præcis om hvorfor. Udgivelsen sker på et registry, ingen udefra kontrollerer, på et tidspunkt angriberen vælger, så intet, der er installeret hos dig, forhindrer en pakke i at være ondsindet. Det, en scanner kan, er at stoppe dig i at sende den i produktion og at forkorte vinduet: fange en kendt ondsindet eller typosquattet pakke, før den bliver merget, markere en pakke, der er for ny til at have en historie, og gentjekke det, du allerede kører, mod advarsler offentliggjort i nat, så afstanden mellem offentliggørelse og din viden er timer og ikke uger.

Hvordan beskytter jeg mit projekt mod forsyningskædeangreb?

Begynd med at kunne opremse dine afhængigheder, for hvert andet forsvar afhænger af det. Commit en lockfile, så præcise versioner er låst i versionsstyringen, og en ondsindet ny udgivelse ikke kan komme ind, uden at nogen godkender en diff. Slå installationsscripts fra i CI for de pakker, der ikke har brug for dem. Begræns, hvad en kompromittering kan nå, ved at bruge kortlivede cloud-nøgler i stedet for langlivede nøgler i repository secrets. Gå derefter ud fra, at noget slipper igennem, og mål, hvor hurtigt du ville vide det.

Hvad lærte xz-utils-bagdøren os?

xz-utils-bagdøren viste, at de alvorligste forsyningskædeangreb ikke ligner angreb, mens de står på. En konto brugte cirka to år på at lave ægte bidrag til et komprimeringsbibliotek, opnåede status som medvedligeholder og indførte derefter en bagdør i release-tarballs i stedet for i kildekode-repositoryet. Ingen afhængighedsscanner ville have fanget den den dag, den blev udgivet, for koden lå i en betroet pakke fra en betroet vedligeholder uden nogen advarsel imod sig. Den blev fundet af en ingeniør, der undersøgte et halvt sekunds forsinkelse i en login-måling. Det forsvar, en almindelig virksomhed har, er ikke forebyggelse, men hastighed: at gentjekke det, du kører, mod det, verden fandt ud af i nat.

Hvor CodeControl passer ind

CodeControl forbinder sig til dit GitHub og arbejder med opdagelses-halvdelen af listen ovenfor. Det opløser hele dit afhængighedstræ ud fra lockfiles, matcher det mod malware-feedet og offentliggjorte advarsler, og markerer typosquattede pakker, dependency confusion og helt nye pakker, ingen endnu har indestået for. Det scanner inden for et minut efter et push og tjekker hver pull request for committede nøgler, så en læk bliver fanget, mens rettelsen stadig er en branch-omskrivning. Og det gentjekker din eksisterende fortegnelse hver nat mod nyoffentliggjorte udnyttelsesdata — hvilket er det eneste forsvar, en almindelig virksomhed har mod de tålmodige angreb.

Det forhindrer ikke en pakke i at være ondsindet, og den tekniske beskrivelse siger tydeligt, hvad det ellers ikke kan se. Det, det ændrer, er, hvor længe du kører den uden at vide det.