Menerapkan prinsip least privilege di GitHub Actions, mulai dari token dengan scope minimal, pembatasan workflow pada branch tertentu, hingga pengamanan secret dan pemindaian keamanan dependensi agar pipeline rilis tidak menjadi celah masuk penyerang.

Di episode 11 workflow rilis sudah dipagari kondisi branch. Sekarang tiba waktunya mengunci pintu-pintu lain. Pipeline rilis adalah bagian paling sensitif di repository: ia bisa membuat tag, menerbitkan package, dan memegang token yang berharga. Satu kesalahan izin dan seluruh ekosistem bisa terbobol lewat jalur yang paling tidak terduga.
Episode ini membahas GitHub Actions security best practices: least privilege dengan token berscope minimal, pembatasan workflow pada branch tertentu, pengelolaan secret, dan pemindaian keamanan dependensi.
Least privilege berarti: beri akses sesedikit mungkin, hanya untuk pekerjaan yang diperlukan, dan hanya selama dibutuhkan. Job lint tidak butuh hak menulis apa pun. Job release butuh menulis tag dan publish package — tetapi tidak butuh akses ke hal lain.
| Token | Asal | Scope | Dipakai untuk |
|---|---|---|---|
GITHUB_TOKEN | otomatis per job, kedaluwarsa | scope repository | checkout, tag, GitHub release |
NPM_TOKEN | GitHub secret | scope publish di registry | publish package ke npm |
GITHUB_TOKEN dibuat otomatis oleh GitHub untuk setiap job dan kedaluwarsa saat workflow selesai — tidak perlu disimpan sebagai secret persisten. Sebaliknya, NPM_TOKEN adalah token nyata yang harus kalian buat dan simpan.
Warning
Bedakan scope kedua token. GITHUB_TOKEN hanya berlaku untuk satu
repository dan ikut kedaluwarsa, sehingga aman di sebagian besar job.
NPM_TOKEN harus dibuat dengan scope publish khusus pada akun yang
memang ditugaskan rilis, bukan token dengan akses penuh ke seluruh akun.
Jangan pernah menampilkan nilai token di log, nama step, atau artifact,
dan jangan pernah meletakkannya di workflow tanpa enkripsi secret.
Sejak awal 2023, repository baru memakai default read-only, tetapi banyak repository lama masih berjalan dengan default yang lebih luas. Selalu deklarasikan permissions secara eksplisit agar perilakunya tidak bergantung pada pengaturan organisasi.
Job non-release cukup contents: read:
name: CI
on:
pull_request:
permissions:
contents: read
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm run lintJob release baru menaikkan scope, dan hanya untuk yang dibutuhkan:
jobs:
release:
runs-on: ubuntu-latest
environment: production
permissions:
contents: write
packages: write
steps:
- uses: actions/checkout@v4
with:
persist-credentials: false
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- name: Semantic Release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
run: npx semantic-releaseDua detail penting: environment: production menempatkan job di bawah environment yang bisa dilindungi peninjau, dan persist-credentials: false membuat token tidak menempel pada git config sehingga langkah berikutnya tidak mewarisi kredensial.
Tip
Gunakan environment untuk rilis produksi. Di Settings > Environments,
konfigurasikan environment production dengan "Required reviewers" — job
release tidak akan berjalan sampai peninjau menyetujui. Secret juga bisa
di-scope per environment sehingga token produksi tidak bocor ke
environment lain.
Batasi trigger di blok on agar workflow hanya pernah berjalan di branch yang dimaksud:
name: Release
on:
push:
branches:
- main
- stagingDengan ini, push ke feature/* atau pull request dari fork tidak akan pernah menjalankan job release. Untuk workflow rilis, ini pertahanan pertama sekaligus paling sederhana.
Warning
Berhati-hatilah dengan pull_request_target. Workflow ini berjalan dengan
konteks branch dasar dan secret repository, sehingga pull request dari
fork bisa mengeksekusi kode dari kontributor asing dengan akses yang
lebih luas. Kalau tidak benar-benar diperlukan, jangan pakai
pull_request_target untuk job yang mengeksekusi kode dari PR.
Aktifkan Dependabot untuk pull request pembaruan otomatis:
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"Lengkapi dengan pemindaian terjadwal di CI:
name: Security Scan
on:
schedule:
- cron: '0 6 * * 1'
permissions:
contents: read
jobs:
audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22
- run: npm ci
- run: npm audit --audit-level=high
codeql:
runs-on: ubuntu-latest
permissions:
security-events: write
steps:
- uses: actions/checkout@v4
- uses: github/codeql-action/init@v3
- uses: github/codeql-action/analyze@v3npm audit menangkap kerentanan pada dependensi langsung dan transitif, CodeQL menangkap bug keamanan pada kode. Perhatikan job CodeQL menaikkan scope security-events: write hanya untuk dirinya sendiri — contoh nyata least privilege dalam satu workflow.
| Kesalahan | Gejala | Solusi |
|---|---|---|
permissions: write-all | Token berhak apa saja | Scope minimal per job |
| Secret bocor ke log | Nilai token terbaca | Jangan echo, pakai env terenkripsi |
pull_request_target untuk rilis | Fork bisa memicu rilis | Batasi branch atau hindari event itu |
Pada episode 12 ini kalian telah:
permissions eksplisit, non-release contents: read, release contents: write dan packages: write.on agar hanya berjalan pada branch yang tepat.environment dan "Required reviewers".npm audit, dan CodeQL, dengan NPM_TOKEN ber-scope publish khusus sebagai aset paling berharga.Di episode 13 kita akan melengkapi pertahanan dengan Protected Branches & PR Policies — branch protection rules, required status checks, dan mandatory reviewers untuk main dan rc. Sampai jumpa di episode 13!