Mengamankan database dari akses tak berhak: prinsip least privilege dengan role hierarchy, grants yang presisi, autentikasi scram-sha-256 menggantikan md5, TLS untuk trafik, enkripsi data-at-rest, dan aktivasi audit trail — karena database adalah kastil tempat harta paling berharga perusahaan tinggal

Di episode 8 database kalian selalu tersedia. Sekarang pertanyaannya: tersedia untuk siapa? Database adalah kastil tempat harta paling berharga perusahaan tinggal — dan keamanannya bukan pilihan, melainkan syarat kerja. Episode ini membangun pertahanan berlapis: least privilege, autentikasi kuat, TLS, enkripsi, dan audit. Semua adalah keterampilan yang langsung ditanyakan di interview DBA.
Prinsip yang memandu semuanya: asumsikan kredensial akan bocor. Desain akses sedemikian rupa sehingga ketika satu akun kompromi, kerusakannya dibatasi — bukan satu akun bisa menjatuhkan seluruh database.
Least privilege artinya: setiap akun mendapat hanya akses minimum untuk menjalankan tugasnya. Fondasi praktisnya di PostgreSQL adalah hierarki role:
-- role kelompok (grup), bukan user
CREATE ROLE app_readonly;
CREATE ROLE app_readwrite;
GRANT CONNECT ON DATABASE appdb TO app_readonly;
GRANT USAGE ON SCHEMA public TO app_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO app_readonly;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT ON TABLES TO app_readonly;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_readwrite;
ALTER DEFAULT PRIVILEGES IN SCHEMA public
GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_readwrite;
-- user aplikasi hanya bergabung ke grup yang dibutuhkan
CREATE ROLE order_service LOGIN PASSWORD 'Kuat#2026';
GRANT app_readwrite TO order_service;
-- akun reporting hanya baca
CREATE ROLE bi_user LOGIN PASSWORD 'ReadOnly#2026';
GRANT app_readonly TO bi_user;Pola ini mudah dirawat: ubah sekali di grup, semua anggota terpengaruh. Tambahkan role baru tanpa mengubah hak per user. Di MySQL padanannya: CREATE ROLE 'app_readwrite'; GRANT SELECT, INSERT, UPDATE, DELETE ON appdb.* TO 'app_readwrite'; GRANT 'app_readwrite' TO 'order_service'@'10.0.0.%';.
md5 autentikasi sudah usang dan bisa diserang. Standar modern: scram-sha-256. Pastikan di pg_hba.conf:
host all all 10.0.0.0/8 scram-sha-256
hostssl all all 0.0.0.0/0 scram-sha-256Perbedaan dua baris di atas penting: baris kedua mewajibkan hostssl — koneksi tanpa TLS ditolak. Untuk MySQL, gunakan plugin caching_sha2_password (default sejak 8.0) yang setara modernnya, dan wajibkan TLS (episode 18).
Aturan DBA yang tegas: tidak ada trust di jaringan non-lokal, tidak ada md5, dan password dikirim hanya di atas TLS.
Enkripsi saat transit (in transit) mencegah penyadapan. Aktifkan SSL di PostgreSQL:
ssl = on
ssl_cert_file = '/etc/postgresql/certs/server.crt'
ssl_key_file = '/etc/postgresql/certs/server.key'
ssl_min_protocol_version = 'TLSv1.2'Tandai semua user agar wajib SSL:
ALTER ROLE app_user SET ssl = true;Verifikasi dari client:
psql "host=10.0.0.11 dbname=appdb user=app_user sslmode=require" \
-c "SHOW ssl;"
# output: onJangan lupa izinkan hanya koneksi hostssl di pg_hba.conf (baris sebelumnya) supaya opsi tanpa TLS tidak ada.
Enkripsi in transit melindungi kabel; enkripsi at rest melindungi disk. Jika hard drive dicuri atau file DB dibajak, tanpa kunci, data tidak terbaca. Tiga lapis pendekatan:
pgcrypto:CREATE EXTENSION IF NOT EXISTS pgcrypto;
-- simpan hash satu arah untuk kredensial
UPDATE users SET password_hash =
crypt('passkuat', gen_salt('bf', 12)) WHERE id = 1;
SELECT id FROM users
WHERE password_hash = crypt('passkuat', password_hash);Aturan emas: encryption without key management is just obfuscation — kunci harus terpisah dari data (KMS / vault), bukan menempel di konfigurasi server yang sama. Detail menyeluruh di episode 19.
Keamanan tanpa audit seperti CCTV mati — siapa yang berbuat apa tidak tercatat. Dua hal yang wajib:
1. Siapa yang masuk: aktifkan log koneksi.
log_connections = on
log_disconnections = on
log_line_prefix = '%m [%p] %u@%d '2. Siapa yang mengubah apa: gunakan ekstensi audit (pgaudit) untuk mencatat pernyataan DML/DDL:
CREATE EXTENSION IF NOT EXISTS pgaudit;
ALTER SYSTEM SET pgaudit.log = 'write, ddl';
SELECT pg_reload_conf();MySQL setara dengan binlog (episode 2) plus general_log/audit plugin. Ingat: simpan log audit di lokasi terpisah dari database — log audit yang ikut hilang saat server diretas tidak berguna.
Important
Regulasi seperti GDPR, UU PDP Indonesia, dan PCI-DSS mewajibkan kontrol ini — dan menuntut bukti. Audit trail yang aktif adalah kartu as kalian saat audit pihak ketiga bertanya "siapa yang akses data nasabah tadi malam?". Hubungan keamanan dan compliance ini kita dalamkan di episode 16.
Setelah episode ini, jalankan checklist sebelum menyebut server "production-ready":
postgres/root yang dipakai aplikasi; aplikasi memakai role CRUD minimal.pg_hba.conf/bind-address hanya membuka subnet yang dibutuhkan.scram-sha-256 (PostgreSQL) / caching_sha2_password (MySQL); tidak ada trust/md5.hostssl, ssl_min_protocol_version = 'TLSv1.2').log_connections + pgaudit aktif; log disimpan terpisah.Inti yang harus dibawa pulang:
scram-sha-256; larang md5 dan trust.hostssl), enkripsi disk untuk at rest, dan kunci terpisah dari data.log_connections, pgaudit) wajib dan disimpan di lokasi berbeda.Di episode 10 selanjutnya kita merencanakan masa depan: capacity planning & scaling — kapan naik vertikal (bigger instance) vs horizontal (replicas/sharding), strategi read replicas, dan membuat rencana kapasitas yang didasarkan data, bukan perasaan. Sampai jumpa di episode 10!