Cookie Aman di PHP — Membiarkan Browser Membawa Lebih Sedikit Risiko
Cookie terlihat kecil, tetapi punya sifat yang tidak biasa: setelah menerimanya, browser dapat melampirkannya secara otomatis pada request berikutnya. Ini praktis untuk session login, tetapi kenyamanan itu berarti aplikasi harus jelas menentukan ke mana cookie boleh dikirim dan kode mana yang boleh mengaksesnya.
Untuk aplikasi PHP kecil, pertanyaan yang berguna bukan “flag keamanan mana yang harus aku salin?” Pertanyaannya adalah: “perilaku apa yang dibutuhkan, dan seberapa sempit cakupan yang masih mendukungnya?” Spesifikasi cookie, dokumentasi PHP setcookie(), dan panduan keamanan dari OWASP memberi titik awal yang baik.
Apa yang Sebenarnya Dilakukan Session Cookie?
HTTP tidak mengingat pengguna di antara request dengan sendirinya. Server dapat mengirim header response Set-Cookie, lalu browser dapat mengembalikan nilainya dalam header request Cookie. Dalam desain session yang umum, nilai tersebut adalah identifier opak. Server menyimpan pengguna, waktu kedaluwarsa, dan status otorisasi secara terpisah, kemudian mencari identifier itu pada setiap request.
Pembedaan ini penting. Cookie bukan otomatis password, container terenkripsi, atau bukti bahwa sebuah request tepercaya. Biasanya cookie adalah pembawa state: siapa pun yang menunjukkan session identifier yang masih valid dapat dianggap sebagai pemilik session sampai server menolak atau mengakhirinya. Atribut cookie mengurangi beberapa cara identifier dapat bocor atau terkirim. Atribut tersebut tidak membuat identifier yang dicuri menjadi tidak berguna.
Mulai dengan Secure dan HttpOnly
Secure memberi tahu browser agar hanya mengirim cookie melalui koneksi aman, biasanya HTTPS. Ini membantu mencegah session identifier terkirim melalui request HTTP yang tidak terenkripsi. Atribut ini tidak mengenkripsi nilai di setiap tempat nilai tersebut mungkin berada, dan tidak memperbaiki aplikasi yang menerima session identifier yang dikendalikan penyerang.
HttpOnly mencegah script browser biasa membaca cookie melalui API seperti document.cookie. Ini berguna jika cookie berisi session identifier. Namun, ini bukan obat untuk XSS. Script yang disisipkan masih dapat membuat request dari browser korban, dan browser dapat melampirkan cookie HttpOnly ke request tersebut. Flag ini melindungi keterbacaan langsung cookie, bukan semua tindakan yang tersedia bagi kode yang berjalan di halaman.
Kedua atribut ini menyelesaikan masalah yang berbeda dan dapat dipakai bersama:
Set-Cookie: __Host-Session=opaque-value; Secure; HttpOnly; SameSite=Lax; Path=/
Nilai di atas hanya contoh bentuk header. Itu bukan implementasi session lengkap. Server tetap perlu membuat identifier yang sulit ditebak, memvalidasinya, menegakkan otorisasi, menggantinya setelah autentikasi, dan membatalkannya saat logout atau kedaluwarsa.
Pilih SameSite dengan Sengaja
Atribut SameSite mengatur apakah browser melampirkan cookie pada request lintas situs. “Same-site” dan “same-origin” berkaitan, tetapi tidak dapat dipertukarkan. Dokumen RFC 6265bis saat ini masih berupa Internet-Draft, jadi detailnya tidak boleh dianggap sebagai RFC final. Penjelasannya sejalan dengan dokumentasi browser saat ini mengenai tiga nilai yang umum:
Strictadalah pilihan paling sempit. Cookie tidak dikirim pada request lintas situs, yang dapat menyulitkan ketika pengguna datang dari situs lain.Laxmengizinkan request same-site dan sebagian navigasi tingkat atas dari situs lain, tetapi mengecualikan banyak request subresource dan method yang tidak aman.Nonemengizinkan pengiriman lintas situs dan harus digabungkan denganSecure. Gunakan hanya jika perilaku lintas situs memang dibutuhkan, bukan sebagai default untuk menghilangkan error integrasi.
Untuk session login first-party biasa, Strict atau Lax biasanya menjadi titik awal yang lebih terbatas daripada None. Pilihan yang tepat bergantung pada kebutuhan navigasi dan integrasi aplikasi. OWASP menjelaskan SameSite sebagai defense in depth untuk CSRF, bukan pengganti token CSRF yang diperiksa server. Endpoint yang mengubah state tetap harus memeriksa perlindungan CSRF dan otorisasi yang sesuai.
Jaga Domain dan Path Tetap Kecil
Jika Domain dihilangkan, cookie bersifat host-only: cookie ditujukan untuk host yang mengaturnya. Menambahkan Domain=example.com memperluas pengiriman ke subdomain seperti admin.example.com dan shop.example.com. Ini mungkin dibutuhkan aplikasi bersama, tetapi juga membuat subdomain yang lebih lemah menjadi lebih relevan terhadap keamanan cookie.
Cakupan terkecil yang masih berguna biasanya merupakan default yang lebih aman. Jangan menambahkan atribut Domain hanya karena contoh di internet menyertakannya. Jika session hanya milik app.example.com, mengizinkan semua subdomain menerima cookie memperbesar dampak tanpa menambah manfaat.
Path mengatur path URL mana yang menerima cookie. Atribut ini dapat mengurangi pengiriman yang tidak perlu, tetapi bukan batas akses. Kode pada host yang sama masih dapat berinteraksi dengan path lain, dan model cookie browser memiliki edge case historis. Perlakukan Path sebagai cakupan routing dan penghematan bandwidth, bukan pengganti otorisasi.
Jangan Mengandalkan Penutupan Browser untuk Kedaluwarsa
Cookie tanpa Expires atau Max-Age sering disebut session cookie. Nama itu dapat menyesatkan: fitur pemulihan session browser dapat mempertahankannya saat browser ditutup dan dibuka kembali. Jika session harus kedaluwarsa setelah periode tertentu, tegakkan masa berlaku itu di server juga.
Untuk cookie persisten, Max-Age menyatakan durasi dan mengalahkan Expires jika keduanya ada. Untuk menghapus cookie, server harus mengirim cookie baru dengan nama dan cakupan yang sama, lalu menetapkan waktu kedaluwarsa di masa lalu atau Max-Age non-positif. Menghapus salinan di browser tidak membatalkan salinan yang sudah diperoleh penyerang; server harus mencabut atau mengakhiri record session terkait.
Gunakan Options Array PHP
PHP mendukung bentuk options array untuk setcookie() sejak PHP 7.3. Manual resmi mendokumentasikan opsi expires, path, domain, secure, httponly, dan samesite. Pemanggilannya harus dilakukan sebelum output karena fungsi ini menghasilkan header response HTTP.
<?php
$sessionId = generate_session_id(); // Gunakan implementasi berbasis CSPRNG.
setcookie('__Host-Session', $sessionId, [
'expires' => 0, // Session cookie; expiry juga harus ditegakkan di server.
'path' => '/',
'secure' => true,
'httponly' => true,
'samesite' => 'Lax',
]);
Nama generate_session_id() sengaja merupakan placeholder, bukan fungsi PHP bawaan. Aplikasi harus memakai generator yang aman secara kriptografis atau mekanisme session framework yang sudah mapan. Jangan memasukkan username, role, alamat email, atau data sensitif lain langsung ke cookie hanya karena browser dapat menyimpannya.
Prefix __Host- menambahkan persyaratan yang ditegakkan browser yang mendukung prefix cookie: cookie harus memakai Secure, harus memiliki Path=/, dan tidak boleh memiliki atribut Domain. Prefix ini berguna sebagai defense in depth, tetapi bukan pengganti pengujian dukungan browser atau kontrol session di server.
Hal yang Tidak Diselesaikan Contoh Ini
Baris Set-Cookie yang terlihat aman masih dapat berada di dalam aplikasi yang tidak aman. Server harus membandingkan identifier yang diterima dengan record session di server, menolak session yang kedaluwarsa atau dicabut, dan membuat ulang identifier setelah login atau perubahan privilege untuk mengurangi risiko session fixation. Server juga sebaiknya tidak menerima session identifier di URL, karena dapat muncul di history, log, bookmark, atau data referrer.
Ada batas penting lain: HttpOnly tidak menghentikan XSS, SameSite tidak menggantikan validasi CSRF, dan Secure tidak membuat endpoint HTTP aman. Semua ini adalah lapisan. Aplikasi tetap membutuhkan output encoding, penanganan input, pemeriksaan otorisasi, TLS sepanjang session, dan jalur logout yang membatalkan state di server.
Checklist Review Singkat
- Apakah nilai session bersifat opak dan dibuat dengan mekanisme kriptografis yang sesuai?
- Apakah session cookie memakai
SecuredanHttpOnlyketika akses JavaScript tidak diperlukan? - Apakah
SameSitedipilih secara eksplisit berdasarkan perilaku lintas situs yang benar-benar dibutuhkan? - Apakah
Domaindihilangkan kecuali berbagi antar-subdomain memang disengaja? - Apakah
Pathdiperlakukan sebagai cakupan pengiriman, bukan otorisasi? - Apakah kedaluwarsa, rotasi, logout, dan pencabutan ditegakkan di server?
- Apakah perlindungan CSRF dan XSS diterapkan secara terpisah?
- Apakah response penghapusan cookie memakai nama, Domain, dan Path yang sama dengan cookie yang dihapus?
Kesimpulan
Keamanan cookie pada dasarnya adalah latihan membatasi perilaku otomatis. Secure membatasi transport, HttpOnly membatasi visibilitas script, SameSite membatasi pengiriman lintas situs, dan host serta path yang sempit membatasi penerima yang dituju. Tidak satu pun atribut ini dapat memberi tahu server apakah request berotorisasi atau session yang dicuri masih sah.
Untuk aplikasi PHP kecil, tujuan yang jujur bukan membuat cookie ajaib yang menyelesaikan keamanan web. Tujuannya adalah membuat pembawa session opak dengan cakupan yang sengaja dibatasi, lalu mengelilinginya dengan pemeriksaan independen. Desain ini mungkin kurang mengesankan daripada menambahkan flag lain, tetapi setiap kontrol memiliki tugas yang dapat ditinjau.
References
- RFC 6265: HTTP State Management Mechanism, IETF/RFC Editor.
- Cookies: HTTP State Management Mechanism, draft-ietf-httpbis-rfc6265bis-latest, IETF HTTP Working Group. Ini masih work in progress, bukan RFC final.
- PHP: setcookie, PHP Documentation Group.
- Set-Cookie header, MDN Web Docs.
- Using HTTP cookies, MDN Web Docs.
- Session Management Cheat Sheet, OWASP Cheat Sheet Series.
