Sikker hemmelig administrasjon i GitHub Actions

Siste oppdatering: 12/01/2025
Forfatter: C SourceTrail
  • GitHub Actions-hemmeligheter er krypterte, omfangsbestemte miljøvariabler som må omfangsbestemmes nøye på repositorium-, miljø- og organisasjonsnivå.
  • Sikkerhet avhenger av minste mulige rettigheter, unngår loggeksponering, rotasjon og revisjon av hemmeligheter og isolering av sensitive produksjonsmiljøer.
  • Risikoer fra skriptinjeksjon, tredjepartshandlinger og selvhostede løpere krever festing, kodegjennomgang og strenge retningslinjer for løpere og tillatelser.
  • OpenID Connect og eksterne hemmelige administratorer bidrar til å erstatte langvarig legitimasjon med kortlivede tokens og sentraliserte, reviderbare hemmelige arbeidsflyter.

Administrasjon av hemmeligheter for GitHub-handlinger

Administrering av hemmeligheter i GitHub Actions er et av de temaene som virker enkle ved første øyekast, men som raskt blir et kritisk sikkerhetsproblem når pipelines begynner å berøre produksjon, skyleverandører og tredjepartstjenester. CI/CD-arbeidsflytene dine håndterer rutinemessig API-nøkler, databasepassord, SSH-nøkler, tokens og mer, og hver av disse verdiene er et potensielt inngangspunkt for en angriper hvis de håndteres uforsiktig.

I denne veiledningen skal vi dykke dypt ned i hvordan hemmeligheter fungerer i GitHub Actions, hvordan man konfigurerer dem på repository-, miljø- og organisasjonsnivå, hvordan man herder arbeidsflyter mot lekkasjer og angrep i forsyningskjeden, og når det er fornuftig å hente inn eksterne hemmelighetsadministratorer. Tanken er å gi deg en praktisk, sikkerhetsfokusert oversikt, slik at du kan holde pipelinene dine raske og trygt uten å gjøre det daglige arbeidet til en hodepine.

Hva er egentlig hemmelighetene til GitHub Actions?

I GitHub Actions er en «hemmelighet» en kryptert miljøvariabel hvis verdi er skjult fra brukergrensesnittet, loggene og innholdet i depotet. Du definerer det én gang (på repo-, organisasjons- eller miljønivå), og refererer deretter til det i arbeidsflyten din YAML ved hjelp av secrets. kontekst, slik at pipelines dine kan bruke sensitive verdier uten å noen gang legge dem inn i kodebasen.

Under panseret krypterer GitHub hemmeligheter ved hjelp av sterk kryptografi (Libsodium-forseglede bokser) før de i det hele tatt treffer GitHubs servere, og verdiene dekrypteres bare under kjøring på arbeidsflytløperen. Når hemmeligheter er opprettet, kan de ikke endres fra brukergrensesnittet: du kan overskrive dem, men du kan ikke lese dem tilbake, og når de vises i logger, maskeres de automatisk med *** der det er mulig.

Denne modellen kommer med noen viktige designbegrensninger du må være klar over: hemmeligheter kan ikke hentes via brukergrensesnittet eller API-et, de er redigert fra logger, og de befinner seg i et bestemt omfang: depot, miljø i et depot eller en organisasjon. Å velge riktig omfang er den første store avgjørelsen for en fornuftig hemmelig strategi.

Hemmelige omfang i GitHub-handlinger

Hemmeligheter for arkiv, miljø og organisasjon

GitHub tilbyr tre hovedlag for hemmeligheter: depothemmeligheter, miljøhemmeligheter og organisasjonshemmeligheter, hver med sine egne brukstilfeller og prioritetsregler. Å forstå hvordan de samhandler hjelper deg med å unngå konflikter og holde sensitive verdier der de hører hjemme.

Hemmeligheter på repositorinivå

Repository-hemmeligheter er knyttet til et enkelt repository og er tilgjengelige for alle arbeidsflyter i det repositoryen. De er perfekte for prosjektspesifikke verdier som en tjenestes API-nøkkel, et distribusjonspassord eller en webhook-token som bare brukes av det depotet.

For å opprette en depothemmelighet fra brukergrensesnittet, går du til måldepotet, åpner «Innstillinger» → «Hemmeligheter og variabler» → «Handlinger», og klikker deretter på «Ny depothemmelighet». Du gir den et navn med store bokstaver med understrek (for eksempel PAYMENTS_API_KEY), lim inn den hemmelige verdien og lagre; fra det øyeblikket kan arbeidsflyter få tilgang til den som ${{ secrets.PAYMENTS_API_KEY }}.

Alle med skrivetilgang til depotet kan referere til disse hemmelighetene i arbeidsflyter, slik at tillatelser på selve depotet blir en del av sikkerhetshistorien din. Hvis du tilfeldig gir skrivetilgang til mange brukere, gir du dem implisitt tilgang til å bruke alle depothemmeligheter i automatiseringen.

Miljøspesifikke hemmeligheter

Miljøhemmeligheter ligger ett nivå under depothemmeligheter og lar deg definere forskjellige verdier per miljø, for eksempel dev, stagingeller production. De er knyttet til et navngitt miljø og kan beskyttes med regler som obligatoriske korrekturlesere eller ventetider før en jobb kan kjøres mot dem.

Du konfigurerer disse ved å gå til «Innstillinger» → «Miljøer», opprette eller velge et miljø, og deretter legge til hemmeligheter i den miljøkonfigurasjonen. De hemmelige navnene bruker fortsatt secrets. kontekst (for eksempel secrets.PROD_DB_PASSWORD), men verdiene blir bare tilgjengelige for jobber som eksplisitt kjører i det miljøet.

En viktig detalj er at miljøhemmeligheter overstyrer depothemmeligheter når de deler samme navn. Det betyr at du kan definere DB_PASSWORD på repositorinivå for lokal/testbruk og deretter ha en annen DB_PASSWORD som en miljøhemmelighet for produksjon som prioriteres i produksjonsjobber, uten å endre arbeidsflytsyntaksen.

Miljøer aktiverer også beskyttelsesregler som «påkrevde anmeldere» eller «kun fra bestemte grener», noe som er utrolig nyttig for å sette rekkverk rundt tilgangen til dine mest sensitive hemmeligheter. For eksempel kan et produksjonsmiljø kreve godkjenning fra DevOps før noen jobb som bruker hemmelighetene kan kjøres.

Hemmeligheter for hele organisasjonen

Organisasjonshemmeligheter deles på tvers av flere databaser i en organisasjon og er ideelle for legitimasjon som gjenbrukes bredt, som en delt Slack-webhook eller et sentralt metrics API-token. De reduserer duplisering og gjør rotasjon enklere fordi du oppdaterer hemmeligheten én gang for alle, og forbrukende repoer plukker opp den nye verdien.

Administratorer oppretter dem i organisasjonens «Innstillinger» → «Hemmeligheter og variabler» → «Handlinger»-del, klikker på «Ny organisasjonshemmelighet» og velger deretter hvilke databaser som har tilgang til den hemmeligheten. Du kan tillate alle nåværende og fremtidige repositorier eller begrense det strengt til et bestemt delsett.

Det finnes en prioritetskjede du bør huske på: organization secret < repository secret < environment secret når navn kolliderer. Med andre ord vinner en miljøhemmelighet over en repositoryhemmelighet, som vinner over en organisasjonshemmelighet hvis de alle deler samme nøkkel.

Hvordan hemmeligheter oppfører seg under kjøring

Når hemmeligheter er definert, blir de tilgjengelige for jobber under kjøring gjennom secrets kontekst og injiseres som miljøvariabler når det refereres til dem. De eksporteres ikke som standard til alle trinn; du kobler dem eksplisitt til din env: blokker eller send dem til handlinger som støtter hemmeligheter som input.

GitHub tilbyr også en spesiell GITHUB_TOKEN per arbeidsflytkjøring, som ikke er en manuelt definert hemmelighet, men oppfører seg som en og ofte brukes til API-kall eller repository-operasjoner. Du kan (og bør) finjustere de finjusterte tillatelsene til dette tokenet ved hjelp av permissions: blokk slik at den har det minimumsomfanget som trengs for hver jobb.

For å redusere utilsiktet eksponering maskerer GitHub alle verdier som samsvarer med en registrert hemmelighet i arbeidsflytlogger, og erstatter dem med ***. Denne maskeringen gjøres på runner-siden, og den fungerer vanligvis bra for rå strenger, men den forutsetter at den nøyaktige hemmelighetsverdien vises i utdataene. Hvis du transformerer hemmeligheten (for eksempel base64-koder den eller bygger den inn i strukturert JSON), kan det hende at masken ikke klarer å fange den opp.

Fordi maskering er best-effort og ikke matematisk garantert, bør du utforme arbeidsflyter for å unngå å skrive ut hemmeligheter eller deres avledninger til logger i det hele tatt, og bruke maskeringskommandoer for tilleggsverdier du genererer under kjøretid. Behandle logger som potensielt synlige for flere enn du forventer, og anta at alt som skrives ut kan lekke.

Praktisk bruk: referere til hemmeligheter i arbeidsflyter

Som oftest bruker du hemmeligheter ved å tilordne dem til miljøvariabler i et bestemt trinn eller en bestemt jobb, og deretter la skriptene eller verktøyene dine lese fra miljøet. Et klassisk mønster ser slik ut:


– navn: Distribuer til API
env:
API_KEY: ${{ secrets.PROD_API_KEY }}
løp: |
curl -H “Autorisering: Bærer $API_KEY” https://api.example.com/deploy

Du kan også skrive en hemmelighet til en fil på løperen, som er sikker så lenge filen forblir innenfor jobbens flyktige arbeidsområde og ikke blir lagret eller lastet opp som en artefakt. For eksempel lagring av en SSH-nøkkel:


– navn: Skriv SSH-nøkkel til fil
skall: bash
env:
SSH_KEY: ${{ hemmeligheter.SSH_KEY }}
løp: |
ekko «$SSH_KEY» > nøkkel
chmod 600-nøkkel

Fra loggene vil du bare se den faktiske skallkommandoen (med $SSH_KEY), men ikke selve den hemmelige verdien, som vil bli redigert eller skjult. Fordi GitHub-hostede løpere blir ødelagt etter at jobben er fullført, forsvinner den midlertidige filen med den virtuelle maskinen. På selvhostede løpere må du være mye strengere med oppryddingen.

Beste sikkerhetspraksis for hemmeligheter i GitHub Actions

Det er ikke nok å bare bruke det hemmelige brukergrensesnittet; du trenger et sett med vaner og sikkerhetstiltak for å minimere eksplosjonsradiusen hvis noe går galt. GitHub tilbyr mange knotter, men det er opp til deg å vri dem riktig.

Bruk prinsippet om minste privilegium

Hver hemmelighet og hvert token skal bare gi de tillatelsene som er absolutt nødvendige for en gitt oppgave. For eksterne tjenester bør du opprette dedikerte legitimasjonsopplysninger med begrensede tillatelser (for eksempel «kun distribusjon» eller «skrivebeskyttet beregning») i stedet for å bruke fullstendige administratornøkler på nytt.

Det samme prinsippet gjelder for den innebygde GITHUB_TOKEN; sett standardtillatelser til det absolutte minimum (ofte contents: read) og deretter øke tillatelsene bare i spesifikke jobber som trenger flere. Du konfigurerer dette med en permissions: seksjonen på arbeidsflyt- eller jobbnivå, slik at en kompromittert jobb ikke i stillhet kan utføre vilkårlige skrivinger.

Unngå å skrive ut eller kode hemmeligheter i logger

Hemmeligheter skal aldri hardkodes i arbeidsflytfiler eller skrives ut i ren tekst for enkel feilsøking. Hvis du har andre sensitive verdier som ikke er registrert som GitHub-hemmeligheter (for eksempel et token generert under kjøring), kan du fortsatt be kjøreren om å behandle dem som hemmeligheter ved å bruke kommandosyntaksen:


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

Strukturerte blober som JSON, XML eller store YAML-dokumenter er spesielt farlige som hemmeligheter fordi GitHubs masker er avhengig av nøyaktig strengsamsvar. Hvis du legger flere sensitive verdier i én stor JSON-streng og bruker den som én hemmelighet, kan små formateringsendringer føre til at maskeringen mislykkes. Definer i stedet individuelle hemmeligheter for hvert sensitive felt.

Gjennomgå alltid logger når du tester arbeidsflyter, og vær spesielt oppmerksom på feilmeldinger og stakkspor. Noen verktøy gjentar gjerne kommandoer og flagg til stderr, som ved et uhell kan inkludere hemmelige verdier med mindre du eksplisitt unngår det mønsteret.

Roter og revider hemmeligheter regelmessig

Hemmelig rotasjon er kjedelig, men ikke noe å forhandle om hvis du bryr deg om sikkerhet. Å la legitimasjon være uendret i årevis øker mulighetsvinduet for angripere. Et rimelig utgangspunkt er å rotere de mest kritiske produksjonshemmelighetene månedlig, de med høy risiko annenhver måned, og alt annet minst kvartalsvis.

Du kan automatisere deler av dette ved hjelp av GitHub REST API for secrets, som lar deg hente den offentlige nøkkelen til et repositorium eller en organisasjon og laste opp nye krypterte verdier. Dette er spesielt nyttig for store organisasjoner med mange repositorier som deler tjenestekontoer og trenger å rotere dem raskt som svar på hendelser.

Revisjon er like viktig: gjennomgå regelmessig listen over konfigurerte hemmeligheter og slett de som ikke lenger brukes, og bruk GitHubs sikkerhets-/revisjonslogger til å spore hendelser som org.update_actions_secret. På den måten vet du hvem som endret hva og når, og du kan koble mistenkelige endringer med annen aktivitet.

Bruk miljøbasert separasjon

Miljøhemmeligheter er en av de enkleste måtene å styrke pipelines på, fordi de lar deg isolere svært sensitive verdier (som produksjons-DB-legitimasjon) bak eksplisitte godkjenninger. Du kan kreve menneskelige kontrollører, begrense hvilke grener som kan distribueres, og til og med legge til nedkjølingstider før en distribusjon starter.

Et vanlig mønster er å ha en staging miljø med mild beskyttelse og en production miljø med strengere regler og separate hemmeligheter. Arbeidsflyter definerer deretter jobber som er målrettet mot hvert miljø, og sikrer at produkthemmeligheter aldri brukes ved et uhell i testjobber.

Velg tydelige navnekonvensjoner

Gode ​​navn på hemmeligheter sparer deg for frustrerende gjetting og farlige forvekslinger. I stedet for generiske navn som API_KEY, kode tjenesten og miljøet i navnet, for eksempel STRIPE_PROD_API_KEY or AWS_STAGING_DEPLOY_ROLE_ARN.

Team som håndterer mange tjenester bruker ofte et mønster som <SERVICE>_<ENV>_<PURPOSE>. Så du har kanskje SLACK_PROD_ALERTS_WEBHOOK, GCP_DEV_BUILD_SERVICE_ACCOUNTog DB_STAGING_PASSWORDDette gjør det mye tydeligere hvilken hemmelighet som skal brukes i hvilken jobb.

Beskyttelse mot skriptinjeksjon og tredjepartsrisikoer

Hemmeligheter er ikke bare i faresonen på grunn av feilkonfigurasjon; de er også fristende mål for skriptinjeksjon i arbeidsflyter og ondsinnede eller kompromitterte tredjepartshandlinger. Et enkelt sårbart trinn kan avsløre alle hemmeligheter som er tilgjengelige for jobben hvis du ikke er forsiktig.

Begrensning av skriptinjeksjon i innebygde trinn

Når du interpolerer upålitelige data (som titler på pull-forespørsler, grennavn eller kommentarer til problemstillinger) direkte inn i skallskript, åpner du døren for injeksjon. For eksempel kan en PR-tittel lages for å bryte ut av en kommando og kjøre vilkårlig skallkode i løperen din.

Den sikreste tilnærmingen er å håndtere kompleks logikk i førsteparts- eller godt reviderte JavaScript/TypeScript-handlinger og sende upålitelige verdier som input i stedet for å legge dem inn i skallet. I den modellen mottar handlingen strenger som argumenter og behandler dem uten å generere skallskript som kan kapres.

Hvis du må bruke inline-shell, lagrer du først upålitelige verdier i miljøvariabler, og refererer deretter til disse variablene, helst i doble anførselstegn. På den måten behandles verdien som data i stedet for som en del av skriptstrukturen, noe som gjør det langt mindre sannsynlig at injeksjonsforsøk lykkes.

Fest og gjennomgå tredjepartshandlinger

Hver tredjepartshandling du trekker inn i arbeidsflyten din kjører med tilgang til jobbens miljø og hemmeligheter, så du bør behandle dem som kodeavhengigheter som krever gransking. En ondsinnet eller kompromittert handling kan lese hemmeligheter og sende dem til en angriper med et enkelt HTTP-kall.

Beste praksis er å feste handlinger med full commit SHA i stedet for bare tagger eller grener, fordi tagger kan flyttes eller overskrives. En SHA refererer til en eksakt versjon av koden, noe som gjør det mye vanskeligere for en angriper å stille injisere ny atferd uten at du oppdaterer arbeidsflyten.

Før du bruker en handling, bør du skumlese kildekoden (eller i det minste en sikkerhetsgjennomgang) for å sikre at den håndterer hemmeligheter ansvarlig og ikke logger dem eller sender dem til ukjente endepunkter. Hvis du bruker markedsplasshandlinger, må du bekrefte utgiveren der det er mulig, og stole på at Dependabot varsler deg om sårbarheter og oppdateringer.

Vertskapsbaserte vs. selvvertskapsbaserte løpere og hemmelig avsløring

Hvor arbeidsflytene dine kjører har stor innvirkning på hvor trygt hemmeligheter håndteres. GitHub-hostede løpere og selvhostede løpere oppfører seg veldig forskjellig når det gjelder isolasjon og utholdenhet.

GitHub-hostede runners spinner opp nye virtuelle maskiner for hver jobb, kjører arbeidsflyten din og river dem deretter ned. Det gir deg et rent miljø hver gang og sikrer at alle filer eller miljøvariabler (inkludert hemmeligheter skrevet til disk) blir ødelagt når jobben er fullført.

Selvhostede løpere er derimot maskiner med lang levetid du administrerer, noe som betyr at all kode med tilgang til hemmeligheter potensielt kan vedvare eller fjerne dem utover levetiden til en enkelt jobb. På offentlige databaser er selvhostede løpere spesielt risikable fordi upålitelige bidragsytere kan åpne pull-forespørsler som utløser arbeidsflyter.

Hvis du bruker selvhostede løpere, isoler dem etter følsomhetsnivå, begrens hvilke repositorier som kan bruke hvilke løpere, og vær paranoid om hva annet som ligger på disse maskinene (SSH-nøkler, skylegitimasjon, nettverkstilgang til interne tjenester og så videre). Noen organisasjoner bruker selvhostede «just-in-time» (JIT)-løpere som opprettes via API for én enkelt jobb og deretter slettes, men selv da må du sørge for at jobber ikke deler den samme løperen uventet.

Bruk av OpenID Connect (OIDC) i stedet for langvarige skyhemmeligheter

En av de største seirene for hemmelig hygiene i GitHub Actions er å erstatte langvarige skytilgangsnøkler med kortvarige legitimasjonsdetaljer via OpenID Connect. I stedet for å lagre AWS-, Azure- eller GCP-nøkler som hemmeligheter, ber arbeidsflytene dine om midlertidige tokener fra skyleverandøren ved å bruke GitHub som identitetsleverandør.

Flyten ser omtrent slik ut: jobben ber om en signert JWT fra GitHubs OIDC-endepunkt, skyleverandøren din validerer den tokenen og bytter den mot kortvarig legitimasjon, og arbeidsflyten bruker denne legitimasjonen så lenge jobben varer. Ingen statisk hemmelighet trenger noen gang å bo i GitHub.

Med AWS konfigurerer du for eksempel en IAM-rolle som stoler på GitHub OIDC-leverandøren og begrenser hvilke repositorier/grener som kan påta seg den rollen. Så bruker du en handling som i arbeidsflyten din aws-actions/configure-aws-credentials med OIDC-tillatelser aktivert for å innhente legitimasjon på farten.

Denne tilnærmingen har flere fordeler: det er ingenting å rotere inne i GitHub, tokener er automatisk kortvarige, tilgangen er snevert begrenset, og du får fullstendige revisjonslogger på skysiden som sporer alle rolleantagelser. For miljøer med høy sikkerhet bør OIDC være standard der det støttes.

Innebygde verktøy og eksterne hemmelighetsadministratorer

GitHubs innebygde hemmeligheter er flotte for mange scenarioer, men på et tidspunkt vil du kanskje ha en mer sentral, funksjonsrik hemmelighetsbehandler som spenner over flere plattformer og miljøer. Verktøy som HashiCorp Vault, Infisical eller Doppler brukes ofte sammen med GitHub Actions til dette formålet.

Disse systemene kan håndtere dynamiske hemmeligheter (for eksempel generering av kortlivede databasebrukere), avanserte tilgangskontrollpolicyer, detaljerte revisjonslogger og rotasjonsarbeidsflyter som går utover det GitHub alene tilbyr. GitHub-handlinger autentiserer deretter til disse managerne (ofte via OIDC eller en annen autentiseringsmetode), henter hemmelighetene de trenger under kjøring og bruker dem uten å lagre langsiktige legitimasjonsopplysninger i depotet.

Det finnes også fellesskapshandlinger og plugins som er utformet for å hente hemmeligheter fra eksterne ledere direkte inn i arbeidsflyter. Når du bruker dem, gjelder det samme rådet: gjennomgå handlingens kilde, fest den til en commit SHA, og begrens rettighetene som er gitt til tokenet eller rollen den bruker for å nå det eksterne systemet.

Sikker hemmelighetsadministrasjon i GitHub Actions betyr å velge riktig omfang for hver hemmelighet, håndheve minimumsrettigheter, bruke miljøer og OIDC der det er aktuelt, behandle logger og tredjepartshandlinger som potensielle angrepsflater, og lene seg på eksterne hemmelighetsadministratorer når skalaen eller samsvarskravene dine krever det. Med disse fremgangsmåtene på plass, kan CI/CD-pipelinene dine forbli fleksible og raske, samtidig som de reduserer sjansene for at en feilplassert token eller en dårlig gjennomgått arbeidsflyt utvikler seg til en fullskala hendelse dramatisk.

Relaterte innlegg: