Säker hemlighetshantering i GitHub Actions

Senaste uppdateringen: 12/01/2025
Författare: C SourceTrail
  • GitHub Actions-hemligheter är krypterade, omfattande miljövariabler som måste noggrant omfattas på databas-, miljö- och organisationsnivå.
  • Säkerhet bygger på minsta möjliga behörighet, undviker loggar exponering, roterar och granskar hemligheter och isolerar känsliga produktionsmiljöer.
  • Risker från skriptinjektion, tredjepartsåtgärder och självhostade runners kräver pinning, kodgranskning och strikta policyer för runners och behörigheter.
  • OpenID Connect och externa hemlighetshanterare hjälper till att ersätta långlivade autentiseringsuppgifter med kortlivade tokens och centraliserade, granskningsbara hemliga arbetsflöden.

Hantering av GitHub Actions-hemligheter

Att hantera hemligheter i GitHub Actions är ett av de ämnen som verkar enkelt vid första anblicken, men som snabbt förvandlas till ett kritiskt säkerhetsproblem när dina pipelines börjar komma i kontakt med produktion, molnleverantörer och tredjepartstjänster. Era CI/CD-arbetsflöden hanterar rutinmässigt API-nycklar, databaslösenord, SSH-nycklar, tokens med mera, och vart och ett av dessa värden är en potentiell ingångspunkt för en angripare om de hanteras vårdslöst.

I den här guiden går vi djupare in på hur hemligheter fungerar i GitHub Actions, hur man konfigurerar dem på repository-, miljö- och organisationsnivå, hur man stärker arbetsflöden mot läckor och attacker i leveranskedjan, och när det är klokt att ta in externa hemlighetshanterare. Tanken är att ge dig en praktisk, säkerhetsfokuserad översikt så att du kan hålla dina pipelines snabba och säkert utan att det dagliga arbetet blir en huvudvärk.

Vad är egentligen GitHub Actions hemligheter?

I GitHub Actions är en "hemlighet" en krypterad miljövariabel vars värde är dolt från användargränssnittet, loggarna och arkivets innehåll. Du definierar det en gång (på repo-, organisations- eller miljönivå) och refererar sedan till det i ditt arbetsflöde YAML med hjälp av secrets. kontext, så att dina pipelines kan använda känsliga värden utan att någonsin committa dem till kodbasen.

Under huven krypterar GitHub hemligheter med stark kryptografi (Libsodium-förseglade lådor) innan de ens når GitHubs servrar, och värdena dekrypteras endast vid körning på arbetsflödesrutinen. När hemligheter väl har skapats är de oföränderliga från användargränssnittet: du kan skriva över dem men du kan inte läsa tillbaka dem, och när de visas i loggar maskeras de automatiskt med *** där det är möjligt.

Den här modellen har några viktiga designbegränsningar som du behöver vara medveten om: hemligheter kan inte hämtas via användargränssnittet eller API:et, de är borttagna från loggar och de finns inom ett specifikt omfång: arkiv, miljö inom ett arkiv eller organisation. Att välja rätt omfång är det första stora beslutet för en vettig hemlig strategi.

Hemliga omfång i GitHub-åtgärder

Hemligheter för arkiv, miljö och organisation

GitHub erbjuder tre huvudlager för hemligheter: arkivhemligheter, miljöhemligheter och organisationshemligheter, vart och ett med sina egna användningsfall och prioritetsregler. Att förstå hur de interagerar hjälper dig att undvika konflikter och behålla känsliga värden där de hör hemma.

Hemligheter på databasnivå

Databashemligheter är knutna till ett enda databas och är tillgängliga för alla arbetsflöden i den databasen. De är perfekta för projektspecifika värden som en tjänsts API-nyckel, ett distributionslösenord eller en webhook-token som endast används av det repot.

För att skapa en databashemlighet från användargränssnittet går du till målrepot, öppnar "Inställningar" → "Hemligheter och variabler" → "Åtgärder" och klickar sedan på "Ny databashemlighet". Du ger den ett namn med versaler och understreck (till exempel PAYMENTS_API_KEY), klistra in det hemliga värdet och spara; från och med det ögonblicket kan arbetsflöden komma åt det som ${{ secrets.PAYMENTS_API_KEY }}.

Alla med skrivåtkomst till repot kan referera till dessa hemligheter i arbetsflöden, så behörigheter till själva repot blir en del av din säkerhetsberättelse. Om du sällan beviljar skrivåtkomst till många användare, ger du dem implicit åtkomst att använda alla databashemligheter i automatiseringen.

Miljöspecifika hemligheter

Miljöhemligheter finns en nivå under databashemligheter och låter dig definiera olika värden per miljö, till exempel dev, staging, eller production. De är kopplade till en namngiven miljö och kan skyddas med regler som obligatoriska granskare eller väntetider innan ett jobb kan köras mot dem.

Du konfigurerar dessa genom att gå till ”Inställningar” → ”Miljöer”, skapa eller välja en miljö och sedan lägga till hemligheter i den miljökonfigurationen. De hemliga namnen använder fortfarande secrets. sammanhang (till exempel secrets.PROD_DB_PASSWORD), men värdena blir bara tillgängliga för jobb som explicit körs i den miljön.

En viktig detalj är att miljöhemligheter åsidosätter databashemligheter när de delar samma namn. Det betyder att du kan definiera DB_PASSWORD på repositorynivå för lokal/testanvändning och sedan ha en annan DB_PASSWORD som en miljöhemlighet för produktion som prioriteras i produktionsjobb, utan att ändra arbetsflödets syntax.

Miljöer möjliggör också skyddsregler som "obligatoriska granskare" eller "endast från vissa grenar", vilket är otroligt användbart för att sätta skyddsräcken runt åtkomst till dina känsligaste hemligheter. Till exempel kan en produktionsmiljö kräva godkännande från DevOps innan något jobb som använder dess hemligheter kan köras.

Organisationsövergripande hemligheter

Organisationshemligheter delas mellan flera databaser i en organisation och är idealiska för autentiseringsuppgifter som återanvänds i stor utsträckning, som en delad Slack-webhook eller en central API-token för mätvärden. De minskar dubbelarbete och gör rotation enklare eftersom du uppdaterar hemligheten en gång för alla och förbrukar repositorier som plockar upp det nya värdet.

Administratörer skapar dem i organisationens avsnitt ”Inställningar” → ”Hemligheter och variabler” → ”Åtgärder”, klickar på ”Ny organisationshemlighet” och väljer sedan vilka databaser som har åtkomst till den hemligheten. Du kan tillåta alla nuvarande och framtida repositories eller strikt begränsa det till en specifik delmängd.

Det finns en prioritetskedja som du bör ha i åtanke: organization secret < repository secret < environment secret när namn kolliderar. Med andra ord vinner en miljöhemlighet över en databashemlighet, som vinner över en organisationshemlighet om de alla delar samma nyckel.

Hur hemligheter beter sig vid körning

När hemligheter har definierats blir de tillgängliga för jobb vid körning via secrets kontext och injiceras som miljövariabler när de refereras. De exporteras inte i stort sett till varje steg som standard; du kopplar dem explicit i din env: block eller skicka dem till åtgärder som stöder hemligheter som indata.

GitHub erbjuder också en speciell GITHUB_TOKEN per arbetsflödeskörning, vilket inte är en manuellt definierad hemlighet men beter sig som en och används ofta för API-anrop eller databasåtgärder. Du kan (och bör) finjustera de finjusterade behörigheterna för denna token med hjälp av permissions: block så att det har det minsta omfång som behövs för varje jobb.

För att minska oavsiktlig exponering maskerar GitHub alla värden som matchar en registrerad hemlighet i arbetsflödesloggar och ersätter det med ***. Denna maskering görs på löparsidan och fungerar generellt bra för råa strängar, men den förutsätter att det exakta hemliga värdet visas i utdata. Om du transformerar hemligheten (till exempel base64-kodar den eller bäddar in den i strukturerad JSON) kan det hända att masken inte fångar upp den.

Eftersom maskering är en metod som är bäst lämpad och inte matematiskt garanterad, bör du utforma arbetsflöden för att undvika att skriva ut hemligheter eller deras derivat till loggar överhuvudtaget, och använda maskeringskommandon för ytterligare värden som du genererar vid körning. Behandla loggar som potentiellt synliga för fler än du förväntar dig och anta att allt som skrivs ut kan läcka.

Praktisk användning: referera till hemligheter i arbetsflöden

För det mesta använder du hemligheter genom att mappa dem till miljövariabler i ett specifikt steg eller jobb och sedan låta dina skript eller verktyg läsa från miljön. Ett klassiskt mönster ser ut så här:


– namn: Distribuera till API
env:
API_KEY: ${{ secrets.PROD_API_KEY }}
kör: |
curl -H “Auktorisering: Bärare $API_KEY” https://api.example.com/deploy

Du kan också skriva en hemlighet till en fil på löparen, vilket är säkert så länge filen finns inom jobbets tillfälliga arbetsyta och inte committas eller laddas upp som en artefakt. Till exempel, lagring av en SSH-nyckel:


– namn: Skriv SSH-nyckel till fil
skal: bash
env:
SSH_KEY: ${{ secrets.SSH_KEY }}
kör: |
echo “$SSH_KEY” > nyckel
chmod 600-nyckel

Från loggarna ser du bara det faktiska shell-kommandot (med $SSH_KEY), men inte själva det hemliga värdet, som kommer att redigeras bort eller döljas. Eftersom GitHub-hostade runners förstörs efter att jobbet är klart, försvinner den temporära filen med den virtuella maskinen; på självhostade runners måste du vara mycket strängare när det gäller att rensa upp.

Bästa säkerhetstips för hemligheter i GitHub Actions

Det räcker inte att bara använda det hemliga användargränssnittet; du behöver en uppsättning vanor och säkerhetsåtgärder för att minimera explosionsradien om något går fel. GitHub tillhandahåller många rattar, men det är upp till dig att vrida på dem korrekt.

Tillämpa principen om minsta privilegium

Varje hemlighet och varje token bör endast ge de behörigheter som är absolut nödvändiga för en given uppgift. För externa tjänster, skapa dedikerade autentiseringsuppgifter med begränsade behörigheter (till exempel "endast driftsättning" eller "skrivskyddad statistik") istället för att återanvända fullständiga administratörsnycklar.

Samma princip gäller för den inbyggda GITHUB_TOKENställ in standardbehörigheter till ett absolut minimum (ofta contents: read) och sedan höja behörigheterna endast i specifika jobb som behöver mer. Du konfigurerar detta med en permissions: avsnittet på arbetsflödes- eller jobbnivå så att ett komprometterat jobb inte i tysthet kan göra godtyckliga skrivningar.

Undvik att skriva ut eller koda hemligheter i loggar

Hemligheter bör aldrig hårdkodas i arbetsflödesfiler eller skrivas ut i klartext för att underlätta felsökning. Om du har andra känsliga värden som inte är registrerade som GitHub-hemligheter (till exempel en token som genereras vid körning) kan du fortfarande be löparen att behandla dem som hemligheter med hjälp av kommandosyntaxen:


echo “::add-mask::$GENERATED_TOKEN”

Strukturerade blobbar som JSON, XML eller stora YAML-dokument är särskilt farliga som hemligheter eftersom GitHubs maskerare förlitar sig på exakt strängmatchning. Om du lägger flera känsliga värden i en stor JSON-sträng och använder den som en enda hemlighet, kan små formateringsändringar göra att maskeringen misslyckas; definiera istället individuella hemligheter för varje känsligt fält.

Granska alltid loggar när du testar arbetsflöden, och var särskilt uppmärksam på felmeddelanden och stackspårningar. Vissa verktyg upprepar gärna kommandon och flaggor till stderr, vilket av misstag kan inkludera hemliga värden om du inte uttryckligen undviker det mönstret.

Rotera och granska hemligheter regelbundet

Hemlig rotation är tråkigt men inte förhandlingsbart om man bryr sig om säkerheten; att lämna inloggningsuppgifter oförändrade i flera år ökar möjligheterna för angripare. En rimlig baslinje är att rotera dina mest kritiska produktionshemligheter varje månad, högriskhemligheter varannan månad och allt annat minst kvartalsvis.

Du kan automatisera en del av detta med hjälp av GitHub REST API for secrets, vilket låter dig hämta den offentliga nyckeln för ett datalager eller en organisation och ladda upp nya krypterade värden. Detta är särskilt praktiskt för stora organisationer med många databaser som delar servicekonton och behöver rotera dem snabbt som svar på incidenter.

Granskning är lika viktigt: granska regelbundet listan över konfigurerade hemligheter och ta bort de som inte längre används, och använd GitHubs säkerhets-/granskningsloggar för att spåra händelser som org.update_actions_secret. På så sätt vet du vem som ändrade vad och när, och du kan koppla misstänkta ändringar till annan aktivitet.

Använd miljöbaserad separation

Miljöhemligheter är ett av de enklaste sätten att härda dina pipelines, eftersom de låter dig isolera mycket känsliga värden (som produktions-databasautentiseringsuppgifter) bakom explicita godkännanden. Du kan kräva mänskliga granskare, begränsa vilka grenar som kan driftsättas och till och med lägga till nedkylningstimers innan en driftsättning startar.

Ett vanligt mönster är att ha en staging miljö med milda skydd och en production miljö med strängare regler och separata hemligheter. Arbetsflöden definierar sedan jobb som riktar sig till varje miljö, vilket säkerställer att produkthemligheter aldrig används av misstag i testjobb.

Välj tydliga namngivningskonventioner

Bra namngivning av hemligheter sparar dig frustrerande gissningar och farliga förväxlingar. Istället för generiska namn som API_KEY, koda tjänsten och miljön i namnet, till exempel STRIPE_PROD_API_KEY or AWS_STAGING_DEPLOY_ROLE_ARN.

Team som hanterar många tjänster använder ofta ett mönster som <SERVICE>_<ENV>_<PURPOSE>. Så du kanske har SLACK_PROD_ALERTS_WEBHOOK, GCP_DEV_BUILD_SERVICE_ACCOUNToch DB_STAGING_PASSWORDDetta gör det mycket tydligare vilken hemlighet som ska användas i vilket jobb.

Skydda mot skriptinjektion och risker från tredje part

Hemligheter riskerar inte bara att orsaka felkonfiguration; de är också frestande mål för skriptinjektion i arbetsflöden och skadliga eller komprometterade åtgärder från tredje part. Ett enda sårbart steg kan avslöja alla hemligheter som är tillgängliga för jobbet om du inte är försiktig.

Mildrande skriptinjicering i inline-steg

När du interpolerar otillförlitlig data (som titlar på pull requests, grennamn eller ärendekommentarer) direkt i shell-skript öppnar du dörren för injektion. Till exempel kan en PR-titel utformas för att bryta ut ur ett kommando och köra godtycklig skalkod i din runner.

Det säkraste tillvägagångssättet är att hantera komplex logik i förstaparts- eller väl granskade JavaScript/TypeScript-åtgärder och skicka otillförlitliga värden som indata istället för att infoga dem i skalet. I den modellen tar åtgärden emot strängar som argument och bearbetar dem utan att generera skalskript som kan kapas.

Om du måste använda inline-shell, lagra först otillförlitliga värden i miljövariabler och referera sedan till dessa variabler, helst inom dubbla citationstecken. På så sätt behandlas värdet som data snarare än som en del av skriptstrukturen, vilket gör det mycket mindre troligt att injektionsförsök lyckas.

Fäst och granska tredjepartsåtgärder

Varje tredjepartsåtgärd som du drar in i ditt arbetsflöde körs med åtkomst till jobbets miljö och hemligheter, så du bör behandla dem som kodberoenden som kräver granskning. En skadlig eller komprometterad åtgärd kan läsa hemligheter och skicka dem till en angripare med ett enda HTTP-anrop.

Bästa praxis är att fästa åtgärder med fullständig commit SHA istället för bara taggar eller grenar, eftersom taggar kan flyttas eller skrivas över. En SHA hänvisar till en exakt version av koden, vilket gör det mycket svårare för en angripare att tyst injicera nytt beteende utan att du uppdaterar arbetsflödet.

Innan du använder en åtgärd, skumma igenom dess källkod (eller åtminstone en säkerhetsgranskning) för att säkerställa att den hanterar hemligheter ansvarsfullt och inte loggar dem eller skickar dem till okända slutpunkter. Om du använder marknadsplatsåtgärder, verifiera utgivaren där det är möjligt och förlita dig på Dependabot för att varna dig om sårbarheter och uppdateringar.

Värdbaserade kontra egna värdbaserade löpare och hemlig exponering

Var dina arbetsflöden körs har stor inverkan på hur säkert hemligheter hanteras. GitHub-hostade löpare och egenhostade löpare beter sig väldigt olika när det gäller isolering och ihållighet.

GitHub-hostade runners skapar nya virtuella maskiner för varje jobb, kör ditt arbetsflöde och river sedan ner dem. Det ger dig en ren miljö varje gång och säkerställer att alla filer eller miljövariabler (inklusive hemligheter som skrivs till disk) förstörs när jobbet är klart.

Självhostade runners är däremot långlivade maskiner som du hanterar, vilket innebär att all kod med åtkomst till hemligheter potentiellt kan bestå eller exfiltrera dem bortom livslängden för ett enda jobb. På publika databaser är självhostade runners särskilt riskabla eftersom otillförlitliga bidragsgivare kan öppna pull requests som utlöser arbetsflöden.

Om du använder självhostade runners, isolera dem per känslighetsnivå, begränsa vilka repos som kan använda vilka runners och var paranoid över vad mer som finns på dessa maskiner (SSH-nycklar, molnuppgifter, nätverksåtkomst till interna tjänster och så vidare). Vissa organisationer använder självhostade "just-in-time" (JIT) löpare som skapas via API för ett enda jobb och sedan förstörs, men även då måste du se till att jobb inte delar samma löpare oväntat.

Använda OpenID Connect (OIDC) istället för långlivade molnhemligheter

En av de största vinsterna för hemlig hygien i GitHub Actions är att ersätta långlivade molnnycklar med kortlivade inloggningsuppgifter via OpenID Connect. Istället för att lagra AWS-, Azure- eller GCP-nycklar som hemligheter begär dina arbetsflöden tillfälliga tokens från molnleverantören med hjälp av GitHub som identitetsleverantör.

Flödet ser ungefär ut så här: jobbet begär en signerad JWT från GitHubs OIDC-slutpunkt, din molnleverantör validerar den token och byter ut den mot kortlivade autentiseringsuppgifter, och arbetsflödet använder dessa autentiseringsuppgifter under hela jobbet. Ingen statisk hemlighet behöver någonsin finnas i GitHub.

Med AWS konfigurerar du till exempel en IAM-roll som litar på GitHub OIDC-leverantören och begränsar vilka repositorier/grenar som kan anta den rollen. Sedan använder du en åtgärd som i ditt arbetsflöde aws-actions/configure-aws-credentials med OIDC-behörigheter aktiverade för att hämta autentiseringsuppgifter direkt.

Den här metoden har flera fördelar: det finns inget att rotera inuti GitHub, tokens är automatiskt kortlivade, åtkomsten är snävt begränsad och du får fullständiga granskningsloggar på molnsidan som spårar varje rollantagande. För miljöer med hög säkerhet bör OIDC vara standard där det stöds.

Inbyggda verktyg och externa hemlighetshanterare

GitHubs inbyggda hemligheter är utmärkta för många scenarier, men vid någon tidpunkt kanske du vill ha en mer central, funktionsrik hemlighetshanterare som spänner över flera plattformar och miljöer. Verktyg som HashiCorp Vault, Infisical eller Doppler används ofta tillsammans med GitHub Actions för detta ändamål.

Dessa system kan hantera dynamiska hemligheter (till exempel generering av kortlivade databasanvändare), avancerade åtkomstkontrollpolicyer, detaljerade granskningsloggar och rotationsarbetsflöden som går utöver vad GitHub ensamt erbjuder. GitHub-åtgärder autentiserar sedan mot dessa hanterare (ofta via OIDC eller någon annan autentiseringsmetod), hämtar de hemligheter de behöver vid körning och använder dem utan att någonsin lagra långsiktiga autentiseringsuppgifter i repot.

Det finns också community-åtgärder och plugins som är utformade för att hämta hemligheter från externa chefer direkt till arbetsflöden. När du använder dem gäller samma råd: granska åtgärdens källa, fäst den till en commit SHA och begränsa de behörigheter som beviljas den token eller roll den använder för att nå det externa systemet.

Säker hemlighetshantering i GitHub Actions innebär att välja rätt omfång för varje hemlighet, tillämpa lägsta möjliga behörighet, använda miljöer och OIDC där det är lämpligt, behandla loggar och tredjepartsåtgärder som potentiella attackytor och förlita sig på externa hemlighetshanterare när era skal- eller efterlevnadskrav kräver det. Med dessa rutiner på plats kan dina CI/CD-pipelines förbli flexibla och snabba samtidigt som risken för att en felplacerad token eller ett dåligt granskat arbetsflöde förvandlas till en fullskalig incident dramatiskt minskas.

Relaterade inlägg: