Insecure deserialization terjadi kalau aplikasi mengubah data dari sumber yang gak dipercaya (cookie, body, header) kembali menjadi object pakai fungsi "native" yang bukan cuma membaca data, tapi juga membangun ulang object dan menjalankan method-nya. Bayangin paket pos: isi kardusnya bisa sekadar barang, atau bisa berupa robot yang begitu kardusnya dibuka langsung bergerak sendiri. json_decode membuka kardus berisi barang biasa; unserialize dan pickle.loads membuka kardus yang isinya bisa bergerak begitu dibuka.
🧪 Tebakan cepat: mana yang lebih berbahaya untuk data dari cookie,
json_decode($cookie, true)atauunserialize($cookie)? Tebak dulu, jawabannya ada di bagian "Kenapa deserialization native berbahaya".
Serialization dan deserialization
Serialization mengubah object jadi teks atau bytes supaya bisa disimpan atau dikirim. Deserialization kebalikannya: dari teks kembali jadi object. Masalahnya bukan pada ide ini, tapi pada siapa yang mengirim datanya dan fungsi apa yang dipakai untuk membacanya kembali.
- JSON (
json_decode,JSON.parse): hasilnya cuma data biasa, string, number, array, object polos. Gak membangun class dan gak menjalankan method. Aman untuk transport data. - Native serialization (
unserializedi PHP,pickle.loadsdi Python,ObjectInputStream.readObjectdi Java,Marshal.loaddi Ruby,yaml.loadtanpa SafeLoader): bisa membangun ulang object dari class apa pun, dan proses itu bisa memicu kode.
Kenapa deserialization native berbahaya
Lihat satu baris yang membaca preferensi user dari cookie:
// RENTAN: cookie dikontrol user
$pref = unserialize($_COOKIE["pref"]);
Cookie dikontrol user, jadi isinya bisa apa saja. unserialize gak sekadar membaca; dia membangun ulang object sesuai yang tertulis di teksnya, termasuk class dan nilainya. Saat object dibangun atau dihancurkan, PHP otomatis memanggil "magic method" seperti __wakeup (dipanggil saat object dipulihkan) dan __destruct (dipanggil saat object dibuang). Kalau di dalam kode ada class yang magic method-nya melakukan sesuatu yang kuat (menulis file, menjalankan perintah), penyerang bisa menyusun teks yang mengarah ke situ. Jadi isi cookie yang menentukan object mana yang dibangun dan method apa yang jalan.
Bandingkan dengan json_decode($_COOKIE["pref"], true): hasilnya cuma array biasa. Gak ada class yang dibangun, gak ada method yang jalan. Itu jawaban tebakannya: unserialize jauh lebih berbahaya, karena dia bisa membangun object dan memicu kode, sementara json_decode cuma menghasilkan data.
Perbaikan: JSON plus validasi
Aturan pertama: jangan deserialisasi data dari user dengan fungsi native. Untuk transport data, pakai JSON, lalu periksa isinya sebelum dipakai:
$data = json_decode($_COOKIE["pref"], true);
if (!is_array($data) || !isset($data["theme"])) {
throw new InvalidArgumentException("Cookie tidak valid");
}
$theme = in_array($data["theme"], ["light", "dark"], true) ? $data["theme"] : "light";
json_decode(..., true)menghasilkan array biasa, bukan object berisi kode.- Validasi memeriksa bentuk dan isinya: harus array, harus punya
theme, danthemeharus salah satu nilai yang diizinkan. Data yang aneh ditolak sebelum dipakai.
Di Python, pola yang sama: json.loads(...) menggantikan pickle.loads(...). Di YAML, pakai yaml.safe_load(...), bukan yaml.load(...), karena safe_load cuma membaca data dan gak membangun object sembarang class.
Kalau terpaksa pakai format binary
Kadang kamu memang butuh menyimpan object, bukan cuma data. Kalau begitu:
- Tanda tangani datanya. Lampirkan tanda tangan (HMAC) atau enkripsi, jadi penyerang gak bisa mengubah isinya tanpa memecahkan kunci. Di Laravel,
Crypt::encryptString(serialize($obj))melakukan ini. - Whitelist class. Di PHP 7+,
unserialize($data, ["allowed_classes" => [MyDto::class]])cuma mengizinkan class tertentu, jadi class lain gak bisa dibangun. Di Java, pasang filter class yang diizinkan. - Catat dan batasi. Log tiap panggilan deserialization, dan jalankan dengan hak akses sekecil mungkin.
⚠️ Jebakan yang sering ketemu
unserializelangsung dari$_COOKIE,$_GET, atau$_POST. Semua itu dikontrol user. Pakai JSON plus validasi.yaml.loaddikira sama denganyaml.safe_load.yaml.loadtanpa SafeLoader bisa membangun object sembarang. Selalu pakaisafe_load.- Mengira JSON pasti aman tanpa validasi. JSON gak menjalankan kode, tapi isinya tetap dari user. Periksa bentuk dan nilainya sebelum dipakai.
- Percaya data binary karena "susah dibaca manusia". Susah dibaca bukan berarti aman. Penyerang punya alat untuk menyusunnya. Tanda tangani kalau harus pakai.
TL;DR: Insecure deserialization terjadi kalau data dari user dibaca dengan fungsi native yang membangun ulang object dan memicu kode, seperti unserialize, pickle.loads, ObjectInputStream.readObject, Marshal.load, atau yaml.load. Dengan unserialize($_COOKIE["pref"]), isi cookie menentukan object mana yang dibangun dan magic method (__wakeup, __destruct) apa yang jalan. Cegah dengan JSON plus validasi: json_decode, lalu periksa bentuk dan nilainya; di Python json.loads, di YAML yaml.safe_load. Kalau terpaksa pakai format binary, tanda tangani datanya dan whitelist class yang boleh dibangun.