Efter SolarWinds-incidenten har kompromettering av källkod visat sig vara ett genomförbart och mycket effektivt sätt att genomföra cyberattacker. Det fungerar när angriparen utnyttjar förtroendet för det komprometterade företaget för att nå flera kunders kärnnätverk.
Sofus Albertsen
Sofus is a Continuous Delivery Trainer and Consultant in Copenhagen. Before joining Eficode Praqma he was assistant professor on an Applied Science Bachelor program. He flies kites and in the summer he escapes the modern world by spending two weeks in a beach hut without electricity or network coverage.
Dessutom avslöjades det i går att även PHP:s källkodsrepo har komprometterats. Om detta hade gått obemärkt förbi skulle alla servrar som kör PHP ha kunnat bli måltavlor när de uppdaterade PHP-versionen. För att vara tydlig: de komprometterade commitarna upptäcktes innan de nådde produktionsversionen av PHP – så du behöver inte oroa dig om du kör PHP-servrar. Händelsen visar dock tydligt hur viktig säkerheten i din leveranskedja för mjukvara är.
Båda dessa händelser visar att läckor av källkod kanske inte längre är vårt främsta säkerhetsproblem.
Säkerhet är mycket komplext. Syftet med den här bloggen är inte att fixa allt™, utan att fokusera på riskerna i vår leveranskedja för mjukvara och ge exempel på hur de kan minskas.
Säkra din leveranskedja med CIA-triaden
CIA-triaden kan användas vid riskbedömningar. De tre delar som CIA-triaden består av är:
Konfidentialitet – skydd och sekretess för information. Detta gäller mjukvarualgoritmer, affärsprocesser och slutanvändardata. Förlorade hemligheter kan påverka ett företags konkurrensfördelar, leda till minskat förtroende hos slutanvändare eller till och med rättsliga åtgärder (t.ex. GDPR).
Integritet – skydd mot manipulation av vår information. Detta gäller både mjukvaruartefakter och data, inklusive slutanvändardata. Bristande integritet kan göra vår mjukvaruplattform sårbar för hackare och därmed också leda till bristande konfidentialitet i större skala.
Tillgänglighet – tillgängligheten för vår mjukvarutjänst. Detta gäller hur vår infrastruktur och våra applikationer är utformade och hur väl de kan stå emot skadliga angrepp. Det påverkar även våra processer – till exempel om vi använder välfungerande DevOps-processer som säkerställer kort återställningstid vid driftstopp, oavsett om driftstoppet orsakas av oss själva (t.ex. mjukvaruuppdateringar) eller externa händelser (t.ex. katastrofåterställning).
Skydda leveranskedjan för mjukvara
För att göra den här guiden mer konkret illustrerar vi flödet från en utvecklares commit till en artefakt i produktion.
I den här situationen förbereder utvecklaren en commit och pushar den till Git-repot. Därefter startar CI/CD-pipelinen och driftsätter en ny version av artefakten i produktion.
I exemplet ovan skyddas dataöverföringen av SSH-tunnlar – men en angripare kan fortfarande lägga in commitar i Git-repot utan att upptäckas, om personen har åtkomst till repot.
Lägg till ett säkerhetslager i leveranskedjan för mjukvara
Hur vi arbetar med Git påverkar konfidentialiteten och integriteten i vår leveranskedja för mjukvara. Utvecklare använder vanligtvis följande för att säkra Git-operationer:
En SSH-nyckel används för att 1) kryptera anslutningen till Git-servern via en SSH-tunnel och 2) autentisera oss mot Git-servern och auktorisera våra åtgärder.
HTTPS mot ett GitWeb-gränssnitt. TLS-protokollet som används i HTTPS är visserligen säkert, men det är inte ovanligt att företagsdatorer har egna rotcertifikatutfärdare. Det innebär att HTTPS-krypteringen som skyddar Git-gränssnittet kanske inte är end-to-end, utan har en företagsintern man-in-the-middle.
Vi kan dra slutsatsen att konfidentialiteten är skyddad när vi använder SSH-protokollet för att kommunicera med vårt Git-repo. SSH-tunnlar ger endast lokal konfidentialitet, eftersom tunnelns räckvidd är punkt-till-punkt i kedjan, inte end-to-end. Integriteten för vår källkod är däremot inte skyddad, och ett nytt steg behöver läggas till: kryptografisk signering av dina commitar.
GPG-nycklar används för att signera Git-commitar och taggar. Ett signerat Git-objekt intygar att commiten eller taggen skapades av någon med åtkomst till den privata signeringsnyckeln – och säkerställer därmed integriteten för varje commit i repot.
För att komma igång med GPG i GitHub skapar du en GPG-nyckel och kopplar den till ditt konto. Läs dokumentationen på github.com för att se hur du gör. GitLab stöder också GPG-nycklar, liksom Bitbucket Server/DC. Bitbucket Cloud gör det tyvärr inte.
Din Git-klient har redan stöd för att arbeta med GPG. Läs mer om hur du gör på Git-klientens hemsida.
När du har skapat din signerade GPG-nyckel kan du låta Git automatiskt signera alla efterföljande commitar och taggar genom att koppla din nyckel till Git-konfigurationen.
$git config --global user.signingkey 0A46826A
Git använder nu din nyckel som standard för att signera taggar och commitar när du vill.
Obs: HTTPS kan inte användas för att skydda integriteten. Gränssnittet låter oss utföra de flesta Git-operationer, förutom att signera commitar eftersom det inte har åtkomst till vår privata signeringsnyckel.
Den nya säkrade leveranskedjan för mjukvara
I det här avsnittet introducerar vi arbetsflödet för en utvecklare som använder en CI-pipeline för att bygga container images för användning i en produktionsmiljö. Här utgår vi från att utvecklaren signerar commits.
Det här arbetsflödet visar också hur SSH-nycklar används för åtkomst till Git, det vill säga att vi ser SSH-tunnlar mellan utvecklaren och Git samt mellan Git och pipelinen. Git-signaturer ger stark integritet från början till slut, vilket är viktigt eftersom bristande integritet också kan leda till bristande konfidentialitet. Git SSH-nycklar för åtkomst till Git ger endast lokal konfidentialitet.
CI-pipelinen bygger container images från Git-källkod och bör validera Git-signaturer för att skapa tillit mellan utvecklaren och pipelinen. CI-pipelinen måste också signera den byggda container imagen för att säkerställa integritet mellan pipelinen och produktionsmiljön via container image-registret (se Docker Content Trust). Vårt CI/CD-system blir därför en tillitsbrygga mellan källkod och binär artefakt.
I det här arbetsflödet kan CI-pipelinen vara en svag punkt, eftersom den utför komplexa operationer under kontroll av källapplikationen med potentiellt många olika verktyg. Det innebär att CI-pipelinen har en stor angreppsyta. Svagheten förstärks av att CI-pipelinen förmedlar tillit från utvecklarsignerade commits till signerade images som valideras för produktion. Om denna tillitsbrygga komprometteras – till exempel genom att lura CI-pipelinen att bygga från källkod som inte är korrekt signerad – går integriteten från början till slut förlorad.
För att minska risken bör vi använda en extern agent för att utföra en sekundär verifiering av Git-signaturer. Kontrollen bör göras av en agent som inte är direkt kopplad till eller påverkas av CI-systemet – det vill säga att vi utgår från att det är mycket osannolikt att den komprometteras samtidigt som CI-systemet.
Arkitekturen för den externa signaturkontrollen visas nedan:
Så verifierar du dina commits
Michael har skapat en Git-signaturkontroll som kan användas både i CI-pipelines och i den externa Git-signaturkontrollen. Den finns här och är öppen källkod.
Kontrollen behöver en katalog med publika nycklar för alla signeringsnycklar som ska vara betrodda. Du kan exportera din publika Git GPG-nyckel enligt följande, men observera att du behöver alla publika nycklar som betraktas som giltiga för signering:
$ gpg --armor --export user@example.com > user-pub-key.asc
Kör sedan signaturkontrollen med ett Git-repository och din lista över betrodda publika nycklar enligt följande:
$ docker run --rm -v $PWD/public-keys:/public-keys:ro -v $PWD/.git:/repo:ro michaelvl/git-signature-checker
Du kan också använda en befintlig GPG-nyckelring, eventuellt med tillit tilldelad till nycklar, enligt följande:
$ docker run --rm -v $HOME/.gnupg:/gnupghome:ro -v $PWD/.git:/repo:ro michaelvl/git-signature-checker --git-dir /repo --keyring /gnupghome --minimum-trust ULTIMATE
Som standard validerar kontrollen alla commits i den aktuella revisionen av det angivna repositoriet. Använd argumentet --revision-range för att kontrollera alternativa referenser och underträd.
Obs: När detta skrivs visar GitHub commits i repositoriet som overifierade. Det beror på att nyckeln som användes för att signera commitsen har gått ut. Nycklar utan utgångsdatum är riskabla ur ett säkerhetsperspektiv, men nycklar med alltför kort giltighetstid kan leda till misstro mot giltigt signerade commits.
En liknande situation uppstår när medarbetare slutar – du bör återkalla deras signeringsnyckel, men signaturer som de har skapat tidigare bör fortfarande vara betrodda. Bedöm generellt en signaturs giltighet utifrån nyckelns status när signaturen skapades.
Om publika nycklar
Det här verktyget validerar inte de publika nycklar som importeras via sökvägen som anges av --public-keys. Tillit mellan nycklar och den tillitsnivå som krävs kan definieras genom att använda --keyring för att använda en specifik GPG-nyckelring med nycklar som har definierad tillit. --minimum-trust kan användas för att ange den lägsta tillitsnivå för nyckeln som krävs för en lyckad signaturvalidering.
Noggrann nyckelhantering är naturligtvis en förutsättning för att validera Gits integritet med signaturer.
Begränsningar vid GitHub-mergningar via webbgränssnittet
Om du använder GitHubs webbgränssnitt för att merga PR:er kommer dina mergningar att signeras med en publik GitHub-nyckel, inte din egen nyckel. Detta bryter tilliten från början till slut, eftersom vi inte kan veta vem som använde gränssnittet – det kan vara någon som använder ett läckt användarnamn och lösenord. Därför bör du alltid använda 2FA i webbgränssnittet.
Du kan överväga att merga PR:er själv. I så fall måste du inkludera GitHubs publika nyckel i listan över betrodda signeringsnycklar. Nyckeln finns här:
curl https://github.com/web-flow.gpg
Det här problemet gäller även de andra Git-leverantörerna som nämns i avsnittet ”Lägga till ett säkerhetslager i vår kedja för mjukvaruleverans”.
Obs! Om du importerar serverns nycklar som betrodda kan du inte längre betrakta din Git repository-server som omöjlig att kompromettera. Spårbarheten vid sammanslagningar går förlorad, och commits som läggs till via serverns UI betraktas som betrodda.
Att endast tillåta fast-forward-sammanslagningar kan ge dig det bästa av två världar, eftersom Git-servern varken ändrar eller lägger till några commits. Det kräver att du alltid håller dina PR:er uppdaterade med master.
Beprövad praxis
Vi rekommenderar att du använder en hårdvarutoken för att lagra dina GPG-certifikat. På så sätt kan du minska risken för att din nyckel stjäls, även om din dator komprometteras. YubiKey är en sådan nyckel.
Nycklar som lagras på YubiKey kan inte exporteras, till skillnad från filbaserade nycklar som lagras på disk, och är praktiska i det dagliga arbetet. I stället för att du behöver komma ihåg och ange lösenfraser för att låsa upp SSH/GPG-nycklar behöver YubiKey bara beröras fysiskt efter att den har låsts upp med en PIN-kod. All signering och kryptering sker på kortet, i stället för i operativsystemets minne.
För en mer djupgående guide tycker vi att beskrivningarna i det här GitHub-repot är välskrivna. Läs dem noggrant, eftersom vi inte har genomfört någon säkerhetsgranskning av informationen.
Avslutande kommentarer
Om du använder GitOps och Kubernetes finns det flera sätt att integrera kontroll av integritet i infrastrukturen.
Om SRE-teamet till exempel signerar alla commits kan GitOps-agenten verifiera Git commit-signaturer innan ändringar tillämpas i Kubernetes. Det ger heltäckande integritet vid leverans av infrastrukturändringar.
För att validera images kan vi använda reproducerbara builds. I praktiken är detta dock ganska komplicerat och medför egna problem. Exempelvis får pipelinen som utför den sekundära builden inte vara sårbar för samma attacker som den primära pipelinen.
Säker spårbarhet i din kedja för mjukvaruleverans
När intrång sker fokuserar vi ofta på stöld av immateriella rättigheter i form av källkod. Men vi behöver i stället fokusera på att säkra spårbarheten i vår kedja för mjukvaruleverans för att undvika attacker som den mot SolarWinds.
Att signera och verifiera varje commit minskar risken för att en illasinnad angripare lägger in exploits i din mjukvara utan din vetskap och därmed komprometterar mjukvarans integritet.
För att göra detta rätt krävs nya verktyg och en övertygelse om att alla har kunskap om hur de används. Alternativet har visat sig vara så förödande att vi måste fråga oss om vi är villiga att ta den risken.
- CI/CD
- Security
Subscribe to our newsletter
Related blogs