Supply Chain

Beroendesäkerhet

Så styr du tredjepartspaket med inventering, lockfiles, versionsdisciplin och granskning av uppströms risk.

Beroendesäkerhet är praxisen att förstå, kontrollera och kontinuerligt granska de tredjepartspaket som ett programprojekt förlitar sig på. Moderna applikationer har ofta hundratals direkta beroenden och tusentals transitiva, var och en av vilka kör med samma förtroendesnivå som applikationens egen kod. God beroendesäkerhet innebär att upprätthålla en korrekt inventering av vad som används, begränsa hur versioner löses upp, granska uppströms risk innan nya paket når bygget och hålla beroenden uppdaterade så att säkerhetsfixar tillämpas snabbt.

Lärandemål

Det här ska du kunna efter genomläsning.
  • Förklara hur direkta och transitiva beroenden förändrar applikationens förtroendegräns.
  • Beskriva hur lockfiles, pinning och uppdateringsflöden minskar överraskningar i leveranskedjan.
  • Känna igen vanliga angrepp vid införande av paket, som dependency confusion och typosquatting.

I korthet

En snabb mental modell innan du går på djupet.
Inventering
  • Direkta paket
  • Transitiva paket
  • Lockfiles
Ändringskontroll
  • Pinning
  • Uppdateringar
  • PR-granskning
Leveranskedjerisk
  • Dependency confusion
  • Typosquatting
  • Tredjepartsrisk

Kärnidén

Varje beroende är lånad kod som kör med någon nivå av förtroende i din miljö. Det betyder att beroendehantering inte bara är underhåll, det är en säkerhetskontroll över vilken extern kod som får komma in i produkten. Ett enda komprometterat eller sårbart paket kan exponera hela applikationen för angrepp, oavsett hur noggrant applikationens egen kod skrevs.

Målet är inte att undvika alla beroenden. Målet är att veta vad du förlitar dig på, begränsa hur versioner löses upp, göra riskabla ändringar synliga innan de sprids över byggen och ha en process för att agera på nya sårbarhetsavslojanden utan att varje uppdatering blir en kris.

Kontrollpunkter

  • Håll en korrekt inventering över direkta och transitiva beroenden så att nya tillagg är synliga och borttagningar spåras.
  • Använd lockfiles och versionspinning för att minska oväntade upplösningsändringar och göra byggen repeterbara.
  • Granska var paket kommer ifrån, vem som underhåller dem och om privata namn kan skuggas av publika paket med samma namn.

Basnivå

  • Ta bort bibliotek som inte längre används i stället för att bära tyst risk vidäre på obestämd tid.
  • Uppdatera beroenden i en regelbüunden rytm så att säkerhetsfixar inte samlas till större, riskablare ändringar.
  • Behandla paketkllor och registry-inställningar som en del av releaseytan, inte bara som utvecklarbekvämlighet.

Signaler att bevaka

Mönster som är värda att undersöka vidare.
  • Ett nytt paket läggs till utan tydligt funktionellt skäl eller ägare.
  • Builds löser olika versioner trots att applikationskoden inte har ändrats.
  • Paketnamn, källor eller maintainers ändras utan granskning.

FÖRDJUPNING

Beroenden

Beroenden inkluderar både de paket en utvecklare explicit installerar (direkta beroenden) och alla paket som dessa paket i sin tur beror på (transitiva beroenden). Den transitiva beroendegrafiken är vanligtvis mycket större än den direkta. En Node.js-applikation med femtio direkta beroenden kan ha två tusen transitiva. De flesta av dessa transitiva paket anropas aldrig direkt av applikationskoden, men de finns närvarande i bygget och sårbarheter i dem kan fortfarande utnyttjas.

Att behandla val av beroenden som ett arkitekturbeslut, inte bara en kodningsgenkväg, förändrar vårdstandarden som tillämpas. När en utvecklare lägger till ett paket för att lösa ett tioradersproblem bör rätt frågor vara. Har det här paketet en sund underhållshistorik? Har det ett rimligt antal transitiva beroenden? Kommer det från en pålitlig utgivare?

Attackytan för ett beroende är hela dess kodbas, inte bara de funktioner som applikationen anropar. Skadlig kod i ett npm-paket som körs under installationen (via installationsskript) utförs oavsett om applikationen någonsin anropar det paketets publika API. Det är därför dependency confusion och typosquatting-attacker är så effektiva.

Beroendeindikatorerna på hälsa som förtjänar uppmärksamhet inkluderar. Tid sedan senaste release, antal öppna säkerhetsproblem, om paketet underhålls av en enda anonym författare och om paketet är ett djupt transitivt beroende som teamet inte har direkt kontroll över.

Lockfiles

En lockfile registrerar den fullständiga, lösta beroendegrafiken som ett bygge använde vid en specifik tidpunkt. Den specificerar inte bara de direkta beroendena och deras versionsbegränsningar utan de exakta lösta versionerna av varje transitivt beroende. När en lockfile committed till versionskontroll och används konsekvent installerar varje utvecklare och varje CI-körning exakt samma paket.

Utan en lockfile löser pakethanterare beroendegrafiken på nytt vid varje installation. Om begränsningarna tillåter intervall kan upplösaren välja olika versioner på olika dagar när nya versioner publiceras. Lockfilen eliminerar denna icke-determinism genom att fasta hela grafiken tills lockfilen medvetet uppdateras.

Lockfiles är en säkerhetskontroll eftersom de förhindrar tyst utbyte av beroendeversioner. Om en skadlig version av ett paket publiceras mellan två byggen, och inget bygge har en lockfile, kan det andra bygget tyst hämta den skadliga versionen. Med en lockfile använder det andra bygget samma exakta version som det första tills någon explicit kör uppdateringskommandot.

Lockfiles måste committed till versionskontroll och hållas uppdaterade. En lockfile som är månader eller år gammal kan innehålla lösta versioner med kända sårbarheter som sedan dess har åtgärdats i nyare releases. Uppdateringsprocessen för lockfilen bör automatiseras och vara regelbüunden.

Pinning

Pinning specificerar en exakt version av ett beroende snarare än ett versionsintervall. I stället för att specificera requests>=2.28.0 specificerar ett pinnat beroende requests==2.28.2. Pinning förhindrar pakethanteraren från att någonsin lösa till en annan version utan en explicit ändring av beroendespecifikationen. Det är en starkare garanti än en lockfile för direkta beroenden.

Pinning är särskilt viktigt för säkerhetsmässigt känsliga beroenden, för beroenden högst upp i bygggrafen och i miljöer där reproducerbarhet är kritisk. Att pinna basimages i Dockerfiler (med digestreferenser snarare än rörliga taggar) och att pinna GitHub Actions till ett specifikt commit-SHA är tillämpningar av samma princip utöver bara språkpaket.

Motvikten med pinning är att pinnade versioner inte automatiskt får säkerhetsfixar. Ett paket pinnat till en specifik version stannar vid den versionen tills någon explicit uppdaterar det. Det innebär att team måste ha en process för att regelbüundet granska och uppdatera pinnade versioner. Automation som Dependabot eller Renovate kan öppna pull requests när en pinnad version har en nyare release tillgänglig.

Ett vanligt misstag är att förväxla pinning med säkerhet. Pinning till en version med en känd sårbarhet ger reproducerbarhet men inte säkerhet. Värdet med pinning är att det gör beroendegrafiken stabil och kontrollerad, vilket är en förutsättning för andra säkerhetspraxis som SCA-scanning och sårbarhetstriage.

Uppdateringar

Regelbüdna beroendeuppdateringar är den operativa motvikten till pinning och lockfiles. Pinning och lockfiles skapar en stabil, kontrollerad beroendegraf, uppdateringar håller den grafiken från att ackumulera kända sårbarheter och kompatibilitetsskulder över tid. Den värsta uppdateringssituationen är när beroenden aldrig uppdateras tills en kritisk sårbarhet tvingar fram en akutsvar.

Automatiserade uppdateringsverktyg som Dependabot (GitHub), Renovate Bot eller PyUP (Python) skapar pull requests när nya versioner av pinnade beroenden är tillgängliga. PR:n inkluderar versionsändringen, ändringsloggen för den nya releasen och eventuella säkerhetsmeddelanden. Att granska och slå ihop dessa PR:er regelbüundet håller beroendegrafiken aktuell i små, lågrisksteg.

Uppdateringsstrategier bör skilja mellan säkerhetsuppdateringar och icke-säkerhetsuppdateringar. Säkerhetsuppdateringar för kritiska eller höga CVE:er bör behandlas som högprioriterat arbete med kort svarstid. Icke-säkerhetsundehållsuppdateringar kan skjutas upp till en regelbüunden uppdateringsrytm.

Brytande ändringar i beroendeuppdateringar är den huvudsakliga anledningen till att team skjuter upp uppdateringar under långa perioder. En stor versionsuppdatering som kräver kodändringar i applikationen är legitimt arbete som konkurrerar med funktionsutveckling. Den praktiska lösningen är att investera i god testtäckning så att effekten av en beroendeuppdatering är synlig i CI-resultat.

Dependency confusion

Dependency confusion är en attack där ett skadligt paket med samma namn som ett internt privat paket publiceras till ett offentligt registry. När byggssystemets paketupplösningslogik kontrollerar offentliga registries utöver det privata kan det lösa det offentliga paketet istället för det privata, särskilt om den offentliga versionen har ett högre versionsnummer.

Denna attack demonstrerades offentligt 2021 av forskaren Alex Birsan, som använde den för att kompromittera byggsystemen hos flera stora teknologiföretag. Genom att publicera paket med samma namn som företagens interna paket till npm, PyPI eller RubyGems vid högre versionsnummer fick han dessa företags byggsystem att hämta och köra hans kod under byggen.

Försvar mot dependency confusion fungerar på registry-konfigurationsnivån. Det mest tillförlitliga försvaret är att avgränsa alla privata paket under ett namnrum som inte kan finnas i offentliga registries (för npm innebär detta att använda avgränsade paket som @mittföretag/utils). För ekosystem som inte stöder namnrymdsplanering inkluderar försvar att konfigurera pakethanteraren att bara använda det interna registry för specifika paketnamn.

Regelbüunden granskning av namnen på interna paket mot offentliga registries är en användbar detektivåtgärd. Om ett internt paketnamn finns i ett offentligt registry utan organisationens vetskap kan det ha registrerats av en angripare i förberedelse för en dependency confusion-attack.

Typosquatting

Typosquatting är registreringen av paketnamn som liknar populära eller ofta använda paket visuellt, med avsikt att installeras när en utvecklare gör ett stavfel. Det typosquattade paketet kan se funktionellt ut vid första användning men innehålla skadlig kod som körs vid installationstillfallet, exfiltrerar miljövariabler (som ofta innehåller autentiseringsuppgifter) eller etablerar en bakdörr.

Det effektivaste försvaret mot typosquatting är sträng beroendegenomgång innan nya paket läggs till ett projekt. En utvecklare som skriver reqeusts istället för requests i en krävningsfil bör stoppas av en PR-granskningsprocess, inte genom att upptäcka att det installerade paketet var skadligt.

Pakethanterare och registries har implementerat vissa skydd mot typosquatting, inklusive algoritmer som blockerar registrering av namn som liknar populära paket och ned-tagningsprocedurer för rapporterade skadliga paket. Dessa kontroller är dock reaktiva och ofullständiga. Ekosystemets enorma storlek innebär att vissa typosquattade paket alltid kommer att finnas i offentliga registries.

I CI/CD-miljöer är vitlistning av paket ett starkt försvar som eliminerar både typosquatting- och dependency confusion-risk. En vitlista specificerar de exakta paket som är tillåtna i ett bygge, ett paket som inte finns på listan misslyckas med bygget. Detta är praktiskt för stabila kodbaser med kända, sällan förändrade beroenden.

Tredjepartsrisk

Tredjepartsrisk omfattar hela omfånget av sätt på vilka externa paket kan introducera risk utöver kända CVE:er. Detta inkluderar komprometterade underhållare, där en angripare tar över ett legitimt paketkonto och publicerar en skadlig version, övergivna projekt, plötsliga ägarbyten och osäkra standardinställningar.

Kompromettering av underhållarkonton är en särskilt farlig risk eftersom den ersätter ett betrott paket med ett skadligt på ett sätt som ser legitimt ut för automatiserade system. Skö att flagga om paketet med en verifikationskontroll så att om din checksum matchar, och angriparen publicerade via det legitima kontot, finns det ingen automatisk signal. Försvaret är vaksomhet. Övervaka oväntade nya releaser från paket i aktivt bruk.

Bedömning av ett beroends löpande hälsa kräver att titta bortom CVE-databasen. Mått som tid sedan senaste commit, antal öppna osådda säkerhetsproblem, svarstid på säkerhetsrapporter och antal aktiva underhållare är alla signaler om framtida risk.

Beslutet om att använda eller fortsätta använda ett tredjepartspaket bör vara ett dokumenterat, återkallbart beslut. Team som antar paket utan att ta hänsyn till långsiktigt underhåll och sedan fortsätter använda dem på obestämd tid utan granskning ackumulerar risk som inte är synlig i någon sårbarhetsscanning.