Keamanan Aplikasi & Data
Bikin App yang Aman, Siap Dijual & Tahan Serangan. Sebuah manual praktik untuk pengembang, arsitek, dan pemilik produk — dari nol sampai siap audit.
Prakata
Setiap baris kode yang Anda tulis adalah janji. Janji bahwa nama, kata sandi, riwayat transaksi, dan rahasia kecil pengguna akan Anda jaga. Ketika aplikasi bocor, yang runtuh bukan sekadar server — melainkan kepercayaan, dan kepercayaan tidak punya tombol "restore from backup".
Buku ini lahir dari sebuah kegelisahan sederhana. Terlalu banyak aplikasi yang jalan tetapi tidak aman. Fitur lengkap, tampilan mulus, demo memukau — lalu jebol di minggu pertama karena satu kolom yang tidak divalidasi, satu password yang disimpan sebagai teks polos, atau satu kunci API yang lupa dicabut dari repositori publik. Keamanan sering diperlakukan sebagai lapisan cat yang dioleskan di akhir, padahal ia adalah rangka bangunan.
Saya menulis buku ini untuk semua level. Jika Anda baru mulai, setiap bab dimulai dari intuisi dan analogi sebelum masuk ke kode. Jika Anda sudah berpengalaman, ada tabel keputusan, potongan kode siap-salin, dan daftar periksa yang bisa langsung Anda tempel ke pipeline. Dan karena zaman sudah berubah, buku ini juga ditulis agar bisa dibaca oleh AI: judul terstruktur, langkah eksplisit, dan satu bab khusus tentang cara asisten AI sebaiknya memakai isi buku ini.
Keamanan adalah sebuah proses, bukan sebuah produk.
Bruce Schneier, kriptografer
Tidak ada aplikasi yang 100% aman. Tujuan kita bukan kesempurnaan mustahil, melainkan menaikkan biaya serangan setinggi mungkin sambil menurunkan dampak bila serangan berhasil. Itulah inti dari pertahanan berlapis. Selamat membaca, dan selamat membangun sesuatu yang layak dipercaya.
— Galih Prasetyo, 2026
Cara Membaca Buku Ini
Buku ini terbagi menjadi 16 bab yang bisa dibaca berurutan atau melompat sesuai kebutuhan. Namun ada urutan logis yang saya sarankan.
Tiga cara memakai buku ini
- Membaca lurus. Jika ini kali pertama Anda serius soal keamanan, baca dari Bab 1 sampai 16. Setiap bab menumpuk di atas bab sebelumnya.
- Mode rujukan. Punya masalah spesifik (misalnya "bagaimana simpan password"?) Loncat ke bab terkait lewat Daftar Isi. Setiap bab berdiri cukup mandiri.
- Mode audit. Gunakan Alat Audit Keamanan interaktif dan Checklist Produksi untuk menilai aplikasi Anda sekarang, lalu baca bab yang skornya rendah.
Contoh kode memakai Node.js/JavaScript, SQL, dan shell sebagai bahasa lingua-franca karena paling mudah dibaca lintas-ekosistem. Prinsipnya berlaku universal — terjemahkan ke Python, Go, PHP, Java, atau bahasa apa pun yang Anda pakai. Setiap blok kode punya tombol Salin.
Peta perjalanan
| Bagian | Bab | Yang Anda dapat |
|---|---|---|
| Fondasi | 1–2 | Kenapa keamanan wajib, dan cara berpikir seperti penyerang (model ancaman). |
| Kripto & Kredensial | 3–5 | Enkripsi, hashing password, dan mengelola secret tanpa bocor. |
| Identitas & Akses | 6–8 | Autentikasi, otorisasi/RBAC, serta perlindungan data pribadi & UU PDP. |
| Pertahanan Aplikasi | 9–13 | OWASP Top 10, validasi input, rate limit, logging, dan rantai pasok. |
| Operasi & Penutup | 14–16 | Incident response, cara dibaca AI, FAQ, glosarium, checklist. |
Notasi & Simbol
Agar cepat dipindai (oleh manusia maupun AI), buku ini memakai kotak-kotak penanda yang konsisten. Inilah artinya.
Praktik berbahaya atau anti-pola yang harus Anda hindari. Jika Anda melihat ini di kode Anda, perbaiki segera.
Ada nuansa, jebakan umum, atau trade-off yang perlu dipertimbangkan sebelum menerapkan.
Rekomendasi yang bisa Anda jadikan default. Aman untuk disalin.
Konteks tambahan, definisi, atau penjelasan "kenapa begini".
Simbol tingkat prioritas dalam checklist: [P0] wajib sebelum rilis, [P1] penting, [P2] peningkatan. Bobot pada Alat Audit ditulis ×N (semakin besar N, semakin kritikal).
Pendahuluan: Kenapa Keamanan Wajib untuk App yang Dijual
Bayangkan Anda baru saja menutup penjualan pertama. Pelanggan menandatangani kontrak, memasukkan data karyawan mereka ke aplikasi Anda, dan mulai bergantung padanya untuk operasional harian. Tiga bulan kemudian, sebuah email masuk: "Data kami muncul di forum internet. Bagaimana ini bisa terjadi?" Momen itu — bukan momen peluncuran — adalah ujian sesungguhnya dari perangkat lunak Anda.
Ada perbedaan mendasar antara aplikasi hobi dan aplikasi yang dijual. Aplikasi hobi hanya menanggung risiko diri sendiri. Aplikasi yang dijual menanggung risiko orang lain — dan itu memikul kewajiban hukum, finansial, dan moral. Ketika uang berpindah tangan, ekspektasi keamanan naik drastis, entah tertulis di kontrak atau tidak.
Keamanan sebagai fitur yang tak terlihat
Pengguna tidak pernah memuji Anda karena data mereka aman — itu dianggap sudah semestinya. Tetapi mereka akan meninggalkan Anda dalam sekejap ketika data itu bocor. Keamanan adalah fitur yang hanya terlihat ketika ia gagal. Inilah paradoks yang membuatnya sering diabaikan: karena tidak menghasilkan "wow" di demo, ia mudah dikorbankan demi tenggat.
Padahal secara bisnis, keamanan justru membuka penjualan. Perusahaan menengah dan besar akan menanyakan: apakah data terenkripsi? Apakah ada audit trail? Bagaimana Anda menangani insiden? Tanpa jawaban meyakinkan, Anda tersingkir dari tender bahkan sebelum bicara harga. Keamanan yang baik adalah kompetitif.
Hanya ada dua jenis perusahaan: yang sudah diretas, dan yang belum tahu bahwa mereka sudah diretas.
Robert S. Mueller III, mantan Direktur FBI (RSA Conference, 2012)
Biaya sebuah kebocoran
Mari kita jujur soal angka. Sebuah insiden kebocoran data bukan hanya soal "memperbaiki bug". Biayanya berlapis:
| Jenis Biaya | Contoh Konkret |
|---|---|
| Langsung | Investigasi forensik, perbaikan darurat, lembur tim, konsultan keamanan. |
| Regulasi | Denda UU PDP (Indonesia), notifikasi wajib ke otoritas & korban dalam tenggat. |
| Reputasi | Pelanggan berhenti berlangganan, calon pembeli mundur, liputan media negatif. |
| Hukum | Gugatan class action, klausul ganti rugi di kontrak korporat. |
| Operasional | Layanan down saat penanganan, kehilangan momentum produk, moral tim turun. |
Bandingkan itu dengan biaya mencegah: beberapa hari kerja untuk hashing yang benar, validasi input, dan konfigurasi TLS. Rasio untung-rugi pencegahan hampir selalu berpihak pada Anda. Keamanan adalah asuransi termurah yang bisa Anda beli dengan disiplin, bukan uang.
Tiga pilar yang harus Anda ingat
Seluruh buku ini pada dasarnya melayani tiga tujuan klasik yang disingkat CIA — bukan agen rahasia, tapi tiga sifat data yang harus dijaga:
- Confidentiality (Kerahasiaan) — hanya pihak berhak yang bisa membaca data. Dilanggar oleh: kebocoran database, akses tak sah, enkripsi lemah.
- Integrity (Integritas) — data tidak diubah tanpa izin. Dilanggar oleh: manipulasi transaksi, tampering, injeksi.
- Availability (Ketersediaan) — layanan tetap hidup saat dibutuhkan. Dilanggar oleh: DDoS, ransomware, kegagalan backup.
Pola pikir: pertahanan berlapis
Prinsip terpenting yang akan berulang di seluruh buku ini adalah defense in depth — pertahanan berlapis. Jangan pernah bergantung pada satu kontrol. Firewall bisa salah konfigurasi, validasi bisa terlewat, pustaka bisa punya celah. Dengan berlapis, kegagalan satu lapisan tidak langsung menjadi bencana.
Asumsikan setiap lapisan bisa gagal. Rancang agar kegagalan satu kontrol tidak berarti kompromi total. Tambahkan prinsip least privilege (beri hak seminimal mungkin) dan secure by default (default-nya aman, bukan default-nya terbuka).
Sepuluh prinsip keamanan abadi
Teknologi berubah setiap tahun, tetapi prinsip di bawah ini bertahan puluhan tahun. Jadikan ia lensa untuk menilai keputusan apa pun — bila sebuah desain melanggar salah satunya, berhentilah dan pikirkan ulang.
| # | Prinsip | Artinya dalam praktik |
|---|---|---|
| 1 | Least Privilege | Beri hak seminimal yang diperlukan, tidak lebih. Cabut yang tak dipakai. |
| 2 | Defense in Depth | Banyak lapisan; satu gagal, yang lain menahan. |
| 3 | Fail Securely | Saat error, default ke menolak akses — bukan membukanya. |
| 4 | Secure by Default | Konfigurasi bawaan harus aman tanpa perlu diutak-atik. |
| 5 | Minimize Attack Surface | Kurangi fitur, port, endpoint, dan data yang terekspos. |
| 6 | Don't Trust Input | Validasi semua yang datang dari luar batas kepercayaan. |
| 7 | Complete Mediation | Cek otorisasi pada setiap akses, bukan sekali di awal. |
| 8 | Separation of Duties | Pisahkan peran kritis agar tak ada satu titik kuasa mutlak. |
| 9 | Keep It Simple | Kompleksitas melahirkan celah; kesederhanaan bisa diaudit. |
| 10 | Assume Breach | Rancang untuk membatasi dampak ketika — bukan jika — jebol. |
Ada dua cara merancang sistem: begitu sederhana sehingga jelas tak ada kekurangan, atau begitu rumit sehingga tak ada kekurangan yang jelas.
C.A.R. Hoare, ilmuwan komputer (kuliah Turing Award, 1980)
Sebelum kita menyelam ke bab-bab teknis, mari lakukan pemeriksaan cepat. Alat berikut membantu Anda memetakan posisi keamanan aplikasi Anda saat ini. Centang apa yang sudah Anda lakukan; skor per kategori dan verdict akan muncul secara langsung. Ini akan menjadi peta bacaan Anda: kategori berskor rendah menunjukkan bab mana yang paling mendesak untuk Anda baca.
◆ Alat: Audit Keamanan Interaktif
Centang setiap kontrol yang sudah benar-benar Anda terapkan (bukan yang "rencananya"). Setiap butir punya bobot sesuai tingkat kritikalitasnya. Gunakan tombol Contoh untuk melihat simulasi, dan Reset untuk mengosongkan.
Security Posture Audit
Ambang verdict: <50% Rentan · 50–79% Cukup · ≥80% Aman. Semua perhitungan berjalan lokal di browser Anda; tidak ada data yang dikirim ke mana pun.
Model Ancaman: Berpikir Seperti Penyerang
Anda tidak bisa mempertahankan sesuatu yang tidak Anda pahami cara menyerangnya. Model ancaman (threat modeling) adalah latihan terstruktur untuk bertanya: apa yang berharga, siapa yang mengincarnya, lewat mana mereka masuk, dan apa yang terjadi bila mereka berhasil. Ini adalah bab paling murah dan paling berdampak dalam buku ini, karena hanya butuh papan tulis dan kejujuran.
Empat pertanyaan inti
Kerangka Shostack menyederhanakan threat modeling menjadi empat pertanyaan yang bisa Anda tanyakan pada tim mana pun:
- Apa yang sedang kita bangun? Gambarkan sistem: komponen, aliran data, batas kepercayaan (trust boundary).
- Apa yang bisa salah? Enumerasi ancaman terhadap tiap komponen dan aliran.
- Apa yang akan kita lakukan soal itu? Tentukan mitigasi untuk ancaman yang penting.
- Apakah kita sudah melakukannya dengan baik? Validasi dan ulangi secara berkala.
Aset, aktor, dan permukaan serangan
Mulai dengan mendaftar tiga hal. Aset: apa yang berharga (kredensial, data pribadi, kunci, dana, reputasi). Aktor ancaman: siapa yang mungkin menyerang. Permukaan serangan (attack surface): setiap titik di mana data masuk atau keluar sistem — form, API, upload file, webhook, integrasi pihak ketiga.
| Aktor Ancaman | Motivasi | Kemampuan |
|---|---|---|
| Skrip otomatis / bot | Cari korban acak, spam, kredensial dumping | Rendah, tapi masif & terus-menerus |
| Penyerang oportunistik | Uang cepat, ransomware, penjualan data | Menengah, memakai exploit publik |
| Orang dalam (insider) | Dendam, keuntungan, kelalaian | Akses sah — sangat berbahaya |
| Pesaing / mata-mata industri | Rahasia dagang, sabotase | Menengah–tinggi, bertarget |
| Aktor negara (APT) | Spionase, disrupsi | Tinggi, sabar, sumber daya besar |
Statistik industri konsisten menunjukkan sebagian besar insiden melibatkan faktor internal — entah jahat, entah lalai. Prinsip least privilege dan audit trail (Bab 12) adalah pertahanan utama Anda di sini, bukan firewall.
STRIDE: taksonomi ancaman
STRIDE adalah akronim dari Microsoft untuk mengklasifikasikan enam jenis ancaman. Gunakan sebagai daftar periksa untuk setiap komponen sistem.
| Huruf | Ancaman | Melanggar | Contoh |
|---|---|---|---|
| S | Spoofing (pemalsuan identitas) | Autentikasi | Login sebagai orang lain, sesi dicuri |
| T | Tampering (perusakan data) | Integritas | Ubah harga di keranjang, manipulasi request |
| R | Repudiation (penyangkalan) | Non-repudiation | "Bukan saya yang transfer" tanpa jejak |
| I | Information Disclosure | Kerahasiaan | Kebocoran data, error yang bocorkan detail |
| D | Denial of Service | Ketersediaan | Banjir request, query mahal |
| E | Elevation of Privilege | Otorisasi | User biasa jadi admin |
Contoh nyata: memodelkan endpoint login
Mari terapkan STRIDE pada satu endpoint POST /login. Perhatikan betapa banyak ancaman muncul dari satu titik masuk sederhana.
# Endpoint: POST /login (email + password) # Aset: kredensial pengguna, sesi, akun Spoofing -> credential stuffing, brute force Mitigasi: rate limit, MFA, cek password bocor Tampering -> ubah payload, mass assignment role=admin Mitigasi: validasi allowlist field, abaikan field tak dikenal Repudiation -> tidak ada bukti percobaan login Mitigasi: audit log (ip, ua, waktu, hasil) Info Disclosure -> pesan "email tidak ada" bocorkan akun valid Mitigasi: pesan error generik "email/password salah" DoS -> hash password mahal dipakai untuk DoS Mitigasi: rate limit sebelum hashing, captcha adaptif Elevation -> respons login sertakan token beroverprivilege Mitigasi: scope minimal, klaim diverifikasi server-side
Kompleksitas adalah musuh terburuk keamanan. Sistem yang rumit lebih sulit dipahami, lebih sulit dianalisis, dan lebih mudah dieksploitasi.
Bruce Schneier
Diagram aliran data & batas kepercayaan
Gambar sederhana lebih berharga daripada dokumen 20 halaman. Petakan komponen dan tarik garis merah di setiap batas kepercayaan — tempat data berpindah dari zona yang kurang tepercaya ke yang lebih tepercaya. Di garis merah itulah validasi dan otorisasi harus terjadi.
Luangkan satu jam bersama tim sebelum menulis fitur besar. Gambar sistemnya, jalankan STRIDE, catat mitigasi. Model ancaman bukan dokumen sekali jadi — perbarui setiap kali arsitektur berubah. Investasi kecil ini mencegah kelas kesalahan yang mahal.
Enkripsi: At-Rest, In-Transit, dan Kunci
Enkripsi mengubah data yang bisa dibaca menjadi sandi yang tak berarti tanpa kunci yang benar. Ia adalah lapisan terakhir yang melindungi Anda: bahkan bila penyerang berhasil mencuri hard disk atau menyadap jaringan, data terenkripsi tetap tak berguna bagi mereka. Namun enkripsi hanya sekuat pengelolaan kuncinya — dan di situlah kebanyakan orang tersandung.
Kriptografi biasanya di-bypass, bukan ditembus.
Adi Shamir, salah satu penemu algoritma RSA
Kalimat Shamir di atas adalah pelajaran terpenting bab ini. Penyerang jarang memecahkan AES-256 secara matematis — itu praktis mustahil. Mereka mencari kunci yang tersimpan sembarangan, konfigurasi TLS yang salah, atau data yang lupa dienkripsi. Fokus Anda bukan memilih algoritma paling eksotis, melainkan menerapkan yang standar dengan benar.
Jangan pernah membuat algoritma kripto sendiri. Gunakan pustaka standar yang telah diaudit (libsodium, Web Crypto API, penyedia KMS). Kriptografi buatan sendiri hampir pasti punya celah yang tak Anda sadari. "Roll your own crypto" adalah anti-pola nomor satu.
Dua keadaan data
Data hidup di dua keadaan yang butuh perlindungan berbeda:
- In-transit — sedang berpindah lewat jaringan (browser ke server, server ke database). Dilindungi oleh TLS.
- At-rest — sedang tersimpan (di database, file, backup, cache). Dilindungi oleh enkripsi penyimpanan.
(Ada keadaan ketiga, in-use — data di memori saat diproses — yang dilindungi teknik lanjutan seperti enkripsi homomorfik dan enclave. Untuk mayoritas aplikasi, fokuslah pada dua yang pertama dulu.)
In-transit: TLS yang benar
TLS (dulu SSL) mengenkripsi lalu lintas jaringan dan memverifikasi identitas server lewat sertifikat. Ini tidak opsional untuk aplikasi apa pun yang menyentuh internet.
[P0] Paksa TLS 1.2 atau 1.3, nonaktifkan protokol lama (SSLv3, TLS 1.0/1.1). [P0] Redirect semua HTTP ke HTTPS. [P0] Aktifkan HSTS agar browser menolak koneksi tak-terenkripsi. [P1] Gunakan sertifikat otomatis (Let's Encrypt) agar tak pernah kedaluwarsa diam-diam.
# Konfigurasi TLS modern + HSTS + redirect server { listen 80; server_name app.contoh.id; return 301 https://$host$request_uri; # paksa HTTPS } server { listen 443 ssl http2; server_name app.contoh.id; ssl_protocols TLSv1.2 TLSv1.3; # tolak yang lama ssl_ciphers HIGH:!aNULL:!MD5; ssl_prefer_server_ciphers on; # HSTS: 2 tahun, termasuk subdomain add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always; }
At-rest: mengenkripsi penyimpanan
Ada dua level. Enkripsi seluruh disk/volume (mis. fitur bawaan cloud) melindungi dari pencurian fisik disk, tetapi tidak dari aplikasi yang dibobol — karena bagi aplikasi, data terlihat normal. Enkripsi level aplikasi/kolom mengenkripsi field sensitif tertentu (NIK, nomor rekening) sebelum masuk database, sehingga bahkan admin database tidak bisa membacanya.
Jangan enkripsi kolom yang perlu di-query (WHERE, JOIN, sort) tanpa strategi khusus — hasilnya jadi acak dan tak bisa dicari. Enkripsi kolom cocok untuk data yang disimpan dan ditampilkan utuh (nomor kartu, dokumen), bukan untuk yang harus difilter di database.
AEAD: enkripsi yang benar di 2026
Gunakan mode AEAD (Authenticated Encryption with Associated Data) seperti AES-256-GCM atau XChaCha20-Poly1305. AEAD mengenkripsi dan mendeteksi kalau ciphertext diubah — melindungi kerahasiaan sekaligus integritas sekaligus. Setiap operasi enkripsi wajib memakai nonce/IV yang unik.
const crypto = require('crypto'); // key: 32 byte dari KMS/vault — JANGAN hardcode function encrypt(plaintext, key) { const iv = crypto.randomBytes(12); // nonce unik per pesan const cipher = crypto.createCipheriv('aes-256-gcm', key, iv); const enc = Buffer.concat([cipher.update(plaintext, 'utf8'), cipher.final()]); const tag = cipher.getAuthTag(); // tag integritas return Buffer.concat([iv, tag, enc]).toString('base64'); } function decrypt(blob, key) { const raw = Buffer.from(blob, 'base64'); const iv = raw.subarray(0, 12), tag = raw.subarray(12, 28), data = raw.subarray(28); const d = crypto.createDecipheriv('aes-256-gcm', key, iv); d.setAuthTag(tag); // gagal jika diubah return Buffer.concat([d.update(data), d.final()]).toString('utf8'); }
Manajemen kunci: bagian yang benar-benar sulit
Kunci enkripsi adalah "kunci dari segala kunci". Bila ia bocor, seluruh enkripsi runtuh. Bila ia hilang, data Anda hilang selamanya. Prinsip-prinsip berikut wajib:
| Prinsip | Penjelasan |
|---|---|
| Pisahkan kunci dari data | Kunci tidak boleh tinggal di tempat yang sama dengan data terenkripsi. Simpan di KMS/HSM/vault. |
| Envelope encryption | Data dienkripsi dengan Data Key; Data Key dienkripsi dengan Master Key di KMS. Rotasi jadi mudah. |
| Rotasi berkala | Ganti kunci secara terjadwal dan setelah insiden. Simpan versi kunci agar data lama tetap terbaca. |
| Least privilege | Hanya layanan yang perlu yang boleh memakai kunci. Catat setiap penggunaan kunci (audit). |
| Backup kunci | Kehilangan kunci = kehilangan data. Backup terpisah & teruji, dengan kontrol akses ketat. |
Untuk produksi serius, gunakan layanan kunci terkelola (AWS KMS, Google Cloud KMS, Azure Key Vault) atau HashiCorp Vault. File .env boleh untuk konfigurasi non-kritis di tahap awal, tetapi kunci enkripsi utama sebaiknya tidak pernah hidup sebagai teks polos di disk. Lihat Bab 5 untuk detail manajemen secrets.
Hashing bukan enkripsi
Kesalahpahaman paling umum: mengira password perlu "dienkripsi". Salah. Enkripsi bisa dibalik (ada dekripsi); password tidak boleh bisa dibalik. Untuk password, kita memakai hashing satu arah — topik Bab 4 berikutnya. Ingat perbedaan ini baik-baik: enkripsi untuk data yang perlu dibaca kembali, hashing untuk verifikasi tanpa menyimpan aslinya.
Hashing Password: Argon2, bcrypt, Salt & Pepper
Jika hanya satu bab yang Anda baca, jadikan ini bab itu. Cara Anda menyimpan password adalah pembeda paling telak antara aplikasi amatir dan profesional. Kabar baiknya: melakukannya dengan benar hanya butuh beberapa baris kode dan satu pustaka yang tepat.
Jangan simpan password sebagai teks polos. Jangan pakai MD5 atau SHA-1/SHA-256 polos untuk password. Jangan pakai enkripsi dua arah. Jangan buat skema hashing sendiri. Semua ini bisa dipecahkan dalam hitungan detik oleh GPU modern atau tabel rainbow.
Kenapa hash cepat justru buruk
SHA-256 dirancang untuk cepat — itu bagus untuk verifikasi file, buruk untuk password. GPU modern bisa menghitung miliaran hash SHA-256 per detik. Artinya, jika database Anda bocor, penyerang bisa menebak password lemah hampir seketika. Untuk password kita justru butuh algoritma yang sengaja lambat dan haus memori, sehingga menebak jadi mahal.
Password itu seperti pakaian dalam: jangan biarkan orang melihatnya, ganti secara berkala, dan jangan berbagi dengan orang asing.
Pepatah keamanan (dipopulerkan Chris Pirillo)
Algoritma pilihan
| Algoritma | Status | Kapan dipakai |
|---|---|---|
| Argon2id | ★ Rekomendasi utama | Pemenang Password Hashing Competition. Tahan GPU & ASIC. Pilihan pertama untuk proyek baru. |
| scrypt | Baik | Haus memori, alternatif solid bila Argon2 tak tersedia. |
| bcrypt | Baik, teruji waktu | Sangat matang & tersedia di mana-mana. Batas 72 byte input. Aman bila Argon2 belum ada. |
| PBKDF2 | Boleh (jika wajib) | Sering diwajibkan standar kepatuhan (FIPS). Pakai iterasi tinggi. |
| MD5, SHA-* polos | ✗ Jangan | Terlalu cepat. Tidak pernah untuk password. |
Salt: melumpuhkan rainbow table
Salt adalah nilai acak unik untuk setiap password, digabung sebelum hashing. Dampaknya besar: dua pengguna dengan password sama akan punya hash berbeda, dan tabel rainbow (hash yang sudah dihitung sebelumnya) jadi tak berguna. Kabar baik: pustaka modern seperti Argon2 dan bcrypt menghasilkan dan menyimpan salt otomatis di dalam string hash. Anda tak perlu mengelolanya manual.
const argon2 = require('argon2'); // Saat registrasi / ganti password async function hashPassword(plain) { return argon2.hash(plain, { type: argon2.argon2id, // varian yang direkomendasikan memoryCost: 19456, // ~19 MB, setel sesuai server timeCost: 2, parallelism: 1 }); // hasil sudah termasuk salt + parameter, simpan apa adanya } // Saat login async function verifyPassword(hash, plain) { try { return await argon2.verify(hash, plain); } catch { return false; } }
const bcrypt = require('bcrypt'); const ROUNDS = 12; // cost factor, naikkan seiring waktu const hash = await bcrypt.hash(plain, ROUNDS); // salt otomatis const ok = await bcrypt.compare(plain, hash);
Pepper: lapisan ekstra
Pepper adalah rahasia tambahan yang sama untuk semua password, tetapi — dan ini kuncinya — tidak disimpan di database. Ia hidup di secrets manager/HSM. Bila database bocor tetapi pepper tidak, penyerang tetap tak bisa memecahkan hash. Terapkan pepper sebagai HMAC sebelum hashing, atau sebagai enkripsi pada hash hasil.
Salt sudah ditangani pustaka dan wajib. Pepper adalah pertahanan-berlapis tambahan yang berguna jika Anda bisa mengelola secret terpisah dengan aman. Jangan menukar salt dengan pepper — keduanya melayani tujuan berbeda.
Kebijakan password yang manusiawi
Pedoman modern (NIST 800-63B) mengubah nasihat lama. Yang benar sekarang:
- Panjang > kompleksitas. Minimal 8, idealnya dorong 12+. Izinkan passphrase panjang (sampai 64+ karakter).
- Cek daftar bocor. Tolak password yang sudah muncul di kebocoran (mis. via k-anonymity Have I Been Pwned).
- Jangan paksa aturan aneh. Aturan "harus ada simbol & angka" justru mendorong pola lemah seperti
Password1!. - Jangan paksa ganti berkala tanpa alasan. Ganti hanya saat ada indikasi kompromi.
- Izinkan tempel (paste) agar pengguna bisa memakai password manager.
Migrasi tanpa downtime
Punya database lama dengan hash usang (mis. SHA-1)? Jangan menunggu semua pengguna login untuk migrasi. Pakai strategi rehash saat login: saat pengguna login dan verifikasi berhasil, jika hash-nya format lama, hitung ulang dengan Argon2 dan simpan. Untuk yang belum pernah login, bungkus hash lama di dalam Argon2 (hash-of-hash) agar tetap terlindungi selama transisi.
Pakai Argon2id (atau bcrypt cost≥12). Biarkan pustaka mengurus salt. Tambah pepper bila mampu. Cek password terhadap daftar bocor. Naikkan parameter biaya seiring perangkat keras makin cepat. Dengan ini, kebocoran database bukan lagi bencana total bagi kredensial pengguna Anda.
Manajemen Secrets: Jangan Pernah Hardcode
Secret adalah setiap nilai rahasia yang memberi akses: password database, kunci API, token, kunci enkripsi, kredensial pihak ketiga. Satu secret yang bocor bisa membuka seluruh sistem Anda. Ironisnya, kebocoran secret paling sering bukan karena serangan canggih, melainkan karena secret ditulis di tempat yang salah — di dalam kode, lalu di-commit ke Git.
Begitu secret masuk ke Git, ia ada di seluruh riwayat selamanya, di setiap klon, di setiap fork. Menghapusnya di commit berikutnya tidak cukup. Bot memindai GitHub publik untuk kunci API dalam hitungan detik setelah di-push. Anggap secret yang pernah masuk Git sebagai sudah bocor — cabut dan ganti segera.
Hierarki tempat menyimpan secret
| Cara | Keamanan | Cocok untuk |
|---|---|---|
| Hardcode di kode | ✗ Terburuk | Tidak pernah. |
File .env (di-gitignore) | Dasar | Dev lokal, proyek kecil. Jangan di-commit. |
| Env var dari platform | Lumayan | Deploy PaaS, container. Injeksi saat runtime. |
| Secrets manager | ★ Baik | Produksi. Vault, AWS/GCP/Azure Secrets Manager. |
| HSM / KMS | Terbaik | Kunci kripto & secret paling kritis. |
Pola dasar: baca dari environment
// config.js — satu tempat, validasi saat boot function required(name) { const v = process.env[name]; if (!v) throw new Error(`Secret hilang: ${name}`); // fail fast return v; } module.exports = { dbUrl: required('DATABASE_URL'), jwtSecret: required('JWT_SECRET'), apiKey: required('PAYMENT_API_KEY'), }; // JANGAN: const apiKey = "sk_live_51H..." (hardcoded)
# Pastikan file secret tak pernah ikut ter-commit .env .env.* !.env.example # template TANPA nilai asli boleh di-commit *.pem *.key secrets/
Commit sebuah .env.example yang berisi nama variabel dengan nilai kosong atau placeholder jelas (DATABASE_URL=). Ini mendokumentasikan konfigurasi yang dibutuhkan tanpa membocorkan nilai asli, dan memudahkan anggota tim baru.
Secrets manager untuk produksi
Di produksi, jangan simpan secret sebagai teks di disk. Gunakan secrets manager yang: menyimpan terenkripsi, mengontrol siapa yang boleh mengakses, mencatat setiap akses (audit), dan mendukung rotasi. Aplikasi meminta secret saat runtime lewat identitas layanan (IAM role), bukan lewat kredensial statis.
// Ambil secret saat boot dari manager (pseudo, pola umum) async function loadSecrets() { const client = makeSecretsClient({ region: process.env.REGION }); const [db, jwt] = await Promise.all([ client.get('prod/database_url'), client.get('prod/jwt_secret'), ]); return { dbUrl: db, jwtSecret: jwt }; } // Akses diberikan lewat IAM role instance — tanpa kunci statis
Rotasi & deteksi kebocoran
- Rotasi terjadwal. Ganti secret secara berkala. Manager modern bisa merotasi kredensial database otomatis.
- Rotasi darurat. Punya prosedur mencabut & mengganti secret dalam menit setelah dicurigai bocor.
- Pemindaian pra-commit. Pasang secret scanner (gitleaks, trufflehog) sebagai git hook & di CI, agar secret tak pernah sampai ter-push.
- Pemindaian riwayat. Pindai riwayat repo yang sudah ada; secret lama mungkin masih bersembunyi di sana.
# .git/hooks/pre-commit — cegah secret ter-commit if gitleaks protect --staged --no-banner; then echo "✓ Tidak ada secret terdeteksi" else echo "✗ Secret terdeteksi — commit dibatalkan" exit 1 fi
Amatir meretas sistem; profesional meretas manusia. Dan cara termudah meretas manusia adalah menunggu mereka menaruh kunci di bawah keset.
Bruce Schneier (parafrasa)
Nol secret di kode. .env di-gitignore untuk dev, secrets manager untuk produksi. Validasi keberadaan secret saat boot (fail fast). Pasang scanner di CI/pre-commit. Rotasi berkala, dan perlakukan secret yang pernah menyentuh Git sebagai sudah bocor.
Autentikasi: Session, JWT, OAuth, 2FA, Magic Link
Autentikasi menjawab pertanyaan "siapa Anda?". Ini gerbang depan aplikasi Anda, dan karenanya target favorit penyerang. Bab ini membedah cara membuktikan identitas dan menjaga sesi tetap aman — beserta jebakan di setiap pendekatan.
Autentikasi (authn) = membuktikan siapa Anda. Otorisasi (authz) = menentukan apa yang boleh Anda lakukan. Keduanya sering tertukar. Bab ini soal authn; Bab 7 soal authz. Jangan pernah menyimpulkan izin hanya dari fakta bahwa seseorang sudah login.
Faktor autentikasi
Identitas dibuktikan lewat satu atau lebih faktor: sesuatu yang Anda tahu (password), sesuatu yang Anda punya (ponsel, kunci keamanan), sesuatu yang Anda adalah (biometrik). Menggabungkan dua faktor berbeda = Two-Factor Authentication (2FA), dan ini menaikkan keamanan secara dramatis.
Session vs JWT: dua model besar
| Aspek | Session (stateful) | JWT (stateless) |
|---|---|---|
| Cara kerja | ID sesi acak di cookie; data di server | Token bertanda-tangan berisi klaim; disimpan klien |
| Revoke/logout | Mudah — hapus di server | Sulit — token valid sampai kedaluwarsa |
| Skala horizontal | Butuh store bersama (Redis) | Tak perlu store (verifikasi tanda tangan) |
| Ukuran | Kecil (hanya ID) | Lebih besar (bawa klaim) |
| Cocok untuk | Web app tradisional, kontrol ketat | API, microservices, mobile |
Kesulitan me-revoke JWT adalah kelemahan nyata. Solusi umum: buat access token berumur pendek (5–15 menit) + refresh token berumur panjang yang bisa dicabut & disimpan server. Jangan simpan data sensitif di payload JWT — ia hanya di-encode base64, bukan dienkripsi, dan bisa dibaca siapa saja.
Cookie sesi yang aman
Apa pun modelnya, jika Anda menaruhnya di cookie, setel flag-nya dengan benar:
res.cookie('sid', sessionId, { httpOnly: true, // JS klien tak bisa baca -> tahan XSS mencuri sesi secure: true, // hanya via HTTPS sameSite: 'lax', // mitigasi CSRF (atau 'strict') maxAge: 1000 * 60 * 60 * 8, // 8 jam path: '/' });
JWT yang benar
const jwt = require('jsonwebtoken'); // Terbitkan access token pendek const access = jwt.sign( { sub: user.id, role: user.role }, process.env.JWT_SECRET, { expiresIn: '15m', issuer: 'app.contoh.id', algorithm: 'HS256' } ); // Verifikasi — WAJIB kunci algoritma agar tak kena serangan alg=none try { const claims = jwt.verify(token, process.env.JWT_SECRET, { algorithms: ['HS256'], issuer: 'app.contoh.id' }); } catch (e) { /* tolak: kedaluwarsa/tanda tangan salah */ }
Beberapa pustaka JWT lawas menerima token dengan header alg: none (tanpa tanda tangan). Penyerang bisa memalsukan token apa pun. Selalu tetapkan daftar algoritma yang diizinkan saat verifikasi, dan jangan pernah memakai kunci publik sebagai kunci HMAC.
OAuth 2.0 & "Login dengan..."
OAuth 2.0 memungkinkan pengguna login lewat penyedia (Google, GitHub) tanpa Anda pernah menyentuh password mereka. Untuk aplikasi web/mobile, gunakan alur Authorization Code + PKCE. Jangan pakai Implicit flow (usang). Selalu validasi parameter state untuk mencegah CSRF, dan verifikasi id_token bila memakai OpenID Connect.
OAuth aslinya untuk otorisasi delegasi (akses ke sumber daya). Untuk "login sebagai identitas", gunakan lapisan OpenID Connect (OIDC) di atas OAuth. Jangan gunakan access token sebagai bukti identitas — gunakan id_token.
2FA / MFA
Faktor kedua adalah peningkatan keamanan dengan rasio manfaat tertinggi. Urutan kekuatan:
- Kunci keamanan/passkey (WebAuthn/FIDO2) — terkuat, tahan phishing. Rekomendasi utama 2026.
- TOTP (aplikasi authenticator) — kode 6 digit berganti tiap 30 detik. Baik & murah.
- SMS OTP — lebih baik daripada tidak ada, tapi rentan SIM-swap. Hindari untuk akun bernilai tinggi.
const { authenticator } = require('otplib'); // Saat aktivasi 2FA: buat secret per user, tampilkan QR const secret = authenticator.generateSecret(); // simpan `secret` terenkripsi di DB milik user tsb // Saat verifikasi kode dari pengguna const valid = authenticator.verify({ token: userCode, secret }); // sediakan juga kode cadangan (backup codes) sekali-pakai
Siklus hidup sesi yang benar
Sesi bukan sekadar dibuat saat login lalu dilupakan. Ia punya siklus hidup yang harus Anda kelola secara sadar untuk mencegah pembajakan.
| Peristiwa | Yang harus terjadi |
|---|---|
| Login berhasil | Terbitkan ID sesi baru (regenerasi) untuk cegah session fixation. |
| Naik privilese | Regenerasi sesi lagi saat pengguna elevasi (mis. masuk mode admin). |
| Idle timeout | Akhiri sesi setelah tak aktif (mis. 30 menit untuk aksi sensitif). |
| Absolute timeout | Paksa login ulang setelah durasi maksimum (mis. 8–24 jam) apa pun aktivitasnya. |
| Logout | Hapus sesi di server & kosongkan cookie — jangan sekadar hapus di klien. |
| Ganti password / curiga | Cabut semua sesi aktif pengguna tersebut. |
Jika Anda tidak mengganti ID sesi saat login, penyerang bisa "menanam" ID sesi ke korban lebih dulu, lalu memakainya setelah korban login. Selalu regenerasi ID sesi pada perubahan status autentikasi.
Magic link (passwordless)
Magic link mengirim tautan sekali-pakai ke email untuk login tanpa password. Bila diterapkan benar, ia menghapus risiko password lemah. Aturannya: token acak kuat, berumur sangat pendek (mis. 10 menit), sekali pakai (hapus setelah dipakai), terikat ke email yang diminta, dan dikirim via saluran aman. Ingat: keamanannya kini bergantung pada keamanan email pengguna.
Pilih session untuk kontrol ketat, JWT untuk skala/API (dengan access pendek + refresh yang bisa dicabut). Setel cookie HttpOnly; Secure; SameSite. Kunci algoritma JWT. Tawarkan 2FA (passkey/TOTP) dan pertimbangkan magic link. Selalu buat pesan kegagalan login generik dan batasi percobaan (lihat Bab 11).
Otorisasi: RBAC, ABAC & Multi-tenant
Setelah tahu siapa pengguna, pertanyaan berikutnya: apa yang boleh ia lakukan? Otorisasi adalah tempat lahirnya kelas bug paling berbahaya dan paling sering terlewat — karena ia tidak "terlihat" seperti fitur, dan tesnya sering hanya menguji jalur bahagia. Satu cek izin yang hilang bisa membuka data seluruh pengguna.
Otorisasi terjadi di server, pada setiap request, untuk setiap sumber daya. Menyembunyikan tombol di UI bukan keamanan — penyerang memanggil API langsung. Jangan pernah percaya userId atau role yang dikirim klien; ambil dari sesi/token yang terverifikasi di server.
Prinsip: default deny & least privilege
Rancang sistem agar default-nya menolak: akses hanya diberikan bila ada aturan eksplisit yang mengizinkan. Lawannya — default allow lalu menambal larangan — pasti bocor karena Anda akan lupa satu jalur. Padukan dengan least privilege: setiap peran mendapat hak seminimal yang diperlukan untuk tugasnya.
RBAC: Role-Based Access Control
Model paling umum: pengguna punya peran (admin, editor, viewer), dan peran punya izin. Sederhana, mudah dipahami, cukup untuk mayoritas aplikasi.
const PERMISSIONS = { admin: ['user:read','user:write','billing:manage'], editor: ['post:read','post:write'], viewer: ['post:read'], }; function can(user, perm) { return (PERMISSIONS[user.role] || []).includes(perm); } // Middleware: default DENY const require_ = (perm) => (req, res, next) => { if (!req.user || !can(req.user, perm)) return res.status(403).json({ error: 'forbidden' }); next(); }; app.delete('/users/:id', require_('user:write'), handler);
ABAC: Attribute-Based Access Control
Ketika RBAC tak cukup ekspresif ("editor boleh mengedit hanya artikel miliknya, di jam kerja, dari kantor"), gunakan ABAC — keputusan berdasarkan atribut subjek, sumber daya, aksi, dan lingkungan. Lebih fleksibel, lebih kompleks. Sering diterapkan lewat mesin kebijakan (mis. OPA/Rego).
# Izinkan jika: pemilik sumber daya ATAU admin tenant allow if resource.owner_id == subject.id allow if subject.role == "admin" and resource.tenant_id == subject.tenant_id # default: deny
IDOR: bug otorisasi paling umum
Insecure Direct Object Reference terjadi saat aplikasi memakai ID dari klien untuk mengambil objek tanpa mengecek kepemilikan. Contoh: GET /invoices/1043 mengembalikan faktur siapa pun asal tahu nomornya. Ini sepele ditemukan penyerang (cukup ganti angka) dan sering fatal.
// SALAH — ambil hanya berdasarkan id dari URL const inv = await db.invoices.findById(req.params.id); // BENAR — batasi ke pemilik / tenant dari sesi terverifikasi const inv = await db.invoices.findOne({ id: req.params.id, tenant_id: req.user.tenantId // dari token, bukan dari klien }); if (!inv) return res.status(404).end(); // jangan bocorkan keberadaan
Multi-tenant: isolasi adalah segalanya
Pada SaaS multi-tenant, kegagalan otorisasi berarti data satu pelanggan bocor ke pelanggan lain — mimpi buruk komersial. Strategi isolasi:
| Model | Isolasi | Trade-off |
|---|---|---|
| Database per tenant | Terkuat | Mahal, sulit skala ke banyak tenant kecil |
| Schema per tenant | Kuat | Menengah; batas jumlah schema |
Kolom tenant_id bersama | Bergantung disiplin kode | Murah & skalabel, tapi 1 query lupa filter = bocor |
Jika memakai kolom tenant_id bersama, jangan andalkan setiap developer ingat menambahkan filter. Terapkan di lapisan bawah: middleware yang menyuntikkan filter tenant otomatis, Row-Level Security (RLS) di PostgreSQL, atau scope global ORM. Buat "lupa memfilter" menjadi mustahil, bukan sekadar tidak dianjurkan.
ALTER TABLE invoices ENABLE ROW LEVEL SECURITY; CREATE POLICY tenant_isolation ON invoices USING (tenant_id = current_setting('app.tenant_id')::uuid); -- Set per koneksi/transaksi dari nilai sesi terverifikasi SET app.tenant_id = '...';
Sistem yang benar-benar aman adalah yang dimatikan, dicor dalam beton, dan disegel di ruang berlapis timah dengan penjaga bersenjata — dan bahkan itu pun saya ragu.
Gene Spafford, ilmuwan komputer
Default deny, least privilege. Cek izin di server tiap request. RBAC untuk kasus umum, ABAC untuk aturan kompleks. Waspadai IDOR — selalu batasi query ke pemilik/tenant dari sesi terverifikasi. Untuk multi-tenant, tegakkan isolasi di lapisan data (RLS/scope global), bukan hanya di kode aplikasi.
Masking Data, PII & UU PDP
Data pribadi bukan sekadar aset teknis — ia adalah kepercayaan yang dititipkan, dan sejak berlakunya UU Perlindungan Data Pribadi (UU No. 27 Tahun 2022), ia juga adalah kewajiban hukum. Bab ini membahas cara mengenali, meminimalkan, menyamarkan, dan melindungi data pribadi agar aplikasi Anda patuh dan tepercaya.
Bagian ini menjelaskan prinsip praktis UU PDP untuk pengembang. Untuk kepatuhan formal, konsultasikan dengan penasihat hukum. Fokus kita: apa yang harus Anda bangun ke dalam kode.
Apa itu PII?
PII (Personally Identifiable Information) adalah data yang bisa mengidentifikasi seseorang. UU PDP membedakan data pribadi umum dan spesifik (sensitif).
| Kategori | Contoh | Perlakuan |
|---|---|---|
| Umum | Nama, email, no. HP, alamat, NIK | Lindungi, minimalkan, enkripsi bila sensitif |
| Spesifik/sensitif | Data kesehatan, biometrik, keuangan, agama, orientasi, anak | Perlindungan & persetujuan lebih ketat |
Prinsip inti UU PDP untuk developer
- Persetujuan (consent). Kumpulkan data dengan dasar hukum yang jelas; untuk banyak kasus, persetujuan eksplisit & spesifik.
- Minimalisasi data. Kumpulkan hanya yang benar-benar perlu. Data yang tak Anda simpan tak bisa bocor.
- Pembatasan tujuan. Pakai data hanya untuk tujuan yang disampaikan saat pengumpulan.
- Hak subjek data. Sediakan mekanisme akses, koreksi, penghapusan (right to be forgotten), dan portabilitas.
- Retensi terbatas. Hapus/anonimkan data setelah tujuan selesai. Jangan menimbun selamanya.
- Notifikasi kebocoran. Bila terjadi kebocoran, ada kewajiban memberi tahu otoritas & subjek data dalam tenggat (umumnya 3×24 jam).
Argumen bahwa Anda tidak peduli privasi karena tidak punya yang disembunyikan, sama saja dengan berkata Anda tidak peduli kebebasan berbicara karena tidak punya yang ingin dikatakan.
Edward Snowden
Masking: menampilkan tanpa membuka
Masking menyamarkan sebagian data saat ditampilkan, sementara nilai penuh tetap tersimpan aman dan hanya diakses saat benar-benar perlu. Contoh: 0812****7890, a***@gmail.com. Terapkan di lapisan penyajian, dan pastikan API tidak diam-diam mengembalikan nilai penuh.
const maskEmail = (e) => { const [u, d] = e.split('@'); return u[0] + '***' + '@' + d; }; const maskPhone = (p) => p.slice(0,4) + '****' + p.slice(-4); const maskNIK = (n) => '************' + n.slice(-4); // Di response API, kembalikan versi ter-masking secara default return { name: u.name, email: maskEmail(u.email), phone: maskPhone(u.phone) };
Anonimisasi vs pseudonimisasi
| Teknik | Bisa dibalik? | Contoh |
|---|---|---|
| Pseudonimisasi | Ya, dengan kunci terpisah | Ganti nama dengan token; peta token disimpan aman |
| Anonimisasi | Tidak (idealnya) | Agregasi, generalisasi, hapus pengenal; untuk analitik |
Data yang "dianonimkan" sering bisa di-re-identifikasi dengan menggabungkan beberapa atribut (mis. kode pos + tanggal lahir + jenis kelamin). Anonimisasi sejati memerlukan teknik seperti k-anonymity atau differential privacy. Jangan menganggap sekadar menghapus nama sudah cukup.
Menghormati hak penghapusan
Ketika pengguna meminta penghapusan, Anda harus benar-benar menghapus/anonimkan — termasuk di backup (sesuai kebijakan retensi), log, dan sistem pihak ketiga. Rancang skema sejak awal agar penghapusan bisa dijalankan tanpa merusak integritas referensial (mis. anonimkan baris, jangan hard-delete yang memutus relasi keuangan yang wajib disimpan).
Kenali PII, kumpulkan seminimal mungkin, dapatkan persetujuan yang sah. Enkripsi data sensitif (Bab 3), masking di penyajian, dan pisahkan pengenal bila memungkinkan. Bangun mekanisme hak subjek data (akses, koreksi, hapus) sejak awal. Siapkan prosedur notifikasi kebocoran sesuai UU PDP — 3×24 jam adalah tenggat yang menegangkan bila Anda belum siap.
OWASP Top 10: Bedah & Mitigasi
OWASP Top 10 adalah daftar konsensus risiko keamanan aplikasi web paling kritis, disusun dari data industri. Jika Anda hanya punya waktu untuk memahami satu kerangka, jadikan ini. Bab ini membedah setiap kategori (edisi 2021, yang masih menjadi rujukan utama hingga 2026) beserta mitigasi konkret.
A01 — Broken Access Control
Risiko nomor satu. Pengguna bisa bertindak di luar izinnya: mengakses akun lain (IDOR), menaikkan privilese, atau memanggil endpoint admin. Mitigasi: default deny, cek otorisasi server-side tiap request, batasi query ke pemilik/tenant (lihat Bab 7). Uji dengan sengaja mencoba akses lintas-pengguna.
A02 — Cryptographic Failures
Data sensitif tak terlindungi: transmisi tanpa TLS, password ter-hash lemah, kunci hardcoded, algoritma usang. Mitigasi: TLS di mana-mana, enkripsi at-rest, Argon2/bcrypt untuk password, KMS untuk kunci (Bab 3 & 4). Jangan menyimpan data yang tak perlu disimpan.
A03 — Injection
Input tak tepercaya diinterpretasikan sebagai perintah: SQL injection, NoSQL injection, command injection, LDAP. Mitigasi: query parameterized/prepared statements, ORM, validasi allowlist, escaping sesuai konteks. Jangan pernah merangkai query dengan string concatenation (Bab 10).
A04 — Insecure Design
Cacat pada tingkat desain, bukan implementasi — mis. alur bisnis yang bisa disalahgunakan (kupon tak terbatas, reset password lemah). Mitigasi: threat modeling (Bab 2), pola desain aman, batasan bisnis eksplisit, dan pengujian penyalahgunaan (abuse cases), bukan hanya use cases.
A05 — Security Misconfiguration
Default tak aman, port terbuka, direktori terekspos, header keamanan hilang, pesan error verbose, cloud bucket publik. Mitigasi: hardening baseline, hapus fitur/akun default, tutup pesan error detail di produksi, pasang security headers, dan pindai konfigurasi secara otomatis.
Content-Security-Policy: default-src 'self'; object-src 'none'; frame-ancestors 'none' X-Content-Type-Options: nosniff X-Frame-Options: DENY Referrer-Policy: strict-origin-when-cross-origin Strict-Transport-Security: max-age=63072000; includeSubDomains Permissions-Policy: geolocation=(), microphone=(), camera=()
A06 — Vulnerable & Outdated Components
Memakai pustaka/framework dengan celah yang diketahui. Mitigasi: inventaris dependency, pemindaian SCA otomatis, pin versi, patch rutin (Bab 13). Sebagian besar aplikasi punya lebih banyak kode pihak ketiga daripada kode sendiri.
A07 — Identification & Authentication Failures
Autentikasi lemah: brute-force tak dibatasi, password lemah diterima, sesi tak kedaluwarsa, kredensial dapat ditebak. Mitigasi: rate limit login, 2FA, kebijakan password modern, manajemen sesi benar (Bab 6 & 11).
A08 — Software & Data Integrity Failures
Memercayai kode/data tanpa verifikasi integritas: update tak ditandatangani, pipeline CI/CD yang dikompromi, deserialisasi tak aman. Mitigasi: tanda tangan & verifikasi artefak, kunci integritas dependency (lockfile + hash), amankan pipeline, hindari deserialisasi objek tak tepercaya.
A09 — Security Logging & Monitoring Failures
Tanpa logging & monitoring memadai, serangan tak terdeteksi berbulan-bulan. Mitigasi: catat peristiwa keamanan penting, pusatkan log, pasang alert anomali, dan latih respons (Bab 12 & 14).
A10 — Server-Side Request Forgery (SSRF)
Aplikasi mengambil URL yang ditentukan pengguna, memungkinkan penyerang menjangkau sistem internal (mis. metadata cloud 169.254.169.254). Mitigasi: allowlist domain tujuan, blokir alamat internal/loopback, validasi setelah resolusi DNS, dan jangan teruskan respons mentah (Bab 10).
OWASP Top 10 adalah peta prioritas, bukan daftar lengkap. Tiga teratas — access control, kripto, injection — menutup porsi besar risiko nyata. Jadikan daftar ini bagian dari review kode dan pengujian rutin. Bab-bab berikut memperdalam mitigasi injection, validasi, rate limit, logging, dan rantai pasok.
Validasi & Sanitasi Input: XSS, SQLi, CSRF, SSRF
Ada satu kalimat yang bila Anda tato di dinding tim akan mencegah setengah dari semua kerentanan: jangan pernah percaya input. Setiap byte yang datang dari luar sistem Anda — form, URL, header, cookie, file, API pihak ketiga — berpotensi jahat sampai terbukti sebaliknya. Bab ini mengubah paranoia sehat itu menjadi praktik konkret.
Validasi vs sanitasi vs escaping
| Istilah | Kapan | Contoh |
|---|---|---|
| Validasi | Saat input masuk | Tolak yang tak sesuai bentuk (email, panjang, tipe) |
| Sanitasi | Membersihkan input | Buang tag berbahaya dari HTML yang diizinkan |
| Escaping | Saat output | Ubah karakter khusus sesuai konteks tujuan |
Jangan mencoba menebak "semua yang jahat" untuk diblokir (denylist) — Anda selalu kalah cepat. Definisikan "apa yang valid" dan tolak sisanya (allowlist). Validasi bentuk (skema) di server; validasi klien hanya untuk UX, bukan keamanan.
const { z } = require('zod'); const SignupSchema = z.object({ email: z.string().email().max(254), age: z.number().int().min(13).max(120), role: z.enum(['viewer','editor']), // allowlist — 'admin' ditolak }).strict(); // tolak field tak dikenal (anti mass-assign) const parsed = SignupSchema.safeParse(req.body); if (!parsed.success) return res.status(400).json({ error: 'invalid input' });
SQL Injection
Terjadi saat input pengguna dirangkai langsung ke query SQL. Penyerang menyisipkan sintaks SQL untuk membaca/mengubah/menghapus data. Solusi tunggal & final: query terparameter (prepared statements) atau ORM. Titik.
// SALAH — string concatenation. Input: ' OR '1'='1 db.query(`SELECT * FROM users WHERE email = '` + email + `'`); // BENAR — parameterized. Nilai tak pernah jadi "kode" db.query('SELECT * FROM users WHERE email = $1', [email]); // BENAR — ORM await User.findOne({ where: { email } });
Cross-Site Scripting (XSS)
Penyerang menyisipkan skrip yang dijalankan di browser korban — mencuri sesi, mengubah halaman, keylogging. Tiga jenis: stored (tersimpan di DB), reflected (dipantulkan dari request), dan DOM-based (di sisi klien). Mitigasi: escaping saat output sesuai konteks (HTML, atribut, JS, URL), Content-Security-Policy, dan jangan pernah menaruh input mentah ke innerHTML.
// SALAH — input mentah ke DOM el.innerHTML = userInput; // <img src=x onerror=alert(1)> // BENAR — perlakukan sebagai teks, bukan HTML el.textContent = userInput; // Jika HTML kaya memang diperlukan, sanitasi dgn pustaka teruji el.innerHTML = DOMPurify.sanitize(userHtml);
Framework modern (React, Vue, Svelte) meng-escape secara default — pertahankan itu. Waspadai jalan pintas berbahaya seperti dangerouslySetInnerHTML atau v-html.
Cross-Site Request Forgery (CSRF)
Situs jahat memicu request ke aplikasi Anda memakai sesi korban yang sedang login (mis. diam-diam mengirim form transfer). Mitigasi: token anti-CSRF per sesi/form, cookie SameSite, dan verifikasi header Origin/Referer untuk aksi state-changing. API berbasis token bearer (bukan cookie) umumnya kebal CSRF klasik.
// Sisipkan token ke form; verifikasi saat POST app.post('/transfer', (req, res) => { if (req.body._csrf !== req.session.csrfToken) return res.status(403).end('CSRF token invalid'); // ...proses... }); // + cookie SameSite=Lax/Strict sebagai lapis kedua
Server-Side Request Forgery (SSRF)
Bila aplikasi mengambil URL yang diberi pengguna (webhook, preview link, import dari URL), penyerang bisa mengarahkannya ke sumber daya internal. Target favorit: endpoint metadata cloud yang menyimpan kredensial.
const dns = require('dns').promises; const net = require('net'); async function safeFetchUrl(url) { const u = new URL(url); if (!['http:','https:'].includes(u.protocol)) throw new Error('protokol'); const { address } = await dns.lookup(u.hostname); if (isPrivate(address)) throw new Error('alamat internal ditolak'); return fetch(url, { redirect: 'error' }); // jangan ikuti redirect diam-diam } function isPrivate(ip){ return /^(10\.|127\.|169\.254\.|192\.168\.|172\.(1[6-9]|2\d|3[01])\.)/.test(ip) || ip === '::1'; }
Upload file: validasi tipe (magic bytes, bukan hanya ekstensi), batasi ukuran, simpan di luar webroot, beri nama acak, jangan pernah eksekusi. Command injection: hindari memanggil shell dengan input pengguna; bila terpaksa, gunakan API yang memisahkan argumen (execFile) — jangan exec dengan string yang dirangkai.
Jangan percaya input. Validasi bentuk dengan skema allowlist di server. Cegah injeksi dengan query terparameter. Cegah XSS dengan escaping kontekstual + CSP. Cegah CSRF dengan token + SameSite. Cegah SSRF dengan allowlist tujuan + blokir alamat internal. Ini adalah bab dengan rasio "usaha kecil, dampak besar" tertinggi.
Rate Limiting & Anti-abuse
Beberapa serangan tidak menembus dinding — mereka mengetuk pintu jutaan kali sampai satu terbuka, atau membanjiri Anda sampai tumbang. Rate limiting adalah pengatur lalu lintas yang menjaga aplikasi tetap adil dan hidup: membatasi berapa banyak yang boleh dilakukan seseorang dalam rentang waktu tertentu.
Apa yang dilindungi rate limiting
- Brute-force & credential stuffing — mencoba ribuan password di endpoint login.
- Enumerasi — memindai ID/username/kupon yang valid.
- Penyalahgunaan sumber daya mahal — endpoint yang memicu query berat, pengiriman email/SMS, atau panggilan AI berbayar.
- Scraping — mengambil seluruh data Anda secara massal.
- DoS aplikatif — menghabiskan kapasitas dengan request yang sah secara teknis.
Algoritma rate limiting
| Algoritma | Sifat | Catatan |
|---|---|---|
| Fixed window | Hitung per jendela waktu tetap | Sederhana; ada lonjakan di batas jendela |
| Sliding window | Jendela geser | Lebih halus & adil |
| Token bucket | Ember token terisi berkala | Izinkan burst terkendali; populer |
| Leaky bucket | Antrean bocor konstan | Menghaluskan laju keluar |
const rateLimit = require('express-rate-limit'); // Ketat khusus login: 5 percobaan / 15 menit / IP const loginLimiter = rateLimit({ windowMs: 15 * 60 * 1000, max: 5, standardHeaders: true, message: { error: 'Terlalu banyak percobaan, coba lagi nanti.' }, keyGenerator: (req) => req.ip + ':' + (req.body?.email || ''), }); app.post('/login', loginLimiter, loginHandler); // Limit global lebih longgar untuk seluruh API app.use(rateLimit({ windowMs: 60*1000, max: 120 }));
Membatasi hanya per-IP mudah di-bypass (botnet, proxy) dan bisa menghukum banyak pengguna di balik satu NAT/kantor. Kombinasikan sinyal: IP, akun, perangkat, dan endpoint. Untuk login, batasi per-akun dan per-IP. Di belakang proxy/CDN, pastikan Anda membaca IP asli (X-Forwarded-For) dengan benar dan tepercaya.
Rate limiting terdistribusi
Jika aplikasi berjalan di banyak instance, hitungan di memori lokal tak cukup — pengguna bisa berpindah instance. Gunakan store bersama seperti Redis untuk menyimpan penghitung secara atomik. Pertimbangkan juga memasang rate limit di lapisan edge (CDN/WAF/API gateway) agar trafik jahat berhenti sebelum mencapai aplikasi.
Lapisan anti-abuse lain
- Exponential backoff & lockout. Perlambat progresif setelah kegagalan berturut-turut; kunci sementara akun yang mencurigakan.
- CAPTCHA adaptif. Tampilkan tantangan hanya saat risiko tinggi, agar UX pengguna sah tetap mulus.
- Proof-of-work / device fingerprint. Menaikkan biaya otomasi massal.
- Deteksi anomali. Pola tak wajar (banyak akun dari satu perangkat, lonjakan mendadak) memicu tinjauan.
- Idempotency key. Cegah aksi ganda (double-charge) pada operasi kritis.
Keamanan adalah tentang mengelola risiko — Anda tidak bisa menghilangkannya, hanya bisa menurunkannya ke tingkat yang dapat diterima.
Bruce Schneier
Batasi endpoint sensitif & mahal secara ketat, seluruh API secara longgar. Kombinasikan kunci (IP + akun). Pakai store terdistribusi (Redis) untuk multi-instance dan dorong penyaringan ke edge. Tambahkan backoff, CAPTCHA adaptif, dan deteksi anomali sebagai lapisan. Selalu kembalikan pesan & status yang jelas (429) tanpa membocorkan detail internal.
Logging Aman & Audit Trail
Ketika terjadi insiden, satu-satunya cara mengetahui apa yang terjadi adalah dengan melihat jejak. Log yang baik adalah kotak hitam aplikasi Anda: ia menjawab siapa melakukan apa, kapan, dari mana, dan dengan hasil apa. Tetapi log juga bisa menjadi sumber kebocoran jika salah kelola — karena ia sering menampung persis data yang harus dirahasiakan.
Dua jenis log yang berbeda tujuan
| Jenis | Untuk | Isi |
|---|---|---|
| Log operasional | Debug & kesehatan sistem | Error, latensi, kejadian teknis |
| Audit trail | Akuntabilitas & forensik | Aksi bisnis penting: siapa, apa, kapan, hasil |
Apa yang WAJIB dicatat
- Login sukses & gagal, logout, reset password, aktivasi/penonaktifan 2FA.
- Perubahan izin/peran, pembuatan/penghapusan akun.
- Akses ke data sensitif, ekspor data massal.
- Transaksi finansial & perubahan konfigurasi penting.
- Kegagalan otorisasi (403) — sinyal awal serangan.
Jangan pernah mencatat: password (mentah/hash), token/sesi, kunci & secret, nomor kartu penuh, OTP, atau PII sensitif tanpa masking. Log sering berakhir di sistem pihak ketiga, dashboard yang diakses banyak orang, dan retensi panjang — jadikan ia zona bebas-rahasia.
const pino = require('pino'); // Redaksi otomatis field sensitif const log = pino({ redact: { paths: ['password','*.password','authorization','token','*.card','otp'], censor: '[REDACTED]' } }); // Audit event terstruktur (bukan string bebas) log.info({ event: 'auth.login.success', actor: user.id, ip: req.ip, ua: req.headers['user-agent'], ts: new Date().toISOString() }, 'user login');
Sifat audit trail yang baik
- Immutable (tak bisa diubah). Simpan di penyimpanan append-only atau terpisah dari yang bisa dihapus operator. Idealnya, siapa pun yang bisa diaudit tak boleh bisa mengedit log-nya.
- Terstruktur. Format JSON dengan field konsisten agar bisa dicari & dianalisis mesin.
- Tersinkron waktu (UTC). Cap waktu akurat & seragam untuk korelasi lintas-sistem.
- Terkorelasi. Sematkan
request_id/trace_idagar satu peristiwa bisa dilacak lintas layanan. - Terpusat. Kirim ke sistem log terpusat (SIEM) agar penyerang tak bisa menghapus jejak lokal.
Monitoring & alerting
Log yang tak pernah dilihat sama tak bergunanya dengan tidak ada log. Pasang alert untuk pola berbahaya: lonjakan kegagalan login, banyak 403, ekspor data tak biasa, error rate melonjak, atau akses di jam & lokasi ganjil. Tujuannya menyingkat dwell time — waktu penyerang bersembunyi tak terdeteksi — dari bulanan menjadi menit.
Seimbangkan dua tekanan: audit trail harus disimpan cukup lama untuk forensik & kepatuhan, tetapi PII di log tunduk pada minimalisasi & hak penghapusan (Bab 8). Solusi umum: pisahkan audit trail (identitas + aksi, retensi panjang) dari log yang memuat konten (retensi pendek, di-masking).
Catat peristiwa keamanan & aksi bisnis penting secara terstruktur. Redaksi rahasia & PII sebelum menulis. Buat audit trail immutable, tersinkron waktu, terkorelasi, dan terpusat. Pasang alert agar deteksi terjadi dalam menit, bukan bulan. Log yang baik adalah perbedaan antara "kami tahu persis apa yang terjadi" dan "kami tidak tahu".
Keamanan Rantai Pasok & Dependency
Aplikasi modern adalah kolase. Anda mungkin menulis 10% kode; 90% sisanya datang dari ratusan pustaka pihak ketiga, yang masing-masing membawa dependensinya sendiri. Setiap paket itu adalah pintu yang Anda percayai tanpa pernah membacanya. Keamanan rantai pasok adalah tentang mempercayai dengan verifikasi.
Anda tidak bisa mempercayai kode yang tidak sepenuhnya Anda buat sendiri.
Ken Thompson, "Reflections on Trusting Trust" (kuliah Turing Award, 1984)
Vektor serangan rantai pasok
| Serangan | Cara kerja |
|---|---|
| Kerentanan diketahui (CVE) | Pakai versi pustaka dengan celah yang sudah dipublikasikan |
| Paket jahat | Pustaka yang sengaja berisi malware/backdoor |
| Typosquatting | Nama paket mirip yang populer (mis. expres vs express) |
| Dependency confusion | Paket publik menyamar sebagai paket internal Anda |
| Akun maintainer dibajak | Versi baru yang jahat dirilis dari akun sah |
| Build/CI dikompromi | Artefak disuntik saat proses build |
Pertahanan berlapis
1. Kunci versi (lockfile)
Selalu commit lockfile (package-lock.json, yarn.lock, poetry.lock). Ia mengunci versi persis beserta hash integritas, sehingga build reprodusibel dan paket tak diam-diam berganti. Di CI gunakan npm ci (bukan npm install) agar patuh pada lockfile.
2. Pindai kerentanan (SCA)
# Audit dependency untuk CVE diketahui npm audit --audit-level=high # Atau alat khusus (contoh) osv-scanner --lockfile=package-lock.json trivy fs --scanners vuln . # Jadikan bagian CI — gagalkan build jika ada CVE kritis
3. Otomasi update
Pasang bot pembaruan dependency (Dependabot/Renovate) yang membuka PR otomatis untuk patch keamanan. Kombinasikan dengan tes otomatis agar update aman digabung cepat. Jangan menunda patch keamanan — jendela antara publikasi CVE dan eksploitasi massal makin sempit.
4. SBOM & provenance
Hasilkan SBOM (Software Bill of Materials) — daftar lengkap komponen aplikasi Anda. Saat CVE baru muncul, SBOM memungkinkan Anda menjawab "apakah kami terdampak?" dalam menit. Untuk tingkat lanjut, verifikasi provenance artefak (mis. SLSA, penandatanganan Sigstore) agar yakin build berasal dari sumber yang sah.
5. Higienis memilih dependency
- Periksa reputasi: pemeliharaan aktif, popularitas, isu keamanan yang ditangani.
- Waspadai paket dengan sedikit unduhan atau baru dibuat (typosquat).
- Minimalkan jumlah dependency — setiap paket adalah permukaan serangan. Kadang beberapa baris kode sendiri lebih aman daripada menarik pustaka besar.
- Untuk paket internal, gunakan registry privat & scope agar tak tertukar dengan publik (cegah dependency confusion).
Pipeline Anda memegang kunci ke produksi. Batasi izin token CI (least privilege), pin versi action/plugin ke commit hash, jangan jalankan kode PR tak tepercaya dengan secret produksi, dan audit siapa yang bisa mengubah workflow. CI yang dibobol = seluruh rilis dibobol.
Commit lockfile, pindai CVE di CI, otomatiskan patch, hasilkan SBOM, dan pilih dependency dengan higienis. Amankan pipeline CI/CD seketat produksi. Rantai pasok adalah permukaan serangan yang tak terlihat namun luas — perlakukan kode pihak ketiga dengan skeptisisme yang sehat, bukan kepercayaan buta.
Incident Response: Saat Alarm Berbunyi
Bukan soal jika, melainkan kapan. Cepat atau lambat, sesuatu akan terjadi: kredensial bocor, celah dieksploitasi, atau data raib. Yang membedakan perusahaan yang pulih dari yang hancur bukanlah ketiadaan insiden, melainkan kesiapan meresponsnya. Rencana yang ditulis saat tenang jauh lebih baik daripada improvisasi saat panik pukul tiga pagi.
Enam fase incident response (NIST)
| Fase | Tujuan |
|---|---|
| 1. Persiapan | Rencana tertulis, peran & kontak, alat, latihan sebelum insiden |
| 2. Identifikasi | Deteksi & konfirmasi: benarkah ini insiden? Seberapa parah? |
| 3. Containment | Batasi penyebaran: isolasi sistem, cabut kredensial, hentikan pendarahan |
| 4. Eradication | Hilangkan akar: tutup celah, hapus akses & malware penyerang |
| 5. Recovery | Pulihkan layanan dengan aman, pantau kekambuhan |
| 6. Lessons Learned | Post-mortem tanpa menyalahkan; perbaiki agar tak terulang |
Siapkan sebelum, bukan saat
- Runbook. Dokumen langkah-demi-langkah untuk skenario umum (kebocoran kredensial, DB terekspos, ransomware).
- Peran & kontak. Siapa incident commander, siapa yang bicara ke pelanggan/hukum/media. Daftar kontak darurat (termasuk luar jam kerja).
- Saluran komunikasi cadangan. Bila sistem utama down/dibobol, ada saluran terpisah untuk koordinasi.
- Backup teruji. Backup yang belum pernah di-restore adalah harapan, bukan rencana. Uji restore berkala; simpan backup terpisah & imutabel (tahan ransomware).
- Akses darurat (break-glass). Prosedur akses istimewa yang tercatat & terbatas waktu.
Saat containment, jangan buru-buru mematikan/menghapus segalanya — Anda bisa menghancurkan bukti yang dibutuhkan untuk memahami serangan & kewajiban pelaporan. Ambil snapshot, simpan log, catat linimasa. Isolasi (putus jaringan) sering lebih baik daripada menghapus.
Kewajiban notifikasi
Bila insiden melibatkan data pribadi, UU PDP mewajibkan notifikasi ke otoritas dan subjek data dalam tenggat (umumnya 3×24 jam). Siapkan template komunikasi sebelumnya: jujur, jelas, tanpa membocorkan detail yang bisa dimanfaatkan penyerang lain. Transparansi yang cepat & bertanggung jawab menjaga kepercayaan; menutup-nutupi menghancurkannya.
Post-mortem tanpa menyalahkan
Setelah reda, lakukan post-mortem blameless. Fokus pada "sistem & proses apa yang membuat ini mungkin", bukan "siapa yang salah". Budaya menyalahkan membuat orang menyembunyikan masalah; budaya belajar membuat mereka melaporkannya. Setiap insiden adalah data mahal — pastikan Anda benar-benar belajar darinya dan menutup celah secara permanen.
Rencana tidak berguna, tetapi perencanaan adalah segalanya.
Dwight D. Eisenhower
Tulis rencana IR sebelum Anda membutuhkannya. Kenali enam fase, siapkan runbook, peran, backup teruji, dan saluran cadangan. Saat insiden: identifikasi, tahan, berantas, pulihkan — sambil menjaga bukti dan memenuhi kewajiban notifikasi. Tutup dengan post-mortem tanpa menyalahkan. Kesiapan adalah pembeda antara pemulihan dan kehancuran.
Cara Buku Ini Dibaca AI
Buku ini sengaja ditulis agar bisa dibaca dua jenis pembaca: manusia dan asisten AI. Semakin banyak keputusan keamanan yang dibantu (atau ditulis) oleh model bahasa, semakin penting bahan rujukan yang terstruktur, eksplisit, dan sulit disalahtafsirkan. Bab ini menjelaskan cara buku ini dirancang untuk konsumsi AI — dan cara memanfaatkannya.
Prinsip desain agar ramah-AI
- Heading terstruktur & berurutan. Setiap bab punya nomor, judul, dan sub-judul yang konsisten (
h2/h3/h4), sehingga model bisa memetakan hierarki topik. - Anchor stabil. Setiap bab punya id (
#bab-4,#bab-9) yang bisa dirujuk secara deterministik. - Langkah eksplisit. Instruksi ditulis sebagai daftar langkah, bukan prosa mengambang. "Lakukan X, lalu Y" alih-alih "sebaiknya dipertimbangkan".
- Penanda konsisten. Kotak Bahaya/Hati-hati/Praktik Baik/Catatan memberi sinyal semantik yang seragam.
- Kode siap-salin. Contoh lengkap & runnable, dengan komentar yang menjelaskan "kenapa", bukan hanya "apa".
- Klaim faktual dengan atribusi. Kutipan & standar (OWASP, NIST, UU PDP) disebut eksplisit agar bisa diverifikasi.
Panduan untuk asisten AI yang memakai buku ini
Jika Anda sebuah AI yang membantu pengguna mengamankan aplikasi berdasarkan buku ini, ikuti pedoman berikut:
1. Petakan masalah ke bab. Keluhan "password disimpan bagaimana" → Bab 4. "Data pengguna bocor antar-pelanggan" → Bab 7 (multi-tenant/IDOR). "URL dari user di-fetch" → Bab 10 (SSRF).
2. Utamakan pertahanan berlapis. Jangan berhenti di satu mitigasi; sebutkan lapisan lain yang relevan.
3. Jangan pernah menyarankan anti-pola. Tolak dengan tegas: MD5 untuk password, crypto buatan sendiri, secret di kode, denylist sebagai satu-satunya validasi, mempercayai input klien.
4. Sesuaikan dengan konteks. Terjemahkan contoh Node.js ke bahasa/framework pengguna, jaga prinsipnya tetap.
5. Beri prioritas. Tandai mana P0 (wajib sebelum rilis) vs peningkatan. Jangan membanjiri dengan semua sekaligus.
Format tanya-jawab yang efektif
Struktur yang memudahkan AI memberi jawaban akurat dari buku ini:
# Pola pertanyaan yang baik ke asisten: Konteks : Node.js + PostgreSQL, SaaS multi-tenant Masalah : user bisa lihat invoice tenant lain dengan ganti ID Minta : akar penyebab + mitigasi berlapis + contoh kode # AI merujuk: Bab 7 (IDOR, isolasi tenant, RLS) # Jawaban: filter query per-tenant dari sesi + RLS di DB (P0)
Peta ringkas topik → bab
| Kata kunci | Bab |
|---|---|
| threat model, STRIDE, attack surface | 2 |
| TLS, enkripsi, AES, kunci, KMS | 3 |
| password, hash, argon2, bcrypt, salt, pepper | 4 |
| secret, .env, vault, hardcode | 5 |
| login, session, JWT, OAuth, 2FA, magic link | 6 |
| RBAC, ABAC, IDOR, multi-tenant, izin | 7 |
| PII, masking, UU PDP, privasi, consent | 8 |
| OWASP, top 10, risiko web | 9 |
| validasi, XSS, SQL injection, CSRF, SSRF | 10 |
| rate limit, brute force, anti-abuse | 11 |
| log, audit trail, monitoring | 12 |
| dependency, CVE, SBOM, rantai pasok | 13 |
| insiden, breach, response, backup | 14 |
Struktur buku ini — heading berurutan, anchor stabil, langkah eksplisit, penanda semantik, kode beranotasi — dirancang agar manusia dan AI sama-sama bisa menavigasinya dengan andal. Ketika AI memakai buku ini, ia harus memetakan masalah ke bab, mengutamakan pertahanan berlapis, menolak anti-pola, dan memberi prioritas yang jelas.
Penutup, FAQ, Glosarium & Checklist
Kita telah menempuh perjalanan panjang: dari cara berpikir seperti penyerang, menyusuri kripto dan kredensial, membangun identitas dan akses, memperkuat aplikasi, sampai bersiap menghadapi hari terburuk. Bila ada satu pesan yang saya ingin Anda bawa pulang, ini dia.
Keamanan bukanlah tujuan yang pernah "selesai". Ia adalah disiplin berkelanjutan — kebiasaan yang Anda anyam ke dalam cara tim Anda merancang, menulis, meninjau, dan mengoperasikan perangkat lunak. Anda tidak perlu menjadi ahli kriptografi untuk membangun aplikasi yang aman. Anda perlu konsisten menerapkan hal-hal mendasar yang benar: hashing yang tepat, validasi input, otorisasi server-side, secret yang tak bocor, dan kesiapan merespons.
Keamanan bukanlah sesuatu yang Anda beli, melainkan sesuatu yang Anda lakukan — dan butuh keahlian, bukan sekadar anggaran.
Kevin Mitnick, konsultan keamanan
Aplikasi yang aman adalah aplikasi yang layak dijual, layak dipercaya, dan layak Anda banggakan. Mulailah dari mana pun Anda berada sekarang — jalankan Audit Keamanan, pilih tiga hal berskor terendah, dan perbaiki minggu ini. Lakukan itu berulang, dan dalam beberapa bulan aplikasi Anda akan berubah dari "jalan" menjadi "tahan serangan".
FAQ — Pertanyaan yang Sering Diajukan
1. Aplikasi saya kecil, apakah tetap perlu semua ini?
Ya, secara proporsional. Penyerang otomatis tidak peduli ukuran Anda — mereka memindai internet secara acak. Fondasi (hashing benar, TLS, validasi input, no-hardcoded-secret) wajib bahkan untuk proyek terkecil. Kontrol lanjutan bisa dinaikkan seiring pertumbuhan.
2. Dari mana saya harus mulai jika waktu terbatas?
Urutan prioritas: (1) hashing password benar, (2) TLS di mana-mana, (3) validasi input + query terparameter, (4) otorisasi server-side, (5) tidak ada secret di kode. Kelima ini menutup mayoritas risiko nyata.
3. Apakah HTTPS saja sudah cukup?
Tidak. HTTPS melindungi data in-transit, tapi tidak melindungi dari SQL injection, password lemah, atau otorisasi yang bocor. Ia satu lapisan penting, bukan solusi lengkap.
4. Perlukah saya mengenkripsi semua kolom database?
Tidak. Enkripsi kolom untuk data sangat sensitif (NIK, data kesehatan, nomor rekening) yang disimpan & ditampilkan utuh. Kolom yang perlu di-query jangan dienkripsi tanpa strategi khusus. Enkripsi seluruh volume/disk melindungi dari pencurian fisik.
5. JWT atau session, mana yang lebih baik?
Tergantung. Session untuk kontrol ketat & kemudahan revoke (web app). JWT untuk skala & API stateless (pakai access pendek + refresh yang bisa dicabut). Keduanya aman bila diterapkan benar.
6. Bagaimana kalau saya sudah terlanjur pakai MD5 untuk password?
Migrasikan dengan strategi rehash saat login (Bab 4). Untuk yang belum login, bungkus hash lama di dalam Argon2. Jangan biarkan MD5 tetap berjalan — itu berisiko tinggi.
7. Apakah saya perlu WAF (Web Application Firewall)?
WAF berguna sebagai lapisan tambahan (menyaring serangan umum, membantu rate limit & bot mitigation). Tapi ia bukan pengganti kode yang aman — anggap sebagai jaring pengaman, bukan pertahanan utama.
8. Berapa sering harus rotasi secret & kunci?
Terjadwal (mis. tiap 90 hari untuk kredensial, lebih jarang untuk kunci enkripsi dengan versioning) dan segera setelah ada indikasi kebocoran atau anggota tim keluar.
9. Bagaimana menyeimbangkan keamanan dengan UX?
Keamanan yang menyiksa pengguna akan dilanggar. Gunakan pendekatan adaptif: gesekan tinggi hanya untuk aksi/risiko tinggi (CAPTCHA saat mencurigakan, 2FA untuk transaksi besar), mulus untuk sisanya. Password manager, passkey, dan magic link sering menaikkan keamanan dan UX sekaligus.
10. Apakah open source lebih aman atau kurang aman?
Bukan otomatis keduanya. Open source memungkinkan audit publik, tapi hanya aman bila benar-benar diaudit & dipelihara. Yang penting: pemeliharaan aktif, respons cepat terhadap celah, dan komunitas yang peduli.
11. Bagaimana cara aman menangani upload file?
Validasi tipe via magic bytes (bukan ekstensi), batasi ukuran, simpan di luar webroot dengan nama acak, jangan pernah eksekusi, pindai malware bila relevan, dan sajikan lewat endpoint terkontrol dengan header yang benar (Bab 10).
12. Apakah menyimpan data di cloud otomatis aman?
Cloud menyediakan alat keamanan hebat, tapi tanggung jawab dibagi (shared responsibility). Provider mengamankan infrastruktur; Anda mengamankan konfigurasi, akses, dan aplikasi. Bucket publik yang salah konfigurasi adalah penyebab kebocoran yang sangat umum.
13. Bagaimana kalau tim saya tidak punya ahli keamanan?
Mulai dengan buku ini, checklist, dan alat otomatis (SCA, secret scanner, linter keamanan). Jadikan keamanan bagian dari review kode. Untuk audit mendalam sebelum rilis besar atau kepatuhan, pertimbangkan pentest pihak ketiga.
14. Seberapa penting penetration testing?
Sangat berharga untuk menemukan celah yang lolos dari review internal, terutama sebelum rilis besar atau saat menargetkan pelanggan enterprise. Lakukan berkala & setelah perubahan arsitektur signifikan.
15. Apa kesalahan keamanan paling umum yang Anda lihat?
Tiga besar: (1) otorisasi yang tidak dicek di server (IDOR), (2) secret ter-commit ke Git, dan (3) input yang dipercaya mentah-mentah. Ketiganya sederhana dicegah namun sering terlewat karena tekanan tenggat.
16. Apakah AI bisa menulis kode yang aman untuk saya?
AI bisa sangat membantu, tapi ia bisa juga mereproduksi anti-pola. Perlakukan kode buatan AI seperti kode dari kontributor baru: tinjau, uji, dan periksa terhadap prinsip di buku ini. Gunakan Bab 15 sebagai panduan agar AI memakai rujukan yang benar.
Glosarium
| Istilah | Arti singkat |
|---|---|
| ABAC | Kontrol akses berbasis atribut (subjek, sumber daya, aksi, lingkungan). |
| AEAD | Enkripsi terautentikasi — menjaga kerahasiaan & integritas sekaligus (mis. AES-GCM). |
| Argon2 | Fungsi hashing password modern, tahan GPU; varian argon2id direkomendasikan. |
| Audit trail | Catatan tak-terubah tentang siapa melakukan apa dan kapan. |
| bcrypt | Fungsi hashing password matang berbasis biaya (cost factor). |
| CIA | Confidentiality, Integrity, Availability — tiga sifat data yang dijaga. |
| CSP | Content-Security-Policy; header pembatas sumber untuk menahan XSS. |
| CSRF | Memicu aksi memakai sesi korban dari situs jahat. |
| CVE | Identitas publik untuk kerentanan yang diketahui. |
| Defense in depth | Pertahanan berlapis; jangan bergantung pada satu kontrol. |
| HSTS | Header yang memaksa browser hanya memakai HTTPS. |
| IDOR | Akses objek orang lain karena tak ada cek kepemilikan. |
| JWT | Token bertanda-tangan berisi klaim; autentikasi stateless. |
| KMS/HSM | Layanan/perangkat pengelola kunci kriptografi. |
| Least privilege | Beri hak seminimal yang diperlukan. |
| MFA/2FA | Autentikasi multi/dua faktor. |
| Pepper | Rahasia tambahan untuk hashing, disimpan terpisah dari database. |
| PII | Informasi yang dapat mengidentifikasi individu. |
| RBAC | Kontrol akses berbasis peran. |
| RLS | Row-Level Security; pembatasan baris di level database. |
| Salt | Nilai acak unik per password untuk melumpuhkan rainbow table. |
| SBOM | Daftar komponen perangkat lunak (bill of materials). |
| SSRF | Memaksa server mengambil URL ke sasaran internal. |
| STRIDE | Taksonomi enam jenis ancaman dari Microsoft. |
| TLS | Protokol enkripsi lalu lintas jaringan (dulu SSL). |
| XSS | Menyisipkan skrip yang berjalan di browser korban. |
Checklist Keamanan Produksi
Daftar periksa sebelum & sesudah rilis. [P0] wajib, [P1] penting, [P2] peningkatan.
Autentikasi & Sesi
[P0]Password di-hash Argon2id/bcrypt; tak ada MD5/SHA polos.[P0]Cookie sesiHttpOnly; Secure; SameSite; sesi punya masa berlaku.[P0]Pesan gagal login generik; rate limit login aktif.[P1]2FA tersedia; passkey/TOTP untuk akun & admin.[P1]JWT mengunci algoritma; refresh token bisa dicabut.
Otorisasi & Data
[P0]Otorisasi dicek server-side tiap request; default deny.[P0]Query dibatasi ke pemilik/tenant (anti-IDOR).[P0]Isolasi multi-tenant diverifikasi (RLS/scope global).[P1]PII sensitif terenkripsi at-rest; masking di penyajian.
Transport & Kripto
[P0]TLS 1.2+ dipaksa; HTTP redirect ke HTTPS; HSTS aktif.[P0]Kunci di KMS/vault; tak ada kunci hardcoded.[P1]Rotasi kunci & secret terjadwal; backup kunci teruji.
Input & Aplikasi
[P0]Query parameterized/ORM (anti-SQLi).[P0]Output di-escape kontekstual; CSP aktif (anti-XSS).[P0]Validasi skema allowlist di server; tolak field tak dikenal.[P1]Proteksi CSRF & SSRF; upload file diamankan.[P1]Security headers lengkap terpasang.
Secrets & Konfigurasi
[P0]Nol secret di repo;.envdi-gitignore; scanner di CI.[P0]Pesan error produksi tidak membocorkan detail internal.[P1]Konfigurasi cloud diperiksa (tak ada bucket publik tak sengaja).
Operasi, Rantai Pasok & IR
[P0]Dependency dipindai (SCA) di CI; lockfile di-commit.[P0]Audit trail untuk aksi penting; log tanpa rahasia/PII mentah.[P0]Backup terjadwal & restore-nya teruji.[P1]Rate limit & monitoring/alert anomali aktif.[P1]Rencana Incident Response tertulis & kontak darurat siap.[P2]SBOM dihasilkan; pentest berkala dijadwalkan.
Tentang Penulis
Galih Prasetyo
Praktisi rekayasa perangkat lunak dengan minat pada keamanan aplikasi dan perlindungan data. Buku ini adalah usaha menuliskan hal-hal yang seharusnya diketahui setiap pembangun aplikasi sebelum menjual produknya — bukan sebagai teori akademis, melainkan sebagai praktik yang bisa langsung diterapkan.
- Judul
- Keamanan Aplikasi & Data — Bikin App yang Aman, Siap Dijual & Tahan Serangan
- Penulis
- Galih Prasetyo
- Edisi
- Pertama, 2026
- Bahasa
- Indonesia
- Pembaca
- Semua level — dan dapat dibaca AI
- Cakupan
- 16 bab + alat audit interaktif
Untuk melangkah lebih jauh
Buku ini adalah fondasi, bukan garis akhir. Rujukan berikut memperdalam setiap tema dan diperbarui secara berkala oleh komunitas:
- OWASP — Top 10, Cheat Sheet Series, Application Security Verification Standard (ASVS), dan Testing Guide.
- NIST — SP 800-63 (identitas digital), SP 800-61 (incident handling), Cybersecurity Framework.
- UU PDP — UU No. 27 Tahun 2022 beserta aturan turunannya untuk kepatuhan data pribadi.
- CIS Benchmarks — panduan hardening konkret per platform & layanan cloud.
- MITRE ATT&CK — basis pengetahuan taktik & teknik penyerang untuk memperkaya model ancaman.
Jadwalkan tinjauan keamanan rutin — bulanan atau per rilis besar. Jalankan ulang Audit Keamanan, perbarui dependency, tinjau log & akses, dan latih satu skenario incident response. Keamanan yang dirawat sedikit demi sedikit jauh mengungguli upaya heroik sesekali.
— Selesai — Bangun sesuatu yang layak dipercaya. —
