Gestionarea securizată a secretelor în Acțiunile GitHub

Ultima actualizare: 12/01/2025
  • Secretele GitHub Actions sunt variabile de mediu criptate, cu scop definit, care trebuie atent definite la nivel de repozitoriu, mediu și organizație.
  • Securitatea se bazează pe privilegiile minime, evitarea expunerii jurnalelor, rotirea și auditarea secretelor și izolarea mediilor de producție sensibile.
  • Riscurile generate de injectarea de scripturi, acțiunile terților și rulourile auto-găzduite necesită fixarea, revizuirea codului și politici stricte privind rulourile și permisiunile.
  • OpenID Connect și managerii de secrete externi ajută la înlocuirea acreditărilor de lungă durată cu token-uri de scurtă durată și fluxuri de lucru centralizate și auditabile pentru secrete.

Gestionarea secretelor GitHub Actions

Gestionarea secretelor în GitHub Actions este unul dintre acele subiecte care pare simplu la prima vedere, dar se transformă rapid într-o problemă critică de securitate odată ce conductele tale încep să se conecteze cu producția, furnizorii de cloud și serviciile terțe. Fluxurile de lucru CI/CD gestionează în mod curent chei API, parole de baze de date, chei SSH, token-uri și multe altele, iar fiecare dintre aceste valori reprezintă un potențial punct de intrare pentru un atacator dacă este gestionată cu neglijență.

În acest ghid, vom analiza în detaliu cum funcționează secretele în GitHub Actions, cum să le configurem la nivel de repozitoriu, mediu și organizație, cum să consolidăm fluxurile de lucru împotriva scurgerilor de informații și a atacurilor din lanțul de aprovizionare și când este util să apelăm la manageri de secrete externi. Ideea este de a vă oferi o prezentare generală practică, axată pe securitate, astfel încât să vă puteți menține fluxurile de lucru rapide. și în siguranță, fără a transforma munca de zi cu zi într-o bătaie de cap.

Care sunt mai exact secretele GitHub Actions?

În GitHub Actions, un „secret” este o variabilă de mediu criptată a cărei valoare este ascunsă din interfața cu utilizatorul, jurnale și conținutul depozitului. Îl definești o singură dată (la nivel de depozit, organizație sau mediu), apoi îl referențiezi în fluxul de lucru YAML folosind secrets. context, astfel încât conductele tale să poată utiliza valori sensibile fără a le include vreodată în baza de cod.

În mod implicit, GitHub criptează secretele folosind criptografie puternică (cutii sigilate Libsodium) înainte ca acestea să ajungă pe serverele GitHub, iar valorile sunt decriptate doar în timpul execuției, pe rulerul fluxului de lucru. Odată create, secretele sunt imuabile din interfața cu utilizatorul: le puteți suprascrie, dar nu le puteți citi înapoi, iar când apar în jurnale, sunt mascate automat cu *** oricând este posibil.

Acest model vine cu câteva constrângeri de design importante de care trebuie să fiți conștienți: secretele nu pot fi recuperate prin intermediul interfeței utilizator sau al API-ului, sunt redactate din jurnale și se află într-un anumit domeniu de aplicare: depozit, mediu în cadrul unui depozit sau organizație. Alegerea domeniului de aplicare potrivit este prima decizie importantă pentru o strategie secretă sănătoasă.

Domenii de aplicare secrete în acțiunile GitHub

Secrete de depozit, mediu și organizație

GitHub oferă trei straturi principale pentru secrete: secrete de repozitoriu, secrete de mediu și secrete de organizație, fiecare cu propriile cazuri de utilizare și reguli de precedență. Înțelegerea modului în care interacționează te ajută să eviți conflictele și să păstrezi valorile sensibile acolo unde le este locul.

Secrete la nivel de depozit

Secretele repozitoriului sunt legate de un singur repozitoriu și sunt disponibile pentru toate fluxurile de lucru din acel repozitoriu. Sunt perfecte pentru valori specifice proiectului, cum ar fi cheia API a unui serviciu, o parolă de implementare sau un token webhook utilizat doar de depozitul respectiv.

Pentru a crea un secret de repozitoriu din interfața cu utilizatorul, accesați repozitoriul țintă, deschideți „Setări” → „Secrete și variabile” → „Acțiuni”, apoi faceți clic pe „Secret nou de repozitoriu”. Îi atribuiți un nume cu majusculă și sublinieri (de exemplu PAYMENTS_API_KEY), lipiți valoarea secretă și salvați; din acel moment, fluxurile de lucru o pot accesa ca ${{ secrets.PAYMENTS_API_KEY }}.

Oricine are acces de scriere la repozitoriu poate face referire la aceste secrete în fluxurile de lucru, astfel încât permisiunile asupra repozitoriului în sine devin parte a securității dumneavoastră. Dacă acordați în mod casual acces de scriere mai multor utilizatori, le acordați implicit acces pentru a utiliza fiecare secret al repozitoriului din automatizare.

Secrete specifice mediului

Secretele de mediu se află la un nivel sub secretele depozitului și vă permit să definiți valori diferite pentru fiecare mediu, cum ar fi dev, staging, production. Acestea sunt atașate unui mediu denumit și pot fi protejate cu reguli precum recenzori obligatorii sau temporizatori de așteptare înainte ca un job să poată rula asupra lor.

Le configurați accesând „Setări” → „Medii”, creând sau selectând un mediu și apoi adăugând secrete în configurația mediului respectiv. Numele secrete încă folosesc secrets. context (de exemplu secrets.PROD_DB_PASSWORD), dar valorile devin disponibile doar pentru joburile care rulează explicit în acel mediu.

Un detaliu cheie este că secretele de mediu au prioritate față de secretele depozitului atunci când au același nume. Asta înseamnă că poți defini DB_PASSWORD la nivel de depozit pentru utilizări locale/de testare și apoi să aibă un alt DB_PASSWORD ca secret de mediu pentru producție care are prioritate în joburile de producție, fără a modifica sintaxa fluxului de lucru.

Mediile permit, de asemenea, reguli de protecție precum „recenzori obligatorii” sau „doar din anumite ramuri”, ceea ce este incredibil de util pentru a pune bariere în jurul accesului la cele mai sensibile secrete. De exemplu, un mediu de producție ar putea necesita aprobarea DevOps înainte de a putea rula orice job care utilizează secretele sale.

Secrete la nivelul întregii organizații

Secretele organizației sunt partajate în mai multe repozitorii dintr-o organizație și sunt ideale pentru acreditări reutilizate pe scară largă, cum ar fi un webhook Slack partajat sau un token API de metrici centrale. Acestea reduc duplicarea și facilitează rotația, deoarece actualizați secretul o singură dată, iar toate depozitele care consumă date preiau noua valoare.

Administratorii le creează în secțiunea „Setări” → „Secrete și variabile” → „Acțiuni” a organizației, făcând clic pe „Secret nou al organizației” și apoi alegând ce depozite pot accesa secretul respectiv. Puteți permite toate depozitele actuale și viitoare sau le puteți restricționa strict la un subset specific.

Există un lanț de precedență pe care ar trebui să îl țineți cont: secretul organizației < secretul repozitoriului < secretul mediului atunci când numele se ciocnesc. Cu alte cuvinte, un secret de mediu învinge un secret de repozitoriu, care învinge un secret de organizație dacă toate au aceeași cheie.

Cum se comportă secretele în timpul execuției

Odată definite, secretele devin disponibile pentru joburi în timpul execuției prin intermediul secrets context și sunt injectate ca variabile de mediu atunci când se face referire. Acestea nu sunt exportate în general la fiecare pas în mod implicit; le conectați explicit în env: blocuri sau să le transmită în acțiuni care acceptă secrete ca intrări.

GitHub oferă, de asemenea, o funcție specială GITHUB_TOKEN per rulare a fluxului de lucru, care nu este un secret definit manual, dar se comportă ca unul și este adesea utilizat pentru apeluri API sau operațiuni de depozit. Puteți (și ar trebui) să ajustați permisiunile detaliate ale acestui token folosind permissions: bloc astfel încât să aibă domeniul de aplicare minim necesar pentru fiecare job.

Pentru a reduce expunerea accidentală, GitHub maschează orice valoare care se potrivește cu un secret înregistrat în jurnalele fluxului de lucru, înlocuind-o cu ***. Această mascare se face pe partea de rulare și, în general, funcționează bine pentru șiruri brute, dar presupune că valoarea exactă a secretului apare în ieșire. Dacă transformați secretul (de exemplu, îl codificați în base64 sau îl încorporați în JSON structurat), masca poate să nu îl detecteze.

Deoarece mascarea este cea mai eficientă metodă și nu este garantată matematic, ar trebui să proiectați fluxuri de lucru astfel încât să evitați deloc imprimarea secretelor sau a derivatelor acestora în jurnale și să utilizați comenzi de mascare pentru valorile suplimentare pe care le generați în timpul execuției. Tratați jurnalele ca fiind potențial vizibile pentru mai multe persoane decât vă așteptați și presupuneți că orice informație imprimată ar putea fi scursă.

Utilizare practică: referirea la secrete în fluxuri de lucru

De cele mai multe ori veți folosi secrete mapându-le în variabile de mediu într-un anumit pas sau job, apoi permițând scripturilor sau instrumentelor să citească din mediu. Un model clasic arată astfel:


– nume: Implementare în API
env:
API_KEY: ${{ secrets.PROD_API_KEY }}
alerga: |
curl -H „Autorizare: Purtător $API_KEY” https://api.example.com/deploy

De asemenea, puteți scrie un secret într-un fișier pe runner, ceea ce este securizat atâta timp cât fișierul rămâne în spațiul de lucru efemer al jobului și nu este validat sau încărcat ca artefact. De exemplu, stocarea unei chei SSH:


– nume: Scrie cheia SSH în fișier
shell: bash
env:
SSH_KEY: ${{ secrets.SSH_KEY }}
alerga: |
echo „$SSH_KEY” > cheie
cheia chmod 600

Din jurnale veți vedea doar comanda shell propriu-zisă (cu $SSH_KEY), dar nu valoarea secretă în sine, care va fi redactată sau ascunsă. Deoarece rulanții găzduiți pe GitHub sunt distruși după finalizarea jobului, acel fișier temporar dispare odată cu mașina virtuală; în cazul rulanților auto-găzduiți, trebuie să fii mult mai strict în ceea ce privește curățarea.

Cele mai bune practici de securitate pentru secrete în Acțiunile GitHub

Doar folosirea interfeței cu secrete nu este suficientă; ai nevoie de un set de obiceiuri și măsuri de siguranță pentru a minimiza raza exploziei dacă ceva nu merge bine. GitHub oferă multe butoane, dar depinde de tine să le rotești corect.

Aplicați principiul privilegiului minim

Fiecare secret și fiecare token ar trebui să acorde doar permisiunile absolut necesare pentru o anumită sarcină. Pentru serviciile externe, creați acreditări dedicate cu permisiuni limitate (de exemplu, „doar implementare” sau „indicatori doar citire”), în loc să reutilizați chei de administrator complete.

Același principiu se aplică și încorporării GITHUB_TOKEN; setați permisiunile implicite la minimul necesar (adesea contents: read) și apoi să crească permisiunile doar în anumite joburi care necesită mai multe. Configurați acest lucru cu un permissions: secțiune la nivel de flux de lucru sau de job, astfel încât un job compromis să nu poată efectua scrieri arbitrare în mod silențios.

Evitați afișarea sau codificarea secretelor în jurnale

Secretele nu ar trebui niciodată codificate în fișierele de flux de lucru sau afișate în text simplu pentru a facilita depanarea. Dacă aveți alte valori sensibile care nu sunt înregistrate ca secrete GitHub (de exemplu, un token generat la momentul execuției), puteți totuși solicita executantului să le trateze ca secrete folosind sintaxa comenzii:


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

Blob-urile structurate precum JSON, XML sau documentele YAML mari sunt deosebit de periculoase ca secrete, deoarece mascatorul GitHub se bazează pe potrivirea exactă a șirurilor de caractere. Dacă plasați mai multe valori sensibile într-un șir JSON mare și îl utilizați ca un singur secret, mici modificări de formatare pot cauza eșecul mascării; în schimb, definiți secrete individuale pentru fiecare câmp delicat.

Verificați întotdeauna jurnalele atunci când testați fluxurile de lucru, acordând o atenție deosebită mesajelor de eroare și urmelor stivei. Unele instrumente transmit cu ușurință comenzi și semnalizări către stderr, care pot include accidental valori secrete, cu excepția cazului în care evitați în mod explicit acel model.

Rotiți și auditați secretele în mod regulat

Rotația secretă este plictisitoare, dar nu este negociabilă dacă vă pasă de securitate; lăsarea acreditărilor neschimbate ani de zile crește fereastra de oportunitate pentru atacatori. O valoare de referință rezonabilă este să rotiți lunar secretele de producție cele mai critice, pe cele cu risc ridicat la fiecare câteva luni și toate celelalte cel puțin trimestrial.

Puteți automatiza o parte din acest proces folosind API-ul REST GitHub pentru secrete, care vă permite să preluați cheia publică a unui depozit sau organizație și să încărcați noi valori criptate. Acest lucru este util în special pentru organizațiile mari, cu multe depozite care partajează conturi de serviciu și trebuie să le rotească rapid ca răspuns la incidente.

Auditarea este la fel de importantă: revizuiți periodic lista de secrete configurate și ștergeți-le pe cele care nu mai sunt utilizate și folosiți jurnalele de securitate/audit ale GitHub pentru a urmări evenimente precum org.update_actions_secret. În acest fel, știi cine a modificat ce și când și poți corela modificările suspecte cu alte activități.

Folosește separarea bazată pe mediu

Secretele de mediu sunt una dintre cele mai simple metode de a consolida canalele de lucru, deoarece vă permit să izolați valori extrem de sensibile (cum ar fi acreditările bazei de date de producție) în spatele aprobărilor explicite. Puteți solicita recenzori umani, puteți limita ce sucursale pot fi implementate și chiar puteți adăuga cronometre de așteptare înainte de începerea unei implementări.

Un model comun este de a avea un staging mediu cu protecții ușoare și un production mediu cu reguli mai stricte și secrete separate. Fluxurile de lucru definesc apoi joburi care vizează fiecare mediu, asigurându-se că secretele prod nu sunt niciodată utilizate accidental în joburile de testare.

Alegeți convenții de denumire clare

O denumire corectă a secretelor te scutește de presupuneri frustrante și de confuzii periculoase. În loc de nume generice precum API_KEY, codifică serviciul și mediul în nume, de exemplu STRIPE_PROD_API_KEY or AWS_STAGING_DEPLOY_ROLE_ARN.

Echipele care se ocupă de multe servicii adoptă adesea un model precum <SERVICE>_<ENV>_<PURPOSE>. Deci s-ar putea să fi avut SLACK_PROD_ALERTS_WEBHOOK, GCP_DEV_BUILD_SERVICE_ACCOUNT și DB_STAGING_PASSWORDAcest lucru face mult mai evident ce secret ar trebui folosit în ce job.

Protecția împotriva injectării de scripturi și a riscurilor provenite de la terți

Secretele nu sunt doar expuse riscului de configurare greșită; ele sunt, de asemenea, ținte tentante pentru injectarea de scripturi în fluxurile de lucru și acțiuni rău intenționate sau compromise ale terților. Un singur pas vulnerabil poate exfiltra fiecare secret accesibil jobului dacă nu ești atent.

Atenuarea injectării de script în pași inline

Când interpolezi date nesigure (cum ar fi titluri de solicitări de extragere, nume de ramuri sau comentarii la probleme) direct în scripturi shell, deschizi calea către injectare. De exemplu, un titlu PR ar putea fi creat pentru a ieși dintr-o comandă și a rula cod shell arbitrar în runner.

Cea mai sigură abordare este gestionarea logicii complexe în acțiunile JavaScript/TypeScript proprie sau bine auditate și transmiterea valorilor nesigure ca intrări, în loc să le inlineze în shell. În acest model, acțiunea primește șiruri de caractere ca argumente și le procesează fără a genera scripturi shell care pot fi deturnate.

Dacă trebuie să utilizați shell inline, stocați mai întâi valorile nesigure în variabilele de mediu și apoi faceți referire la acele variabile, de preferință între ghilimele duble. În acest fel, valoarea este tratată ca date, mai degrabă decât ca parte a structurii scriptului, ceea ce face ca încercările de injectare să fie mult mai puțin probabile de reușită.

Fixează și revizuiește acțiunile terților

Fiecare acțiune terță parte pe care o introduceți în fluxul de lucru rulează cu acces la mediul și secretele jobului, așa că ar trebui să le tratați ca dependențe de cod care necesită atenție sporită. O acțiune rău intenționată sau compromisă poate citi secrete și le poate trimite unui atacator cu un singur apel HTTP.

Cea mai bună practică este să fixați acțiunile prin SHA complet de validare în loc de doar etichete sau ramuri, deoarece etichetele pot fi mutate sau suprascrise. Un SHA se referă la o versiune exactă a codului, ceea ce face mult mai dificilă pentru un atacator injectarea silențioasă a unui nou comportament fără ca tu să actualizezi fluxul de lucru.

Înainte de a utiliza o acțiune, parcurgeți codul sursă (sau cel puțin o analiză de securitate) pentru a vă asigura că gestionează secretele în mod responsabil și nu le înregistrează și nu le trimite către endpoint-uri necunoscute. Dacă folosești acțiuni pe piață, verifică editorul acolo unde este posibil și bazează-te pe Dependabot pentru a te alerta cu privire la vulnerabilități și actualizări.

Concurenți găzduiți vs. alergători auto-găzduiți și expunere secretă

Locul în care se execută fluxurile de lucru are un impact uriaș asupra modului în care sunt gestionate în siguranță secretele. Runcătorii găzduiți pe GitHub și runcătorii auto-găzduiți se comportă foarte diferit în ceea ce privește izolarea și persistența.

Executările găzduite pe GitHub pornesc mașini virtuale noi pentru fiecare job, execută fluxul de lucru și apoi le demontează. Asta îți oferă un mediu curat de fiecare dată și asigură că orice fișiere sau variabile de mediu (inclusiv secretele scrise pe disc) sunt distruse odată ce lucrarea se finalizează.

În schimb, rulourile auto-găzduite sunt mașini cu durată lungă de viață pe care le gestionați, ceea ce înseamnă că orice cod cu acces la secrete le poate persista sau le poate exfiltra dincolo de durata de viață a unui singur job. În depozitele publice, rulourile auto-găzduite sunt deosebit de riscante, deoarece contribuitorii neîncrezători pot deschide cereri de extragere care declanșează fluxuri de lucru.

Dacă folosiți runnere auto-găzduite, izolați-le în funcție de nivelul de sensibilitate, restricționați ce repozitorii pot utiliza ce runnere și fiți paranoici în legătură cu ce altceva se află pe acele mașini (chei SSH, acreditări cloud, acces la rețea la servicii interne și așa mai departe). Unele organizații utilizează rulouri auto-găzduite „just-in-time” (JIT), create prin API pentru un singur job și apoi distruse, dar chiar și în acest caz trebuie să vă asigurați că joburile nu partajează același rulor în mod neașteptat.

Utilizarea OpenID Connect (OIDC) în locul secretelor cloud de lungă durată

Una dintre cele mai mari victorii în materie de igienă secretă în GitHub Actions este înlocuirea cheilor de acces la cloud cu durată lungă de viață cu acreditări cu durată scurtă de viață prin OpenID Connect. În loc să stocheze cheile AWS, Azure sau GCP ca secrete, fluxurile de lucru solicită token-uri temporare de la furnizorul de cloud folosind GitHub ca furnizor de identitate.

Fluxul arată aproximativ astfel: jobul solicită un JWT semnat de la endpoint-ul OIDC al GitHub, furnizorul dvs. de cloud validează acel token și îl schimbă cu acreditări de scurtă durată, iar fluxul de lucru folosește aceste acreditări pe durata jobului. Niciun secret static nu trebuie să existe vreodată pe GitHub.

De exemplu, cu AWS configurați un rol IAM care are încredere în furnizorul GitHub OIDC și restricționează ce repozitorii/ramuri pot asuma acel rol. Apoi, în fluxul tău de lucru folosești o acțiune precum aws-actions/configure-aws-credentials cu permisiuni OIDC activate pentru a obține acreditări din mers.

Această abordare are multiple avantaje: nu există nimic care să se rotească în GitHub, token-urile au automat o durată scurtă de viață, accesul este limitat la un anumit domeniu și obțineți jurnale complete de audit pe partea de cloud care urmăresc fiecare presupunere de rol. Pentru mediile cu securitate ridicată, OIDC ar trebui să fie implicit, acolo unde este acceptat.

Instrumente native și manageri de secrete externe

Secretele încorporate în GitHub sunt excelente pentru multe scenarii, dar la un moment dat s-ar putea să doriți un manager de secrete mai centralizat, bogat în funcții, care să se întindă pe mai multe platforme și medii. Instrumente precum HashiCorp Vault, Infisical sau Doppler sunt frecvent utilizate alături de GitHub Actions în acest scop.

Aceste sisteme pot gestiona secrete dinamice (de exemplu, generarea de utilizatori de baze de date cu durată scurtă de viață), politici avansate de control al accesului, jurnale de audit detaliate și fluxuri de lucru cu rotație care depășesc ceea ce oferă singur GitHub. Acțiunile GitHub se autentifică apoi la acești manageri (adesea prin OIDC sau o altă metodă de autentificare), preiau secretele de care au nevoie în timpul execuției și le utilizează fără a stoca vreodată acreditări pe termen lung în depozit.

Există, de asemenea, acțiuni comunitare și pluginuri concepute pentru a extrage secrete de la manageri externi direct în fluxuri de lucru. Când le folosiți, se aplică același sfat: verificați sursa acțiunii, fixați-o la un SHA de commit și limitați privilegiile acordate token-ului sau rolului pe care îl folosește pentru a ajunge la sistemul extern.

Gestionarea în siguranță a secretelor în GitHub Actions înseamnă alegerea domeniului de aplicare potrivit pentru fiecare secret, impunerea privilegiilor minime, utilizarea mediilor și a OIDC acolo unde este cazul, tratarea jurnalelor și a acțiunilor terților ca potențiale suprafețe de atac și apelarea la manageri de secrete externi atunci când cerințele de scalare sau de conformitate o impun. Cu aceste practici implementate, fluxurile de lucru CI/CD pot rămâne flexibile și rapide, reducând în același timp dramatic șansele ca un token plasat greșit sau un flux de lucru revizuit necorespunzător să se transforme într-un incident complet.

Postări asemănatoare: