Bij softwareontwikkeling zijn we afhankelijk van zowel eigen componenten als artefacten of artefacten van derden. Een flexibel Dependency Management is essentieel voor moderne software. Pakketbeheerders zoals NPM, Maven, pit or NuGet worden vaak gebruikt om softwareafhankelijkheden te specificeren. Deze tools zijn ontworpen met gemak en gebruiksgemak in gedachten, niet met beveiliging.
Het probleem
Het probleem is dat flexibiliteit en gebruiksgemak voor ontwikkelaars de slechteriken zijn, die software-afhankelijkheden als onweerstaanbaar charmant voor hun bedrijf beschouwen. Het resultaat: slechte acteurs volgden alle mogelijke aanvalspaden die hier worden weergegeven. Bron: "Backstabber's Knife Collection: een overzicht van aanvallen op de supply chain van open source-software"
In dit bericht zullen we ons concentreren op het gebruik van open-versiedeclaraties, in die zin dat de gedownloade versie niet vaststaat, maar tot een bepaald bereik moet behoren. Tijdens het bouwen wordt de hoogste bestaande versie die compatibel is met het opgegeven versiebereik gekozen en gedownload/geïnstalleerd door de pakketbeheerder.
Laten we open declaraties illustreren in afhankelijkheidsdeclaraties voor verschillende pakketbeheerders:
-
- NPM: package.json
{
...
“afhankelijkheden”: {
...
“accepteert”: “>=1.3.8”,
“lodash”: “~4.16.0”,
...
},
...
}
De grootste bestaande versie niet lager dan 1.3.8 voor het accept-pakket zal worden geïnstalleerd, evenals de grootste 'patch'-update voor lodash in het bereik van 4.16.x.
-
- meester: pom.xml
...
...
commons-io
commons-io
UITGAVE
...
...
De laatst beschikbare release die beschikbaar is voor commons-io (jar-bestand) wordt als afhankelijkheid toegevoegd.
-
- Pip: setup.py
...
opgericht(
...
install_requires=['peperkorrel', 'launchpadlib'],
...
)
...
Dergelijke openversieschema's hebben een goede en een slechte kant. Het goede is dat er nieuwere versies zijn doorgaans bevatten functionele en kwaliteitsverbeteringen, bugfixes en beveiligingspatches, die automatisch worden geüpgraded. Houd er rekening mee dat voor de meeste projecten in de echte wereld de oplossingen niet worden teruggezet naar eerdere kleine releases, behalve misschien voor catastrofale beveiligingsproblemen. Open versies zijn ook goed voor gebruik in bibliotheken, om het aantal versies te verminderen dat moet worden geïnstalleerd als alle afhankelijkheden zijn opgelost.
Maar open versiereeksen hebben een slechte kant. U weet niet precies welke versies tijdens de build worden geïnstalleerd en builds zijn niet herhaalbaar. En er is ook een donker kant met open versies. Als een slechte actor erin slaagt een kwaadaardige component in de openbare repository te publiceren met een hoge versie die compatibel is met uw open bereik, zal uw volgende build de kwaadaardige component bevatten, en misschien zelfs malware uitvoeren in installatiescripts die mogelijk automatisch worden uitgevoerd. Het verdoezelen van de aanvalslading is een kunst.
Dit staat bekend als de Gebrek aan versievastzetten kwestie.
Kwaadwillenden proberen altijd kwaadaardige versies van populaire open-sourcepakketten te plaatsen. Ze kunnen toegang krijgen tot de sleutels voor pakketrepositories in een geheim lek; ze gebruiken vaak social engineering of verbergen een geneste kwaadaardige afhankelijkheid in een ogenschijnlijk nuttige pull requestZelfs een paar auteurs besluiten op een dag dat de wereld niet eerlijk is en bijten hun cliënten met protestmateriaal in hun eigen pakketten!
Stel je nu voor dat je voor een organisatie werkt die gebruik maakt van interne componenten en open source-componenten.
Als een slechte actor de naam van dergelijke interne componenten kent, kan hij/zij erin slagen een component met dezelfde naam in de openbare repository te publiceren. Veel pakketbeheerders krijgen eerst de openbare componenten, en als de versie correct is gekozen en de versie in uw aangegeven afhankelijkheid open is, boem! Dit probleem heeft een naam Afhankelijkheid verwarring.
Laten we een voorbeeld laten zien. Stel dat we in ons NPM-project afhankelijk zijn van een private component:
-
- NPM: package.json
{
"name": "mijn-project",
...
“afhankelijkheden”: {...
“mijn-privé-dep”: “>=1.0.0”,...
}
...
}
De aanvaller kan een hoge hoofdversie van my-private-dep (zoals 99.0.0) maken en deze publiceren in de openbare npm-repository, met zijn eigen nepaccount (de aanvaller hoeft niets met mijn organisatie te doen). De NPM-pakketbeheerder installeert de kwaadaardige afhankelijkheid, vaak met verwoestende gevolgen.
De oplossing
Om deze problemen in ons softwareontwikkelingsproces te voorkomen, moeten we strikte normen volgen voor het declareren van componentversies, die afhankelijk zijn van de gebruikte technologie. Het belangrijkste is dat een specifieke versie van een pakket, zodra het in een repository is gepubliceerd, onveranderlijk moet zijn (om te voorkomen dat afhankelijke pakketten kapot gaan, en niet alleen om veiligheidsredenen).
Het algemene idee is om te fixeren (pijnboom) versies, waarbij u altijd controleert of de vaste versies van de componenten (inclusief ALLE transitieve afhankelijkheden) vrij zijn van malware, en dit is mogelijk dankzij de vergrendelbestanden die veel pakketbeheerders bieden. Laten we eens kijken hoe het vastzetten van versies werkt voor verschillende pakketbeheerders. Er bestaat een delicate afweging tussen frequente versie-updates voor het oplossen van bekende kwetsbaarheden en versie vastzetten om niet-deterministische constructies en potentiële aanvallen op de toeleveringsketen te voorkomen.
-
- NPM:
De npm- of garenpakketbeheerders gebruiken verschillende lockfiles (respectievelijk npm-shrinkwrap.json / package-lock.json of garen.lock) die vaste versies vermelden voor alle afhankelijkheden, direct en indirect. De lockfiles moeten onder versiebeheer staan, anders kunnen andere ontwikkelaars/build-nodes met andere versies eindigen. Vermijd npm-installatie tenzij u tijdens de ontwikkeling de afhankelijkheden moet bijwerken (bijvoorbeeld om beveiligingsoplossingen te installeren). Gebruik in het algemeen de meer deterministische npm ci (Schone installatie), zodat de pakketbeheerder het lockfile zal gebruiken of zal eindigen met een fout als er geen lockfile is, of als het niet overeenkomt met package.json. Als de vermelde versies op malware zijn gecontroleerd, zorgt het lockfile ervoor dat er tijdens de build niets ergs zal gebeuren.Voor interne componenten wordt aanbevolen om een NPM-bereik beheerd door de organisatie (zoals @myorg), en gebruik dat bereik in de afhankelijkheid (zoals @myorg/my-private-dep), die alleen privézichtbaarheid kan hebben. Deze blokkeert afhankelijkheid verwarring aanvallen, omdat alleen leden van de organisatie met schrijftoegang pakketten onder dit bereik kunnen publiceren.
- NPM:
-
- Maven:
Maven / Gradle hebben geen lockfiles (maar zie dit StackOverflow-artikel).Versiebereiken worden bij Maven/Gradle niet zo vaak gebruikt als bij andere ecosystemen. Vermijd gewoon versiereeksen en LAATSTE of RELEASE metaversies. Indirecte versies moeten ook worden gecontroleerd. De Versies Maven-plug-in is een leuk hulpmiddel voor versiebeheer.
Houd er rekening mee dat Maven altijd het concept van organisatiebereik had (het groupId-gedeelte van de afhankelijkheid), en verwarring over afhankelijkheid lijkt helemaal geen probleem te zijn voor dat ecosysteem.
- Maven:
-
- Pip:
In Python zijn er verschillende tools voor het omgaan met lockfiles:- pijpenv, dat een Pipfile.lock-vergrendelingsbestand genereert.
- poëzie, dat poëzie.lock genereert.
- pit bevriezen, opdracht die een vereisten.txt genereert die fungeert als een lockfile. Controleer of alle afhankelijkheden vaste versies gebruiken met de operator ==. Vervolgens gebruikt pip install -r requirements.txt de vaste afhankelijkheden.
- Pip:
Houd er rekening mee dat de bovenstaande lockfiles onder versiebeheer moeten staan, en dat de gekozen build-opdracht het lockfile moet gebruiken.
De gebruikelijke pakketrepository die wordt gebruikt met pip (PyPI) heeft geen naamgevingsbereik en is kwetsbaar voor aanvallen op afhankelijkheidsverwarring. Het vermijden van verwarring over afhankelijkheid in het python-ecosysteem is niet eenvoudig, en sommige auteurs raden aan een interne repository te gebruiken als proxy voor publieke afhankelijkheden die uit PyPI worden opgehaald, maar eerst de private afhankelijkheden uit de interne repository te halen (-index-url moet naar de interne repository verwijzen, niet naar PyPI, en –extra-index-url moet worden verwijderd).
Enkele echte aanvallen
Getcookies-aanval: De acteur Dustin87 heeft een indirecte afhankelijkheid toegevoegd aan het populaire npm mailparser-pakket aan een kwaadaardig pakket met een RCE-achterdeur (gCOMMANDhDATAi):
JSON.stringify(req.headers).replace(/g([a-f0-9]{4})h((?:[a-f0-9]{2})+)i/gi, (o, p, v) => {})
Ondanks dat het verouderd is (geen reviewers!), ontving mailparser nog steeds ongeveer 64,000 wekelijkse downloads. Dit was een geval van een bijna-aanval, aangezien de RCE niet echt werd uitgevoerd.cised..
NPM gepubliceerd dit bericht met details over de getcookies-aanval.
Afhankelijkheid verwarring:
Alex Birsan ontdekte in 2021 het probleem van afhankelijkheidsverwarring en publiceerde een bericht met de titel “Hoe ik Apple, Microsoft en tientallen andere bedrijven hackte'.
Houd er rekening mee dat voor npm het organisatiebereik zoals @myorg moet worden gereserveerd, en dat interne pakketten moeten worden aangepast om het bereik te gebruiken.
Met pip heeft het gemeenschappelijke openbare register PyPI geen scopes/naamruimten. Elk privépakket kan een openbaar pakket-squat hebben met dezelfde naam als het interne pakket, maar dan leeg, en kan mogelijk een fout genereren wanneer het wordt gebruikt, zodat het kan worden geïdentificeerd als het per ongeluk wordt opgehaald.
Knooppunt-ipc:
Toen de oorlog tussen Rusland en Oekraïne begon, injecteerde de pakketeigenaar kwaadaardige code voor het verwijderen van willekeurige bestanden, wanneer deze op Russische en Wit-Russische hosts werd geïnstalleerd. Het bestand ssl-geospec.js maakte een dergelijk geografisch onderscheid:
Interessant genoeg gebruikten andere pakketten open versies voor de node-ipc-afhankelijkheid, zoals het populaire Vue.js-framework, en de beheerders ervan kregen een dringend beroep om de node-ipc-afhankelijkheid vast te zetten op een veilige versie.
In deze bericht bevat meer details over deze sabotage, die een stap verder gaat dan andere Protestmateriaal kwesties.
Slotopmerkingen
Open versies zouden dat moeten doen nooit worden gebruikt in geconsolideerde softwareprojecten. Ze maken builds niet-reproduceerbaar, en aanvallers kunnen deze misbruiken en erin slagen malware te injecteren via aanvallen op de afhankelijkheidsbomen, zoals de bovengenoemde afhankelijkheidsverwarring.
Verkeerde configuraties zoals open versies, gebrek aan versievastzetten of interne componenten zonder bereik moet worden vermeden. Het eerste is om dergelijke problemen op te sporen, misschien zelfs de build te blokkeren wanneer ze worden gevonden, en een protocol voor actie te standaardiseren.
Automatische detectie van fouten en verkeerde configuraties in afhankelijkheden, waarbij verdachte afhankelijkheden worden gerapporteerd die kwetsbaar kunnen zijn voor specifieke aanvallen op de toeleveringsketen, zoals afhankelijkheid verwarring, allemaal met bruikbare fixtools, is een van de hoofddoelen van het Xygeni-platform.
| Om meer te lezen |
| Ohm M., Plate H., Sykosch A., Meier M.: "Backstabber's Knife Collection: een overzicht van aanvallen op de supply chain van open source-software". DIMVA 2020. Lecture Notes in Computer Science, vol 12223. Springer – 2020 (bron van de afhankelijkheidsaanvalboomfiguur.) |
| @adam-npm: “Gemelde kwaadaardige module: getcookies“. npm Blog (gearchiveerd) – 2 mei 2018. |
| Alex Birsan: “Hoe ik Apple, Microsoft en tientallen andere bedrijven hackte”. Middel – 9 februari 2021. |
| Bijl Sharma: “GROTE sabotage: Beroemd npm-pakket verwijdert bestanden om te protesteren tegen de oorlog in Oekraïne” BleepingComputer – 17 maart 2022 |





