Episode ini membahas pengelolaan data sensitif di GitHub Actions: cara menyimpan dan memakai secrets, pengaburan nilai di log, mencegah script injection, serta menerapkan prinsip least privilege pada GITHUB_TOKEN.

Sejak episode 6 kita membangun pipeline yang makin kompleks: job berurutan, matrix test, artifacts, dan caching. Semakin matang pipeline-nya, semakin banyak hal berharga yang diaksesnya — dan justru di situlah keamanan bermain. Bayangkan API key produksi bocor ke log publik sebuah workflow: dalam hitungan menit akun kalian bisa diambil alih, dan tagihan cloud melonjak tanpa bisa dihentikan.
Episodes berikutnya (9 dan 10) adalah fase keamanan seri ini. Pada episode 9 kita membahas pengelolaan data sensitif: menyimpan secrets dengan benar, memastikan nilainya tidak pernah tampil di log, mencegah serangan script injection dari konten yang tidak tepercaya, dan memangkas hak akses GITHUB_TOKEN seminimal mungkin.
Secrets adalah nilai terenkripsi yang disimpan GitHub dan hanya bisa dibaca oleh workflow lewat context secrets. Begitu sebuah secret disimpan, kalian tidak akan pernah bisa melihat isinya lagi — hanya bisa menimpanya. Ini menjawab pertanyaan kunci: kredensial tidak pernah menjadi bagian dari kode, commit, atau riwayat repository.
Ada tiga tingkat cakupan secret:
| Cakupan | Berlaku untuk | Catatan |
|---|---|---|
| Repository | Satu repository | Paling umum, mudah dikelola per-proyek |
| Environment | Satu environment (misal production) | Dipakai bersama protection rules |
| Organization | Semua repository terpilih dalam organisasi | Untuk key bersama, misalnya registry |
Environment secrets punya perilaku khusus: nilainya menimpa repository secrets dengan nama sama, dan hanya tersedia di job yang mendeklarasikan environment. Inilah cara memisahkan token staging dari token produksi dalam satu repository.
Akses secret lewat context secrets, biasanya dipindahkan dulu ke variabel env agar mudah dipakai di beberapa step:
jobs:
deploy:
runs-on: ubuntu-latest
env:
API_TOKEN: ${{ secrets.API_TOKEN }}
steps:
- uses: actions/checkout@v4
- run: ./deploy.shKabar baik: GitHub otomatis mengaburkan (masking) nilai secret yang muncul di log — baik itu output step, error, maupun traceback. Yang tertulis hanya ***. Namun masking otomatis hanya berlaku untuk nilai yang memang disimpan sebagai secret. Jika sebuah nilai sensitif dihasilkan saat runtime (misalnya token sementara dari respon API), kalian harus mengaburkannya sendiri dengan command ::add-mask:::
TOKEN_LAIN=$(curl -s https://api.example.com/token)
echo "::add-mask::$TOKEN_LAIN"Setelah perintah kedua, nilai di TOKEN_LAIN ikut di-mask sepanjang step tersebut — dan di semua output selanjutnya. Ini garis pertahanan terakhir jika nilai rahasia nyasar ke log.
Ini salah satu kerentanan paling serius di GitHub Actions, dan sering tidak disadari. Perhatikan pola ini:
- name: Cetak judul issue
run: echo "${{ github.event.issue.title }}"Apa masalahnya? Nilai github.event.issue.title adalah konten yang bisa dikendalikan orang lain — siapa pun yang membuat issue bisa menulis "; rm -rf /; # sebagai judul. Karena nilai itu diinterpolasi langsung ke script shell sebelum dieksekusi, penyerang telah menyuntikkan perintah mereka ke dalam runner kalian. Inilah yang disebut script injection.
Solusinya sederhana dan wajib: jangan pernah meletakkan context yang tidak tepercaya langsung ke run. Pindahkan dulu ke variabel env, karena penugasan env tidak dieksekusi sebagai shell:
- name: Cetak judul issue dengan aman
env:
JUDUL: ${{ github.event.issue.title }}
run: echo "$JUDUL"Sekarang isi judul diperlakukan sebagai data, bukan perintah — karakter $, backtick, dan titik koma tidak akan dieksekusi oleh shell. Prinsip yang sama berlaku untuk konten PR, body issue, nama branch, dan komentar: semuanya tidak tepercaya.
Warning
Aturan emas: content dari event GitHub tidak pernah tepercaya. Jangan interpolasikan github.event, github.head_ref, atau nilai hasil parsing input user langsung ke run tanpa melewati env. Perintah yang menyertakan secret juga jangan pernah menulis nilai secret langsung ke run untuk "pengujian" — pindahkan ke env dan baca variabelnya. Lebih jauh, jangan jalankan self-hosted runner untuk repository publik tanpa perlindungan ekstra, karena siapa pun bisa memicu workflow di atas mesin kalian.
Setiap workflow berjalan memakai GITHUB_TOKEN — token otomatis yang berperilaku seperti akun github-actions. Bila tidak dibatasi, token ini memiliki akses cukup luas ke repository. Prinsip least privilege menuntut: berikan hanya izin yang benar-benar dibutuhkan job tersebut.
name: CI
on: [push]
permissions:
contents: read
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm run lintWorkflow ini hanya butuh membaca kode — jadi contents: read saja. Workflow yang mem-push ke GHCR butuh packages: write, workflow yang membuat release butuh contents: write. Pendekatan terbaik: mulai dari permissions: {} (tidak ada izin) lalu naikkan per job yang benar-benar butuh.
| Kesalahan | Gejala | Solusi |
|---|---|---|
Menulis secret langsung di run | Nilai rahasia bisa bocor di log | Pindahkan ke env, baca variabelnya |
| Context user diinterpolasi ke shell | Runner bisa dieksekusi penyerang | Selalu lewati env terlebih dahulu |
| Token dengan izin penuh di semua job | Eksposur tidak perlu | Tetapkan permissions per workflow/job |
| Nilai runtime sensitif tanpa mask | Bocor lewat output step | Tambahkan ::add-mask:: |
| Secret di-commit ke repository | Bocor permanen di riwayat git | Putar nilai, simpan sebagai secret |
Keamanan pipeline adalah tanggung jawab yang dimulai dari disiplin kecil:
secrets; pilih cakupan repository, environment, atau organization sesuai kebutuhan.::add-mask:: untuk nilai runtime.run — selalu lewati variabel env.GITHUB_TOKEN seminimal mungkin lewat key permissions.Di episode 10 selanjutnya, kita angkat satu tingkat lagi: Passwordless Cloud Authentication Menggunakan OIDC — menghapus kredensial jangka panjang dari secrets sama sekali, dan mengautentikasi ke AWS, GCP, serta Azure dengan token yang berumur pendek. Ini lompatan keamanan terbesar yang bisa kalian lakukan untuk pipeline!