Manajemen rahasia yang aman di GitHub Actions

Pembaharuan Terakhir: 12/01/2025
  • Rahasia GitHub Actions adalah variabel lingkungan terenkripsi dan memiliki cakupan yang harus ditentukan secara cermat di tingkat repositori, lingkungan, dan organisasi.
  • Keamanan bergantung pada hak istimewa paling rendah, menghindari paparan log, memutar dan mengaudit rahasia, serta mengisolasi lingkungan produksi yang sensitif.
  • Risiko dari injeksi skrip, tindakan pihak ketiga, dan runner yang dihosting sendiri memerlukan penyematan, peninjauan kode, serta kebijakan runner dan izin yang ketat.
  • OpenID Connect dan pengelola rahasia eksternal membantu mengganti kredensial jangka panjang dengan token jangka pendek dan alur kerja rahasia terpusat yang dapat diaudit.

Manajemen rahasia GitHub Actions

Mengelola rahasia di GitHub Actions adalah salah satu topik yang tampaknya sederhana pada pandangan pertama, tetapi dengan cepat berubah menjadi masalah keamanan kritis setelah jaringan Anda mulai menyentuh produksi, penyedia cloud, dan layanan pihak ketiga. Alur kerja CI/CD Anda secara rutin menangani kunci API, kata sandi basis data, kunci SSH, token, dan banyak lagi, dan masing-masing nilai tersebut merupakan titik masuk potensial bagi penyerang jika ditangani secara sembarangan.

Dalam panduan ini kita akan mendalami cara kerja rahasia di GitHub Actions, cara mengonfigurasinya di tingkat repositori, lingkungan, dan organisasi, cara memperkuat alur kerja terhadap kebocoran dan serangan rantai pasokan, serta kapan saatnya perlu melibatkan pengelola rahasia eksternal. Idenya adalah untuk memberi Anda gambaran praktis dan berfokus pada keamanan sehingga Anda dapat menjaga jaringan pipa Anda tetap cepat dan aman tanpa mengubah pekerjaan sehari-hari menjadi sakit kepala.

Apa sebenarnya rahasia GitHub Actions?

Dalam GitHub Actions, "rahasia" adalah variabel lingkungan terenkripsi yang nilainya disembunyikan dari UI, log, dan konten repositori. Anda mendefinisikannya sekali (di tingkat repo, org atau lingkungan), lalu merujuknya di YAML alur kerja Anda menggunakan secrets. konteks, sehingga jalur pipa Anda dapat menggunakan nilai sensitif tanpa pernah memasukkannya ke basis kode.

Di balik layar, GitHub mengenkripsi rahasia menggunakan kriptografi yang kuat (kotak tertutup Libsodium) sebelum rahasia tersebut sampai ke server GitHub, dan nilainya hanya didekripsi saat runtime pada alur kerja pelari. Setelah dibuat, rahasia tidak dapat diubah dari UI: Anda dapat menimpanya tetapi Anda tidak dapat membacanya kembali, dan ketika muncul di log, rahasia tersebut secara otomatis ditutupi dengan *** sedapat mungkin.

Model ini memiliki beberapa batasan desain penting yang perlu Anda ketahui: rahasia tidak dapat diambil melalui UI atau API, rahasia tersebut dihapus dari log, dan berada dalam cakupan tertentu: repositori, lingkungan di dalam repositori, atau organisasi. Memilih cakupan yang tepat adalah keputusan besar pertama untuk strategi rahasia yang sehat.

Cakupan rahasia dalam GitHub Actions

Rahasia repositori, lingkungan, dan organisasi

GitHub menawarkan tiga lapisan utama untuk rahasia: rahasia repositori, rahasia lingkungan, dan rahasia organisasi, masing-masing dengan kasus penggunaan dan aturan prioritasnya sendiri. Memahami bagaimana mereka berinteraksi membantu Anda menghindari konflik dan menjaga nilai-nilai sensitif pada tempatnya.

Rahasia tingkat repositori

Rahasia repositori terikat pada satu repo dan tersedia untuk semua alur kerja dalam repositori tersebut. Mereka sempurna untuk nilai spesifik proyek seperti kunci API layanan, kata sandi penerapan, atau token webhook yang hanya digunakan oleh repo tersebut.

Untuk membuat rahasia repositori dari UI, Anda masuk ke repo target, buka “Pengaturan” → “Rahasia dan variabel” → “Tindakan”, lalu klik “Rahasia repositori baru”. Anda memberinya nama huruf besar dengan garis bawah (misalnya PAYMENTS_API_KEY), tempel nilai rahasia, dan simpan; sejak saat itu, alur kerja dapat mengaksesnya sebagai ${{ secrets.PAYMENTS_API_KEY }}.

Setiap orang yang memiliki akses tulis ke repo dapat merujuk rahasia ini dalam alur kerja, sehingga izin pada repositori itu sendiri menjadi bagian dari kisah keamanan Anda. Jika Anda secara asal-asalan memberikan akses tulis kepada banyak pengguna, secara implisit Anda memberi mereka akses untuk menggunakan setiap rahasia repositori dalam otomatisasi.

Rahasia khusus lingkungan

Rahasia lingkungan berada satu tingkat di bawah rahasia repositori dan memungkinkan Anda menentukan nilai yang berbeda per lingkungan seperti dev, staging, atau production. Mereka melekat pada lingkungan bernama dan dapat dilindungi dengan aturan seperti peninjauan yang diperlukan atau pengatur waktu tunggu sebelum pekerjaan dapat dijalankan terhadap mereka.

Anda mengonfigurasinya dengan membuka “Pengaturan” → “Lingkungan”, membuat atau memilih lingkungan, lalu menambahkan rahasia di dalam konfigurasi lingkungan tersebut. Nama rahasia masih menggunakan secrets. konteks (misalnya secrets.PROD_DB_PASSWORD), tetapi nilai tersebut hanya tersedia untuk pekerjaan yang secara eksplisit berjalan di lingkungan tersebut.

Detail utamanya adalah bahwa rahasia lingkungan mengesampingkan rahasia repositori ketika keduanya berbagi nama yang sama. Itu berarti Anda dapat mendefinisikan DB_PASSWORD di tingkat repo untuk penggunaan lokal/pengujian dan kemudian memiliki yang berbeda DB_PASSWORD sebagai rahasia lingkungan untuk produksi yang diutamakan dalam pekerjaan produksi, tanpa mengubah sintaksis alur kerja.

Lingkungan juga mengaktifkan aturan perlindungan seperti "peninjau yang diperlukan" atau "hanya dari cabang tertentu", yang sangat berguna untuk menempatkan pembatas di sekitar akses ke rahasia Anda yang paling sensitif. Misalnya, lingkungan produksi mungkin memerlukan persetujuan dari DevOps sebelum pekerjaan apa pun yang menggunakan rahasianya dapat dijalankan.

Rahasia di seluruh organisasi

Rahasia organisasi dibagikan di beberapa repositori dalam satu organisasi dan ideal untuk kredensial yang digunakan kembali secara luas, seperti webhook Slack bersama atau token API metrik pusat. Mereka mengurangi duplikasi dan membuat rotasi lebih mudah karena Anda memperbarui rahasia satu kali dan semua repo yang mengonsumsi mengambil nilai baru.

Admin membuatnya di bagian “Pengaturan” → “Rahasia dan variabel” → “Tindakan” organisasi, mengklik “Rahasia organisasi baru”, lalu memilih repositori mana yang dapat mengakses rahasia tersebut. Anda dapat mengizinkan semua repo saat ini dan di masa mendatang atau membatasinya secara ketat ke subset tertentu.

Ada rantai prioritas yang mesti Anda ingat: rahasia organisasi < rahasia repositori < rahasia lingkungan saat nama bertabrakan. Dengan kata lain, rahasia lingkungan menang atas rahasia repositori, yang menang atas rahasia organisasi jika semuanya berbagi kunci yang sama.

Bagaimana rahasia berperilaku saat runtime

Setelah didefinisikan, rahasia menjadi tersedia untuk pekerjaan pada saat runtime melalui secrets konteks dan disuntikkan sebagai variabel lingkungan saat direferensikan. Mereka tidak diekspor secara luas ke setiap langkah secara default; Anda secara eksplisit menghubungkannya ke dalam env: blok atau meneruskannya ke tindakan yang mendukung rahasia sebagai masukan.

GitHub juga menyediakan layanan khusus GITHUB_TOKEN per alur kerja yang dijalankan, yang bukan merupakan rahasia yang ditentukan secara manual tetapi berperilaku seperti rahasia dan sering digunakan untuk panggilan API atau operasi repositori. Anda dapat (dan harus) menyetel izin terperinci token ini menggunakan permissions: blok sehingga memiliki cakupan minimum yang dibutuhkan untuk setiap pekerjaan.

Untuk mengurangi paparan yang tidak disengaja, GitHub menutupi nilai apa pun yang cocok dengan rahasia terdaftar di log alur kerja, menggantinya dengan ***. Masking ini dilakukan di sisi runner, dan umumnya berfungsi dengan baik untuk string mentah, tetapi mengasumsikan nilai rahasia yang tepat muncul di keluaran. Jika Anda mengubah rahasia (misalnya, mengodekannya dengan base64 atau menyematkannya dalam JSON terstruktur), masking mungkin gagal menangkapnya.

Karena penyamaran merupakan upaya terbaik dan tidak dijamin secara matematis, Anda harus merancang alur kerja untuk menghindari pencetakan rahasia atau turunannya ke log sama sekali, dan menggunakan perintah penyamaran untuk nilai tambahan yang Anda hasilkan saat runtime. Perlakukan log sebagai sesuatu yang berpotensi terlihat oleh lebih banyak orang daripada yang Anda perkirakan dan asumsikan apa pun yang dicetak dapat bocor.

Penggunaan praktis: merujuk rahasia dalam alur kerja

Sering kali Anda akan menggunakan rahasia dengan memetakannya ke dalam variabel lingkungan dalam langkah atau pekerjaan tertentu, lalu membiarkan skrip atau alat Anda membaca dari lingkungan tersebut. Pola klasiknya terlihat seperti ini:


– nama: Deploy ke API
lingkungan:
API_KEY: ${{ rahasia.PROD_API_KEY }}
lari: |
curl -H “Otorisasi: Pembawa $API_KEY” https://api.example.com/deploy

Anda juga dapat menulis rahasia ke dalam berkas di runner, yang aman selama berkas tersebut tetap berada dalam ruang kerja sementara dari pekerjaan tersebut dan tidak dikomit atau diunggah sebagai artefak. Misalnya, menyimpan kunci SSH:


– nama: Tulis kunci SSH ke file
shell: bash
lingkungan:
KUNCI_SSH: ${{ rahasia.KUNCI_SSH }}
lari: |
gema “$SSH_KEY” > kunci
tombol chmod 600

Dari log Anda hanya akan melihat perintah shell sebenarnya (dengan $SSH_KEY), tetapi bukan nilai rahasia itu sendiri, yang akan dihapus atau disembunyikan. Karena pelari yang dihosting GitHub dihancurkan setelah pekerjaan selesai, berkas sementara itu hilang bersama VM; pada pelari yang dihosting sendiri, Anda harus lebih ketat dalam membersihkannya.

Praktik terbaik keamanan untuk rahasia di GitHub Actions

Sekadar menggunakan UI rahasia saja tidak cukup; Anda memerlukan serangkaian kebiasaan dan perlindungan untuk meminimalkan radius ledakan jika terjadi kesalahan. GitHub menyediakan banyak tombol, tetapi Anda sendiri yang harus memutarnya dengan benar.

Terapkan prinsip hak istimewa paling sedikit

Setiap rahasia dan setiap token seharusnya hanya memberikan izin yang mutlak diperlukan untuk tugas tertentu. Untuk layanan eksternal, buat kredensial khusus dengan izin terbatas (misalnya metrik "hanya penerapan" atau "hanya baca") daripada menggunakan kembali kunci admin penuh.

Prinsip yang sama berlaku untuk built‑in GITHUB_TOKEN; menetapkan izin default ke minimum (seringkali contents: read) dan kemudian menaikkan izin hanya pada pekerjaan tertentu yang membutuhkan lebih banyak. Anda mengonfigurasi ini dengan permissions: bagian pada tingkat alur kerja atau pekerjaan sehingga pekerjaan yang terganggu tidak dapat melakukan penulisan sembarangan secara diam-diam.

Hindari mencetak atau mengodekan rahasia dalam log

Rahasia tidak boleh dikodekan secara permanen dalam file alur kerja atau dicetak dalam teks biasa demi kemudahan penelusuran kesalahan. Jika Anda memiliki nilai sensitif lainnya yang tidak terdaftar sebagai rahasia GitHub (misalnya token yang dibuat saat runtime), Anda masih dapat meminta pelari untuk memperlakukannya sebagai rahasia menggunakan sintaksis perintah:


gema “::tambah-mask::$TOKEN_YANG_DIHASILKAN”

Blob terstruktur seperti JSON, XML atau dokumen YAML besar sangat berbahaya sebagai rahasia karena penyamaran GitHub bergantung pada pencocokan string yang tepat. Jika Anda meletakkan beberapa nilai sensitif di dalam satu string JSON besar dan menggunakannya sebagai satu rahasia, perubahan format sedikit saja dapat menyebabkan penyamaran gagal; sebagai gantinya, tentukan rahasia individual untuk setiap bidang yang sensitif.

Selalu tinjau log saat menguji alur kerja, berikan perhatian khusus pada pesan kesalahan dan jejak tumpukan. Beberapa alat dengan senang hati menggemakan perintah dan bendera ke stderr, yang dapat secara tidak sengaja menyertakan nilai rahasia kecuali Anda secara eksplisit menghindari pola tersebut.

Putar dan audit rahasia secara berkala

Rotasi rahasia memang membosankan tetapi tidak dapat dinegosiasikan jika Anda peduli dengan keamanan; membiarkan kredensial tidak berubah selama bertahun-tahun meningkatkan peluang bagi penyerang. Dasar yang masuk akal adalah merotasikan rahasia produksi yang paling penting setiap bulan, yang berisiko tinggi setiap beberapa bulan, dan yang lainnya setidaknya setiap tiga bulan.

Anda dapat mengotomatiskan sebagian ini menggunakan GitHub REST API untuk rahasia, yang memungkinkan Anda mengambil kunci publik repositori atau organisasi dan mengunggah nilai terenkripsi baru. Hal ini sangat berguna bagi organisasi besar dengan banyak repo yang berbagi akun layanan dan perlu merotasinya dengan cepat sebagai respons terhadap insiden.

Audit sama pentingnya: tinjau secara berkala daftar rahasia yang dikonfigurasi dan hapus yang tidak lagi digunakan, dan gunakan log keamanan/audit GitHub untuk melacak peristiwa seperti org.update_actions_secret. Dengan cara itu Anda tahu siapa yang mengubah apa dan kapan, dan Anda dapat menghubungkan perubahan yang mencurigakan dengan aktivitas lainnya.

Gunakan pemisahan berbasis lingkungan

Rahasia lingkungan adalah salah satu cara termudah untuk memperkuat jaringan Anda, karena rahasia ini memungkinkan Anda mengisolasi nilai yang sangat sensitif (seperti kredensial DB produksi) di balik persetujuan eksplisit. Anda dapat meminta peninjau manusia, membatasi cabang mana yang dapat menerapkan, dan bahkan menambahkan penghitung waktu pendinginan sebelum penerapan dimulai.

Pola umum adalah memiliki staging lingkungan dengan perlindungan ringan dan production lingkungan dengan aturan yang lebih ketat dan rahasia yang terpisah. Alur kerja kemudian menentukan pekerjaan yang menargetkan setiap lingkungan, memastikan bahwa rahasia prod tidak pernah digunakan secara tidak sengaja dalam pekerjaan pengujian.

Pilih konvensi penamaan yang jelas

Penamaan yang baik untuk rahasia akan menyelamatkan Anda dari tebakan yang membuat frustrasi dan kesalahan fatal. Alih-alih nama generik seperti API_KEY, mengkodekan layanan dan lingkungan dalam nama, misalnya STRIPE_PROD_API_KEY or AWS_STAGING_DEPLOY_ROLE_ARN.

Tim yang menangani banyak layanan sering kali mengadopsi pola seperti <SERVICE>_<ENV>_<PURPOSE>. Jadi Anda mungkin memiliki SLACK_PROD_ALERTS_WEBHOOK, GCP_DEV_BUILD_SERVICE_ACCOUNT, dan DB_STAGING_PASSWORD. Hal ini membuat lebih jelas rahasia mana yang harus digunakan pada pekerjaan mana.

Melindungi dari injeksi skrip dan risiko pihak ketiga

Rahasia tidak hanya berisiko akibat kesalahan konfigurasi; tetapi juga menjadi target yang menggoda untuk injeksi skrip dalam alur kerja dan tindakan pihak ketiga yang berbahaya atau yang disusupi. Satu langkah yang rentan dapat mengungkap setiap rahasia yang dapat diakses dalam pekerjaan tersebut jika Anda tidak berhati-hati.

Mengurangi injeksi skrip dalam langkah sebaris

Bila Anda menginterpolasi data yang tidak tepercaya (seperti judul permintaan penarikan, nama cabang, atau komentar masalah) langsung ke skrip shell, Anda membuka pintu terhadap injeksi. Misalnya, judul PR dapat dibuat untuk keluar dari perintah dan menjalankan kode shell acak di runner Anda.

Pendekatan yang paling aman adalah menangani logika kompleks dalam tindakan JavaScript/TypeScript pihak pertama atau yang diaudit dengan baik dan meneruskan nilai yang tidak tepercaya sebagai input alih-alih menyisipkannya dalam shell. Dalam model itu, tindakan menerima string sebagai argumen dan memprosesnya tanpa membuat skrip shell yang dapat dibajak.

Bila Anda terpaksa menggunakan shell sebaris, simpan nilai yang tidak tepercaya terlebih dahulu dalam variabel lingkungan, lalu rujuk variabel tersebut, sebaiknya dalam tanda kutip ganda. Dengan cara itu nilai tersebut diperlakukan sebagai data dan bukan sebagai bagian dari struktur skrip, sehingga upaya penyuntikan menjadi sangat kecil kemungkinannya untuk berhasil.

Sematkan dan tinjau tindakan pihak ketiga

Setiap tindakan pihak ketiga yang Anda tarik ke alur kerja Anda berjalan dengan akses ke lingkungan dan rahasia pekerjaan, jadi Anda harus memperlakukannya sebagai dependensi kode yang memerlukan pengawasan. Tindakan jahat atau yang disusupi dapat membaca rahasia dan mengirimkannya ke penyerang dengan satu panggilan HTTP.

Praktik terbaik adalah menyematkan tindakan dengan SHA komit penuh, bukan hanya tag atau cabang, karena tag dapat dipindahkan atau ditimpa. SHA merujuk pada versi kode yang tepat, sehingga lebih sulit bagi penyerang untuk diam-diam menyuntikkan perilaku baru tanpa Anda memperbarui alur kerja.

Sebelum menggunakan suatu tindakan, teliti kode sumbernya (atau setidaknya tinjauan keamanan) untuk memastikan tindakan tersebut menangani rahasia secara bertanggung jawab dan tidak mencatatnya atau mengirimkannya ke titik akhir yang tidak dikenal. Jika Anda menggunakan tindakan pasar, verifikasi penerbit jika memungkinkan dan andalkan Dependabot untuk mengingatkan Anda tentang kerentanan dan pembaruan.

Pelari yang dihosting vs. pelari yang dihosting sendiri dan paparan rahasia

Tempat alur kerja Anda berjalan memiliki dampak besar pada seberapa aman rahasia ditangani. Pelari yang dihosting GitHub dan pelari yang dihosting sendiri berperilaku sangat berbeda dalam hal isolasi dan persistensi.

Pelari yang dihosting GitHub menjalankan mesin virtual baru untuk setiap pekerjaan, menjalankan alur kerja Anda, lalu menghentikannya. Itu memberi Anda lingkungan yang bersih setiap saat dan memastikan bahwa semua file atau variabel lingkungan (termasuk rahasia yang ditulis ke disk) dihancurkan setelah pekerjaan selesai.

Pelari yang dihosting sendiri, sebaliknya, adalah mesin berumur panjang yang Anda kelola, yang berarti kode apa pun yang memiliki akses ke rahasia berpotensi dapat bertahan atau mengekstraknya melebihi umur satu pekerjaan. Pada repositori publik, runner yang dihosting sendiri sangat berisiko karena kontributor yang tidak tepercaya dapat membuka permintaan penarikan yang memicu alur kerja.

Jika Anda menggunakan runner yang dihosting sendiri, pisahkan runner tersebut per tingkat sensitivitas, batasi repo mana yang dapat menggunakan runner mana, dan bersikaplah paranoid tentang hal lain yang ada di mesin tersebut (kunci SSH, kredensial cloud, akses jaringan ke layanan internal, dan sebagainya). Beberapa organisasi menggunakan runner yang dihosting sendiri “just‑in‑time” (JIT) yang dibuat melalui API untuk satu pekerjaan dan kemudian dimusnahkan, tetapi meskipun demikian Anda harus memastikan pekerjaan tidak berbagi runner yang sama secara tidak terduga.

Menggunakan OpenID Connect (OIDC) alih-alih rahasia cloud yang berumur panjang

Salah satu kemenangan terbesar untuk kebersihan rahasia di GitHub Actions adalah mengganti kunci akses cloud yang berumur panjang dengan kredensial yang berumur pendek melalui OpenID Connect. Alih-alih menyimpan kunci AWS, Azure atau GCP sebagai rahasia, alur kerja Anda meminta token sementara dari penyedia cloud menggunakan GitHub sebagai penyedia identitas.

Alurnya kurang lebih seperti ini: pekerjaan meminta JWT yang ditandatangani dari titik akhir OIDC GitHub, penyedia cloud Anda memvalidasi token tersebut dan menukarnya dengan kredensial jangka pendek, dan alur kerja menggunakan kredensial tersebut selama durasi pekerjaan. Tidak ada rahasia statis yang perlu ada di GitHub.

Misalnya, dengan AWS Anda mengonfigurasi peran IAM yang memercayai penyedia OIDC GitHub dan membatasi repositori/cabang mana yang dapat mengasumsikan peran tersebut. Kemudian dalam alur kerja Anda, Anda menggunakan tindakan seperti aws-actions/configure-aws-credentials dengan izin OIDC diaktifkan untuk memperoleh kredensial dengan cepat.

Pendekatan ini memiliki banyak manfaat: tidak ada yang perlu dirotasi di dalam GitHub, token secara otomatis berumur pendek, akses dibatasi secara sempit, dan Anda mendapatkan log audit lengkap di sisi cloud yang melacak setiap asumsi peran. Untuk lingkungan keamanan tinggi, OIDC harus menjadi default jika didukung.

Perkakas asli dan manajer rahasia eksternal

Rahasia bawaan GitHub sangat bagus untuk banyak skenario, tetapi pada titik tertentu Anda mungkin menginginkan pengelola rahasia yang lebih terpusat dan kaya fitur yang mencakup berbagai platform dan lingkungan. Alat seperti HashiCorp Vault, Infisical atau Doppler sering digunakan bersama GitHub Actions untuk tujuan ini.

Sistem ini dapat menangani rahasia dinamis (misalnya, menghasilkan pengguna basis data jangka pendek), kebijakan kontrol akses tingkat lanjut, log audit terperinci, dan alur kerja rotasi yang melampaui apa yang ditawarkan GitHub saja. GitHub Actions kemudian mengautentikasi ke manajer ini (seringkali melalui OIDC atau metode autentikasi lain), mengambil rahasia yang mereka butuhkan saat runtime, dan menggunakannya tanpa pernah menyimpan kredensial jangka panjang dalam repo.

Ada juga tindakan komunitas dan plugin yang dirancang untuk menarik rahasia dari manajer eksternal langsung ke alur kerja. Saat menggunakannya, saran yang sama berlaku: tinjau sumber tindakan, sematkan ke SHA komit, dan batasi hak istimewa yang diberikan ke token atau peran yang digunakannya untuk mencapai sistem eksternal.

Manajemen rahasia yang aman di GitHub Actions berarti memilih cakupan yang tepat untuk setiap rahasia, menerapkan hak istimewa paling rendah, menggunakan lingkungan dan OIDC jika sesuai, memperlakukan log dan tindakan pihak ketiga sebagai permukaan serangan potensial, dan mengandalkan pengelola rahasia eksternal saat skala atau persyaratan kepatuhan Anda menghendakinya. Dengan praktik tersebut, alur kerja CI/CD Anda dapat tetap fleksibel dan cepat sekaligus secara drastis mengurangi kemungkinan terjadinya token yang salah tempat atau alur kerja yang tidak ditinjau dengan baik yang berubah menjadi insiden besar.

Pos terkait: