Kenapa backup offsite masih bisa ikut hilang
~/.aws/credentials atau ~/.config/rclone/rclone.conf mudah dikenali dan isinya berupa teks biasa.
Insiden Pusat Data Nasional Sementara (PDNS) 2 di Surabaya pada Juni 2024 dengan varian Brain Cipher menjadi contoh yang sering dibahas. Salah satu kelemahan yang disorot adalah banyak sistem terdampak tidak punya strategi backup yang memadai atau tidak menerapkan air-gapped backup, yaitu backup yang terisolasi sehingga tidak bisa dijangkau dari sistem yang terinfeksi. Untuk skala server biasa, prinsipnya sama: backup yang bisa dihapus dari server produksi bukan backup yang terisolasi.
Selain penghapusan langsung, ada juga jalur yang kurang disadari. rclone sync menghapus file di tujuan yang tidak ada lagi di sumber, jadi kalau sumbernya sudah terenkripsi, sync berikutnya bisa ikut menimpa atau membersihkan backup. Begitu juga restic forget --prune, yang butuh izin hapus untuk membuang data lama. Kalau perintah itu dijalankan dari server produksi, berarti server produksi memegang izin hapus.
Di meja support, pertanyaan "backup kami kan di cloud, aman kan?" sering baru muncul setelah server terinfeksi. Pengecekan di bawah sebaiknya dijalankan jauh sebelum itu.
Langkah 1: inventaris credential yang tersimpan di server
BASH
# Daftar file credential yang umum. grep -l hanya menampilkan nama file, bukan isinya
ls -la /root/.aws/ /root/.config/rclone/ 2>/dev/null
grep -rlE 'secret_access_key|RESTIC_PASSWORD|B2_ACCOUNT_KEY' /root /home /etc /usr/local/bin 2>/dev/null
# Cron yang menjalankan backup, untuk tahu script mana yang memakai credential tadi
crontab -l -u root
ls -la /etc/cron.d/
# Identitas (user atau role) yang dipakai credential default AWS CLI
aws sts get-caller-identitygrep -l supaya isi secret tidak tercetak ke terminal atau ke history log sesi. Jangan menempelkan isi file credential ke tiket atau chat.
Perhatikan dua hal dari hasilnya. Pertama, jumlah lokasi credential. Sering ternyata ada lebih dari satu, misalnya key lama yang tertinggal di script yang sudah tidak dipakai. Kedua, ARN dari get-caller-identity. Catat nama user atau role-nya, karena itu yang akan dicek izinnya di langkah berikut.
rclone memang mendukung enkripsi file config, tapi untuk backup otomatis, password config-nya biasanya juga harus tersimpan di server supaya cron bisa jalan. Jadi enkripsi config tidak banyak membantu kalau penyerangnya sudah root.
Langkah 2: cek izin dan retensi dari workstation admin
BASH
# Jalankan dari workstation admin, BUKAN dari server produksi
# Ganti backup-web01 dengan nama user dari hasil get-caller-identity
aws iam list-attached-user-policies --user-name backup-web01
aws iam list-user-policies --user-name backup-web01
# Status versioning dan Object Lock pada bucket backup
aws s3api get-bucket-versioning --bucket backup-web01
aws s3api get-object-lock-configuration --bucket backup-web01
# Retensi pada satu file backup tertentu
aws s3api get-object-retention --bucket backup-web01 --key daily/web01-backup.tar.gz--endpoint-url sesuai dokumentasi provider. Perintah IAM di atas khusus AWS. Banyak provider lain mengatur izin per access key lewat panel, jadi cek di sana.
Saat membaca policy, cari izin yang memungkinkan data hilang: s3:DeleteObject, s3:DeleteObjectVersion, s3:PutObjectRetention, s3:BypassGovernanceRetention, s3:PutLifecycleConfiguration, s3:PutBucketPolicy, dan tentu saja wildcard seperti s3:*. Izin lifecycle sering terlewat. Dengan izin itu, penyerang tidak perlu menghapus apa pun secara langsung, cukup memasang aturan expiration dan menunggu.
Lalu pahami dulu apa itu versioning dan Object Lock. Versioning menyimpan versi lama setiap object. Dengan versioning aktif, DeleteObject tanpa version ID hanya menambahkan delete marker, sementara datanya masih ada sebagai versi lama. Yang benar-benar menghapus adalah DeleteObjectVersion. Object Lock menambahkan masa retensi: selama masa itu, versi object tidak bisa dihapus atau ditimpa.
Object Lock punya dua mode. Mode GOVERNANCE masih bisa dilewati oleh identitas yang punya izin s3:BypassGovernanceRetention. Mode COMPLIANCE di AWS tidak bisa dipersingkat atau dilepas oleh siapa pun sampai masa retensinya habis, termasuk akun root. Kalau tujuannya melindungi dari penyerang yang mungkin juga mencuri credential admin, COMPLIANCE lebih aman, dengan konsekuensi data uji atau backup yang salah juga tidak bisa dibuang sebelum waktunya.
Hasil yang kamu harapkan: versioning Enabled, Object Lock Enabled dengan default retention, dan get-object-retention pada file backup menunjukkan mode serta tanggal RetainUntilDate yang masih di masa depan. Kalau get-object-lock-configuration mengembalikan error bahwa konfigurasi tidak ada, berarti bucket itu belum punya perlindungan immutable. Di banyak implementasi, Object Lock harus diaktifkan saat bucket dibuat, jadi bisa jadi kamu perlu bucket baru.
Sebagai patokan kasar, masa retensi sebaiknya lebih panjang dari waktu yang realistis untuk menyadari ada penyusup. Retensi yang lebih pendek dari itu berarti backup bersih bisa sudah kedaluwarsa saat kamu membutuhkannya.
Langkah 3: uji hapus memakai credential server
BASH
# Jalankan DI server produksi dengan credential yang dipakai script backup
echo 'uji isolasi backup' > /tmp/uji-hapus.txt
# Upload object uji. Output put-object berisi VersionId, catat nilainya
aws s3api put-object --bucket backup-web01 --key uji-isolasi/uji-hapus.txt --body /tmp/uji-hapus.txt
# Coba hapus permanen versi tersebut. Yang diharapkan: AccessDenied
aws s3api delete-object --bucket backup-web01 --key uji-isolasi/uji-hapus.txt --version-id ISI_VERSION_ID
# Coba lewati retensi GOVERNANCE. Yang diharapkan: tetap ditolak
aws s3api delete-object --bucket backup-web01 --key uji-isolasi/uji-hapus.txt --version-id ISI_VERSION_ID --bypass-governance-retention
rm /tmp/uji-hapus.txts3:DeleteObjectVersion. Kalau hanya perintah kedua yang berhasil, retensinya GOVERNANCE dan credential server punya izin bypass, yang secara praktis sama dengan tidak terkunci.
Jangan terkecoh oleh aws s3 rm atau rclone deletefile yang sukses tanpa error. Di bucket dengan versioning, perintah itu hanya membuat delete marker, jadi "berhasil hapus" belum tentu berarti data hilang, dan "gagal hapus" juga perlu dicek ulang dengan version ID. Uji yang bermakna adalah penghapusan versi spesifik seperti di atas.
Perlu diingat, kalau default retention aktif, object uji ini akan tetap tersimpan sampai masa retensinya habis. Ukurannya kecil, tapi tetap ikut dihitung sebagai storage. Itu harga yang wajar untuk bukti bahwa kuncinya bekerja.
Uji juga jalur tidak langsung kalau memungkinkan: dari server produksi, coba aws s3api get-bucket-lifecycle-configuration dan aws s3api get-bucket-policy pada bucket backup. Credential yang hanya perlu menulis backup tidak punya alasan membaca, apalagi mengubah, konfigurasi bucket.
Kalau hasilnya belum aman
s3:PutObject dan, kalau tool backup kamu membutuhkannya, s3:ListBucket serta s3:GetObject. Semua izin hapus, retensi, lifecycle, dan policy dipindah ke credential admin yang tidak pernah disimpan di server produksi. Setelah policy diubah, rotasi access key yang lama, karena key lama bisa saja sudah tersalin.
Pekerjaan yang butuh izin hapus, seperti restic forget --prune atau pembersihan backup lama, dijalankan dari host terpisah. Untuk restic, rest-server punya mode --append-only sehingga client hanya bisa menambah snapshot. Alternatif lain adalah model pull: server backup yang terpisah menarik data dari produksi, sehingga server produksi tidak memegang credential apa pun ke tempat penyimpanan backup.
Pemisahan akses di level storage hanya satu lapisan. Kalau penyerang bisa bergerak bebas antar server, dia mungkin menemukan host admin yang memegang credential penuh. Soal membatasi pergerakan lateral di lingkungan virtualisasi dibahas di artikel hardening cluster virtualisasi terhadap ransomware.
Praktisnya, anggap setiap credential yang ada di server produksi sudah dimiliki penyerang, lalu tanyakan: dengan credential itu, apa yang bisa dia hapus? Jalankan uji hapus di atas setiap kali ada perubahan script backup, rotasi key, atau pindah provider storage. Kalau jawabannya masih "bisa menghapus", backup offsite kamu baru pindah lokasi, belum terisolasi.