Model keamanan SQLite bertumpu pada satu fakta: database adalah berkas biasa di sistem operasi. Siapa pun yang bisa membaca berkas itu bisa membaca seluruh data, dan siapa pun yang bisa menulis bisa mengubah atau menghapus isinya. Chapter ini membahas cara melihat izin berkas, mengapa isi toko.db ternyata bisa dibaca tanpa SQLite sama sekali, berkas samping apa saja yang ikut menyimpan data, lalu menutup semuanya dengan chmod.

Izin Berkas adalah Pagar Utama #

Database server seperti PostgreSQL punya akun pengguna dan perintah GRANT. SQLite tidak — izin berkas di sistem operasi adalah satu-satunya pagar antara data dan pengguna lain di mesin yang sama.

Kita pakai toko.db berisi tabel pelanggan. Karena ukuran berkas akan kita periksa dengan ls -l, contoh ini dimulai dari berkas yang dibuat baru — jalankan rm -f toko.db* lebih dulu bila Anda melanjutkan dari chapter sebelumnya:

bash
rm -f toko.db*
sqlite3 toko.db "CREATE TABLE pelanggan (id INTEGER PRIMARY KEY, nama TEXT NOT NULL, email TEXT NOT NULL);
INSERT INTO pelanggan VALUES (1,'Budi','[email protected]'),(2,'Siti','[email protected]');"
ls -l toko.db

Output yang Dihasilkan:

text
-rw-r--r-- 1 z z 8192 Oct 13 06:15 toko.db

Semua demo ls -l pada chapter ini dijalankan di mesin penulis — kolom nama pemilik (z z), tanggal, dan jam di komputer Anda akan berbeda. Yang penting adalah sepuluh karakter pertama: -rw-r--r--.

Rincian izin tersebut:

  1. Tanda - pertama: berkas biasa, bukan direktori.
  2. rw- (pemilik): pemilik boleh membaca dan menulis.
  3. r-- (grup): anggota grup hanya boleh membaca.
  4. r-- (pengguna lain): semua pengguna lain di mesin itu juga bisa membaca.

Dengan izin 644, akun lain di komputer yang sama bisa menyalin toko.db dan membaca seluruh isi tabel pelanggan. Umum yang diberlakukan umask bawaan memang menghasilkan 644 — kecenderungan ini perlu kita ubah untuk berkas database, tetapi sebelum itu, mari lihat seberapa banyak yang sebenarnya bocor.

Isinya Teks Biasa, Bisa Dibaca Tanpa SQLite #

Izin 644 berbahaya karena isi berkas SQLite menyimpan data dalam bentuk yang bisa ditemukan dengan alat seadanya. Enam belas byte pertama berkas adalah header format:

bash
head -c 16 toko.db | od -c

Output yang Dihasilkan:

text
0000000   S   Q   L   i   t   e       f   o   r   m   a   t       3  \0
0000020

Yang lebih penting: nilai teks tersimpan apa adanya, tanpa enkripsi. grep dengan opsi -a (perlakukan berkas biner sebagai teks) cukup untuk membuktikannya:

bash
grep -ao '[email protected]' toko.db
grep -ac 'Budi' toko.db

Output yang Dihasilkan:

text

Siapa pun yang berhasil menyalin berkas ini — lewat akses baca, folder bersama, atau sinkronisasi cloud — tidak membutuhkan kredensial SQLite apa pun untuk membaca datanya.

Berkas Samping: Journal dan WAL #

SQLite membuat berkas sementara di sekitar database utama, dan berkas-berkas itu juga memuat data. Saat transaction berjalan di journal mode bawaan (DELETE), rollback journal muncul sebentar:

bash
( echo "BEGIN;";
  echo "INSERT INTO pelanggan VALUES (3,'Ani','[email protected]');";
  sleep 2
  echo "COMMIT;" ) | sqlite3 toko.db &
sleep 1
ls -l toko.db*
wait
ls -l toko.db*

Output yang Dihasilkan:

text
-rw-r--r-- 1 z z 8192 Oct 13 06:15 toko.db
-rw-r--r-- 1 z z 4616 Oct 13 06:15 toko.db-journal
-rw-r--r-- 1 z z 8192 Oct 13 06:15 toko.db

Berkas toko.db-journal berisi salinan halaman lama sebelum perubahan — izinnya sama longgarnya dengan berkas utama, lalu hilang setelah COMMIT. Pada WAL mode, dua berkas samping berumur lebih panjang:

bash
( echo "PRAGMA journal_mode=WAL;";
  echo "INSERT INTO pelanggan VALUES (4,'Tono','[email protected]');";
  sleep 2 ) | sqlite3 toko.db &
sleep 1
ls -l toko.db*
grep -ao '[email protected]' toko.db-wal
wait
ls -l toko.db*

Output yang Dihasilkan:

text
wal
-rw-r--r-- 1 z z 8192 Oct 13 06:15 toko.db
-rw-r--r-- 1 z z 32768 Oct 13 06:15 toko.db-shm
-rw-r--r-- 1 z z 4152 Oct 13 06:15 toko.db-wal
[email protected]
-rw-r--r-- 1 z z 8192 Oct 13 06:15 toko.db

Dua hal penting dari hasil di atas. Pertama, baris Tono belum ada di toko.db — data terbaru justru berada di toko.db-wal sebelum checkpoint, dan grep membuktikan berkas itu bocor dengan izin 644. Kedua, toko.db-wal dan toko.db-shm lenyap ketika koneksi terakhir menutup dengan bersih. Pembahasan lengkapnya ada pada Chapter Journal Modes dan WAL di section Transaksi dan Konsistensi.

Setelah semua titik bocor terlihat, penutupnya sederhana: untuk database milik satu pengguna atau satu aplikasi, izin 600 adalah titik awal yang wajar — hanya pemilik yang membaca dan menulis:

bash
chmod 600 toko.db
ls -l toko.db

Output yang Dihasilkan:

text
-rw------- 1 z z 8192 Oct 13 06:15 toko.db

Sekarang hanya pemilik berkas yang bisa membaca dan menulis. SQLite menyalin izin berkas utama ke berkas -wal dan -shm pada saat dibuat, jadi chmod 600 yang dipasang sejak awal membuat berkas samping berikutnya ikut ketat — tetapi audit izin tetap perlu memakai pola toko.db*, bukan hanya toko.db, atau setel umask yang ketat sebelum aplikasi mulai menulis.

Di server produksi, prinsipnya sama tetapi pemiliknya bukan manusia: jalankan aplikasi dengan akun sistem khusus, lalu chown berkas database ke akun tersebut dan berikan izin 600 — akun lain, termasuk akun login Anda, tidak perlu menyentuh berkas itu.

Risiko Khusus Web Hosting #

Kesalahan klasik aplikasi web kecil adalah meletakkan toko.db di dalam webroot, misalnya public_html/toko.db. Pengunjung cukup membuka https://example.com/toko.db dan browser mengunduh seluruh database — ekstensi .db bukan penghalang bagi web server.

Solusi terbaik: simpan database di luar webroot, misalnya ../data/toko.db relatif terhadap public_html/. Jika struktur aplikasi memaksa database berada di dalam webroot, tolak akses langsung lewat konfigurasi server. Untuk nginx:

text
location ~ \.db$ {
    deny all;
}

Untuk Apache, berkas .htaccess di folder yang sama:

text
<FilesMatch "\.db$">
    Require all denied
</FilesMatch>

Backup Mewarisi Semua Risiko #

Setiap salinan database membawa datanya ke titik izin baru: folder backup, berkas terkompresi, unduhan komputer lain, atau penyimpanan cloud. cp toko.db backup-toko.db menghasilkan berkas baru yang izin dan lokasinya bisa saja lebih longgar dari aslinya. Perlakukan setiap salinan dengan disiplin yang sama: izin minimal, lokasi terkendali, dan enkripsi untuk salinan yang keluar dari mesin — topik Chapter Enkripsi Database berikutnya.