Setiap janji keamanan kriptografi berakar pada sebuah nilai rahasia: kunci yang menandatangani token, secret yang menghubungkan aplikasi dengan layanan lain, atau kredensial basis data. Kekuatan algoritma menjadi tidak berarti apabila nilai itu tersimpan di tempat yang salah atau tidak pernah diganti selama bertahun-tahun. Kegagalan pengelolaan kunci termuat dalam kategori A04:2025 Cryptographic Failures pada OWASP Top 10:2025. Topik ini membahas tempat penyimpanan secret, model token API berbasis HMAC-SHA256, rotasi kunci dengan masa transisi, serta urutan tanggap setelah secret bocor. Setelah mengikuti topik ini, pembaca dapat merancang siklus hidup sebuah secret dari penerbitan hingga penghancurannya.
Tempat secret disimpan#
Secret yang tertanam pada kode sumber adalah tempat penyimpanan yang terburuk, karena nilainya terbawa ke repositori, terbagi kepada seluruh kontributor, dan tetap tersimpan pada riwayat commit. Tingkat yang lebih baik adalah variabel lingkungan, yaitu nilai yang diberikan proses saat dijalankan sehingga tidak masuk kode, meskipun salinannya masih tersebar pada berkas konfigurasi lingkungan dan mudah tercetak saat sesi debugging. Tingkat tertinggi adalah secret manager, yaitu layanan yang menyimpan secret dengan kontrol akses, catatan audit, dan versi, sehingga pencabutan terjadi pada satu tempat. Kerahasiaan kata sandi pengguna tidak termasuk pembahasan topik ini karena penyimpanannya memiliki aturan tersendiri dan dibahas pada topik Penyimpanan Kata Sandi di modul Kegagalan Autentikasi.
Blok berikut mengilustrasikan dua pencarian yang lazim dijalankan saat mengaudit sebuah repositori.
# tidak-dijalankan
# mencari nama variabel secret yang tertinggal pada seluruh isi berkas
grep -rn "API_KEY" .
# menelusuri riwayat: berkas yang sudah dihapus tetap ada pada commit lama
git log --oneline --all -- config/produksi.env
git show 9f3c2a1:config/produksi.envBlok tersebut tidak dijalankan di sandbox karena membutuhkan repositori contoh beserta riwayatnya. Pada repositori nyata, baris pertama menemukan secret yang terlupa tertanam di berkas aktif, sedangkan dua baris berikutnya memperlihatkan inti masalahnya: git menyimpan cuplikan lengkap pada setiap commit, sehingga menghapus berkas pada commit baru tidak menghapus salinannya dari sejarah. Penulisan ulang riwayat pun tidak mengembalikan kerahasiaan, karena salinan repositori sudah tersebar pada komputer kontributor dan nilai itu dapat saja sudah terbaca sebelum dibersihkan.
Token API dan HMAC-SHA256#
Token API yang ditandatangani secara simetris adalah model yang paling sering ditemui pada praktik. HMAC (hash-based message authentication code) menghasilkan tanda tangan dari muatan menggunakan kunci bersama, dengan notasi . Verifikasi menghitung ulang tanda tangan dari muatan yang diterima, lalu membandingkannya dengan tanda tangan yang terlampir. Perbandingannya wajib menggunakan fungsi berwaktu konstan seperti hmac.compare_digest, karena perbandingan string biasa selesai lebih cepat ketika karakter awalnya sudah cocok, sehingga waktu respons membocorkan panjang awalan yang benar. Pola verifikasi yang sama dengan compare_digest juga digunakan pada topik Integritas Webhook dan Callback untuk permintaan pihak lain, sedangkan topik ini menggunakannya untuk token yang diterbitkan sendiri.
Muatan token umumnya membawa kid (key identifier), yaitu penanda kunci yang menandatangani token, sehingga server memilih kunci yang tepat tanpa mencoba satu per satu. Skrip berikut memodelkan alur penuhnya dengan kunci contoh yang jelas fiktif: penerbitan dengan kid v1, rotasi ke kid v2, masa transisi yang menerima keduanya, penolakan setelah masa tenggang, dan kegagalan verifikasi atas muatan yang diubah.
"""Model token API ber-HMAC-SHA256 beserta rotasi kunci dan masa transisinya."""
import hashlib
import hmac
import json
KUNCI = {
"v1": b"kunci-lab-v1-hanya-contoh-0123456789abcdef",
"v2": b"kunci-lab-v2-hanya-contoh-fedcba9876543210",
}
masa_tenggang_v1_berakhir = False # berubah menjadi True setelah rotasi usai
def terbitkan(muatan, kid):
"""Menandatangani muatan dengan kunci kid lalu mengembalikan pasangan token."""
tubuh = json.dumps({"kid": kid, **muatan}, sort_keys=True, separators=(",", ":"))
tanda = hmac.new(KUNCI[kid], tubuh.encode(), hashlib.sha256).hexdigest()
return tubuh, tanda
def verifikasi(tubuh, tanda):
"""Memeriksa token: kunci dipilih dari kid, perbandingan memakai compare_digest."""
data = json.loads(tubuh)
kid = data["kid"]
if kid not in KUNCI:
return "TOLAK", "kid tidak dikenal"
if kid == "v1" and masa_tenggang_v1_berakhir:
return "TOLAK", "masa tenggang kid v1 berakhir"
harapan = hmac.new(KUNCI[kid], tubuh.encode(), hashlib.sha256).hexdigest()
if hmac.compare_digest(tanda, harapan):
return "TERIMA", f"ditandatangani kunci {kid}"
return "TOLAK", "tanda tangan tidak cocok"
def periksa(label, tubuh, tanda):
status, alasan = verifikasi(tubuh, tanda)
print(f" {label:<18} -> {status} ({alasan})")
# 1. Penerbitan: token diterbitkan dengan kid v1.
tubuh_v1, tanda_v1 = terbitkan({"akun": "budi", "peran": "baca"}, "v1")
print("1. token v1 diterbitkan, tanda: " + tanda_v1[:16] + "...")
# 2. Rotasi: kunci v2 ditetapkan, token lama masih diterima pada masa transisi.
tubuh_v2, tanda_v2 = terbitkan({"akun": "budi", "peran": "baca"}, "v2")
print("2. token v2 diterbitkan, tanda: " + tanda_v2[:16] + "...")
print(" masa transisi (v1 dan v2 diterima):")
periksa("token v1", tubuh_v1, tanda_v1)
periksa("token v2", tubuh_v2, tanda_v2)
# 3. Masa tenggang v1 berakhir: hanya token ber-kid v2 yang diterima.
masa_tenggang_v1_berakhir = True
print("3. masa tenggang v1 berakhir:")
periksa("token v1", tubuh_v1, tanda_v1)
periksa("token v2", tubuh_v2, tanda_v2)
# 4. Muatan yang diubah tanpa menandatangani ulang gagal diverifikasi.
tubuh_palsu = tubuh_v2.replace('"baca"', '"tulis"')
print("4. muatan token v2 diubah menjadi peran tulis:")
periksa("token v2 palsu", tubuh_palsu, tanda_v2)1. token v1 diterbitkan, tanda: c1467b4367ac7b28...
2. token v2 diterbitkan, tanda: e7c53c87f5db7b2c...
masa transisi (v1 dan v2 diterima):
token v1 -> TERIMA (ditandatangani kunci v1)
token v2 -> TERIMA (ditandatangani kunci v2)
3. masa tenggang v1 berakhir:
token v1 -> TOLAK (masa tenggang kid v1 berakhir)
token v2 -> TERIMA (ditandatangani kunci v2)
4. muatan token v2 diubah menjadi peran tulis:
token v2 palsu -> TOLAK (tanda tangan tidak cocok)Empat kelompok keluaran memperlihatkan seluruh kontrak token. Perubahan peran dari baca menjadi tulis pada kasus keempat gagal karena tubuh yang berubah membuat tanda tangan lama tidak cocok lagi, dan penolakan itu wajar terjadi pada koneksi mana pun karena HMAC hanya dapat menghasilkan tanda tangan yang benar dengan kuncinya. Struktur berbasis kid inilah yang membuat rotasi dapat dilakukan tanpa mematikan token yang sudah beredar. Primitif kriptografi di balik HMAC didalami lebih jauh pada jalur Cryptography.
Rotasi dengan masa transisi#
Rotasi membatasi kerusakan yang dapat ditimbulkan satu kunci, karena kunci yang digunakan bertahun-tahun hampir pasti telah disalin di lebih banyak tempat daripada yang tercatat. Rotasi mengikuti pola tiga tahap yang terlihat pada skrip sebelumnya: kunci baru ditetapkan, token lama tetap diverifikasi selama masa transisi karena token yang sudah terbit memang memiliki umur, lalu kunci lama ditolak setelah masa tenggangnya berakhir. Diagram berikut memperlihatkan linimasanya.
Dua aturan menjaga pola ini tetap sehat. Masa transisi dinyatakan bertanggal dan pendek, kira-kira selebar masa hidup token terpanjang ditambah sedikit toleransi, sebab masa transisi yang tidak berujung sama saja dengan mempertahankan dua kunci aktif selamanya. Rotasi juga dijadwalkan berkala, misalnya setiap 90 hari, sehingga kebiasaannya terlatih dan tidak baru dipelajari di tengah insiden. Panduan lengkap siklus kunci, dari pembangkitan hingga penghancuran, dirangkum pada Key Management Cheat Sheet milik OWASP.
Tanggap setelah secret bocor#
Penemuan secret di tempat publik mengaktifkan urutan tanggap yang tetap, dan urutannya penting karena setiap langkah menutup celah dari langkah sebelumnya.
- Cabut secret yang bocor segera, karena nilainya dianggap sudah berpindah tangan sejak terlihat di tempat publik.
- Terbitkan penggantinya dan sebarkan ke seluruh layanan yang menggunakannya, sehingga layanan sendiri tidak ikut berhenti.
- Tinjau log pemakaian secret tersebut, mencatat rentetan waktu, alamat sumber, dan endpoint yang digunakan pelaku untuk menetapkan lingkup dampaknya.
- Lacak sumber kebocorannya pada commit, log aplikasi, laporan error, atau tangkapan layar, lalu tutup salurannya.
- Catat kejadian beserta waktu tanggapnya, agar proses berikutnya lebih cepat daripada yang sekarang.
Rotasi tanpa pencabutan menyisakan secret lama tetap berlaku, sedangkan pencabutan tanpa penyiapan pengganti mematikan layanan sendiri, sehingga keduanya berjalan bersamaan dalam urutan di atas. Tinjauan log tahap ketiga sering mengubah kesimpulan awal, karena durasi dan cakupan pemakaian oleh pelaku menentukan apakah kejadian itu berhenti sebagai pergantian secret atau berlanjut sebagai insiden data. Kebiasaan yang menopang tanggap ini adalah pencatatan pemakaian secret sejak hari pertama, karena log yang tidak pernah ada tidak dapat ditinjau saat dibutuhkan. Panduan penyimpanan secret pada aplikasi dan pipeline dirangkum pada Secrets Management Cheat Sheet milik OWASP.
Kesalahan umum#
Empat kekeliruan berikut paling sering ditemukan pada pengelolaan secret.
- Menganggap penghapusan berkas dari commit sebagai pembersihan. Riwayat git masih menyimpan nilainya, sehingga yang harus dicabut adalah nilainya, bukan hanya berkasnya.
- Menggunakan secret produksi pada lingkungan pengembangan. Lingkungan uji menjadi salinan risiko produksi yang paling mudah bocor, sehingga setiap lingkungan memegang secret-nya sendiri.
- Menetapkan masa transisi tanpa tanggal berakhir. Identifier kid
v1tetap diterima selamanya karena tidak ada pemicu penolakannya, dan dua kunci aktif menjadi kondisi permanen. Tanggal berakhirnya ditetapkan sejak rencana rotasi dibuat. - Mencetak seluruh variabel lingkungan saat sesi debugging. Penuangan isi lingkungan ke log adalah penyerahan seluruh secret proses kepada pembaca log, sehingga variabel secret difilter atau disamarkan lebih dahulu.
Ringkasan#
- Secret tidak tertanam pada kode: variabel lingkungan memisahkan nilai dari sumber, dan secret manager menambahkan audit serta pencabutan terpusat.
- Token HMAC-SHA256 diverifikasi dengan menghitung ulang tandanya dan membandingkannya melalui
compare_digestyang berwaktu konstan. - Rotasi menggunakan kid dan masa transisi bertanggal:
v1danv2sama-sama diterima, laluv1ditolak setelah masa tenggangnya berakhir. - Tanggap kebocoran berurutan: cabut, rotasi, tinjau log pemakaian, tutup sumber kebocorannya, lalu catat kejadiannya.
Referensi#
- Key Management Cheat Sheet — OWASP — Panduan siklus hidup kunci kriptografi, dari pembangkitan hingga penghancuran.
- Secrets Management Cheat Sheet — OWASP — Panduan penyimpanan secret pada aplikasi beserta praktik rotasinya.
- A04:2025 Cryptographic Failures — OWASP Top 10 — Kategori yang menaungi kegagalan pengelolaan kunci dan secret.