Server HTTP melihat setiap permintaan sebagai kejadian yang berdiri sendiri, sedangkan aplikasi nyata membutuhkan ingatan berupa status login dan preferensi pengguna. Cookie mengisi kekosongan itu, dan karena cookie umumnya berisi pengenal sesi (session), mekanisme sekecil ini menjadi sasaran pencurian yang paling sering dicoba. Topik ini membahas cara kerja cookie, atribut yang mempersempit lingkupnya, percobaan langsung di lab, dan jalur pencurian yang ditutup oleh setiap atribut.
Cookie sebagai pengingat state pada HTTP#
Cookie adalah pasangan kunci dan nilai berukuran kecil yang dikirim server melalui header Set-Cookie, disimpan browser, lalu dikirim ulang secara otomatis pada setiap permintaan berikutnya yang cocok lingkupnya. Mekanismenya didefinisikan dalam RFC 6265 yang berjudul HTTP State Management Mechanism, sebuah nama yang menegaskan tujuannya, yaitu mengelola keadaan (state) pada protokol yang tidak mengenal keadaan. Server tidak perlu menghafal apa pun tentang klien, karena ingatan itu disimpan di sisi browser dan diserahkan kembali pada setiap permintaan.
Aplikasi memanfaatkan cookie untuk menyimpan pengenal sesi, sedangkan keadaan sesi yang sesungguhnya, misalnya identitas yang sedang login beserta hak aksesnya, disimpan di server. Pasangan itu bekerja seperti tiket: cookie hanya berisi nomor yang sulit ditebak, dan server mengembalikan detail pemegang nomor tersebut dari penyimpanannya sendiri. Pembagian peran ini bukan sekadar gaya penulisan kode, melainkan konsekuensi dari fakta bahwa isi cookie dapat dibaca dan diubah oleh pemilik perangkatnya. Keamanan sesi dengan demikian bertumpu pada kerahasiaan dan lingkup cookie yang membawa pengenal tersebut.
Atribut yang mempersempit lingkup cookie#
Header Set-Cookie dapat membawa atribut yang tidak mengubah isi cookie, melainkan mempersempit kapan dan oleh siapa cookie itu digunakan. Tabel berikut merangkum atribut utamanya.
| Atribut | Peran |
|---|---|
Path | Membatasi pengiriman hanya ke jalur yang cocok. |
Domain | Menentukan host penerima, termasuk subdomainnya bila diisi. |
Expires, Max-Age | Menentukan masa berlaku, dan tanpa keduanya cookie hilang saat browser ditutup. |
Secure | Cookie hanya dikirim melalui HTTPS. |
HttpOnly | Cookie tidak terbaca oleh JavaScript pada halaman. |
SameSite | Mengatur pengiriman pada permintaan lintas situs dengan nilai Strict, Lax, atau None. |
Skrip berikut membangun dua string Set-Cookie dengan pustaka http.cookies bawaan Python. Cookie pertama adalah cookie sesi yang atribut pelindungnya lengkap, sedangkan cookie kedua hanya preferensi tampilan yang berlaku pada jalur /pengaturan.
"""Membangun string Set-Cookie dengan atribut yang mempersempit lingkup."""
from http.cookies import SimpleCookie
def cookie_sesi(nilai):
kue = SimpleCookie()
kue["sid"] = nilai
kue["sid"]["path"] = "/"
kue["sid"]["max-age"] = 3600
kue["sid"]["httponly"] = True
kue["sid"]["secure"] = True
kue["sid"]["samesite"] = "Lax"
return kue["sid"].OutputString()
def cookie_tema():
kue = SimpleCookie()
kue["tema"] = "gelap"
kue["tema"]["path"] = "/pengaturan"
kue["tema"]["samesite"] = "Strict"
return kue["tema"].OutputString()
print(cookie_sesi("s-demo-2481"))
print(cookie_tema())sid=s-demo-2481; HttpOnly; Max-Age=3600; Path=/; SameSite=Lax; Secure
tema=gelap; Path=/pengaturan; SameSite=StrictSetiap atribut pada baris pertama menutup satu jalur pencurian. HttpOnly menghalangi skrip hasil serangan XSS membaca cookie, Secure menghalangi penyadapan pada HTTP polos, SameSite=Lax menghalangi sebagian besar skenario CSRF, sedangkan Path dan Max-Age membatasi tempat dan lama cookie beredar. Cookie kedua menunjukkan atribut minimum yang wajar untuk data nonkritikal, yaitu lingkup jalur yang sempit tanpa masa berlaku panjang.
Mencoba Set-Cookie dan pemilihan cookie di lab#
Server lab berikut melengkapi percobaan. Rute /masuk mengirim cookie sid dengan atribut pelindung lengkap, rute /pengaturan mengirim cookie tema yang terbatas jalurnya, dan rute lain menggemakan header Cookie yang benar-benar diterima. Seperti server lab pada topik HTTP sebagai Medan Serangan, berkas ini memiliki mode uji mandiri tanpa argumen dan mode layanan dengan argumen --layani.
"""Server lab untuk melihat header Set-Cookie dan pemilihan cookie oleh browser.
Rute yang dilayani:
GET /masuk mengirim cookie sid (Path=/, HttpOnly, SameSite=Lax, Max-Age=3600)
GET /pengaturan mengirim cookie tema (Path=/pengaturan, SameSite=Strict)
GET rute lain menggemakan header Cookie yang diterima
Jalankan tanpa argumen untuk uji mandiri, atau dengan argumen --layani
agar server melayani permintaan sungguhan pada http://127.0.0.1:8000/.
"""
import sys
import threading
import urllib.request
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
class PenanganKue(BaseHTTPRequestHandler):
"""Mengirim Set-Cookie pada rute tertentu dan menggemakan cookie masuk."""
protocol_version = "HTTP/1.1"
def _kirim(self, kode, tubuh, set_cookie=None):
data = tubuh.encode("utf-8")
self.send_response(kode)
if set_cookie:
self.send_header("Set-Cookie", set_cookie)
self.send_header("Content-Type", "text/plain; charset=utf-8")
self.send_header("Content-Length", str(len(data)))
self.end_headers()
self.wfile.write(data)
def do_GET(self):
if self.path == "/masuk":
self._kirim(
200,
"Login dicatat. Cookie sid dikirim.",
"sid=s-demo-2481; Path=/; HttpOnly; SameSite=Lax; Max-Age=3600",
)
elif self.path == "/pengaturan":
self._kirim(
200,
"Preferensi disimpan. Cookie tema dikirim.",
"tema=gelap; Path=/pengaturan; SameSite=Strict",
)
else:
kue = self.headers.get("Cookie") or "(tanpa Cookie)"
self._kirim(200, "Server menerima: " + kue)
def send_response(self, code, message=None):
# Header Server dan Date tidak dikirim agar keluaran lab deterministik.
self.log_request(code)
self.send_response_only(code, message)
def log_message(self, format, *args):
pass # log akses tidak dicetak agar keluaran tetap bersih
def jalankan(port=8000):
server = ThreadingHTTPServer(("127.0.0.1", port), PenanganKue)
try:
server.serve_forever()
finally:
server.server_close()
def uji_mandiri():
server = ThreadingHTTPServer(("127.0.0.1", 8000), PenanganKue)
thread = threading.Thread(target=server.serve_forever, daemon=True)
thread.start()
try:
with urllib.request.urlopen("http://127.0.0.1:8000/masuk", timeout=5) as jawaban:
print("/masuk Set-Cookie: " + jawaban.headers.get("Set-Cookie"))
with urllib.request.urlopen("http://127.0.0.1:8000/pengaturan", timeout=5) as jawaban:
print("/pengaturan Set-Cookie: " + jawaban.headers.get("Set-Cookie"))
with urllib.request.urlopen("http://127.0.0.1:8000/profil", timeout=5) as jawaban:
print("/profil " + jawaban.read().decode("utf-8"))
finally:
server.shutdown()
server.server_close()
if __name__ == "__main__":
if "--layani" in sys.argv:
jalankan()
else:
uji_mandiri()/masuk Set-Cookie: sid=s-demo-2481; Path=/; HttpOnly; SameSite=Lax; Max-Age=3600
/pengaturan Set-Cookie: tema=gelap; Path=/pengaturan; SameSite=Strict
/profil Server menerima: (tanpa Cookie)Mode layanan digunakan untuk melihat header Set-Cookie secara utuh dengan curl dan opsi -i, persis seperti cara browser membacanya. Jalankan blok berikut, lalu perhatikan header yang menyertai respons.
# jalankan server lab di latar belakang
python3 server-kue.py --layani &
PID=$!
sleep 0.5
# amati header Set-Cookie pada respons
curl -s -i "http://localhost:8000/masuk"
echo
# hentikan server lab
kill $PID
wait $PID 2>/dev/null
echo "server lab dihentikan"HTTP/1.1 200 OK
Set-Cookie: sid=s-demo-2481; Path=/; HttpOnly; SameSite=Lax; Max-Age=3600
Content-Type: text/plain; charset=utf-8
Content-Length: 34
Login dicatat. Cookie sid dikirim.
server lab dihentikanBaris Set-Cookie pada respons di atas itulah yang menyebabkan browser menyimpan cookie sid. Tidak ada yang istimewa dari curl pada proses ini, karena browser pun hanya membaca header yang sama lalu menyimpannya sesuai atributnya. Atribut Max-Age=3600 membuat cookie itu bertahan satu jam, sedangkan tanpa atribut tersebut cookie hanya berlaku selama sesi browser dan hilang setelah ditutup.
Percobaan terakhir meniru mekanisme pemilihan cookie oleh browser menggunakan berkas jar, yaitu penyimpanan cookie milik curl. Dua halaman pertama dikunjungi untuk mengumpulkan cookie, lalu dua permintaan berikutnya memperlihatkan cookie mana yang dikirim ulang untuk setiap jalur.
# jalankan server lab di latar belakang
python3 server-kue.py --layani &
PID=$!
sleep 0.5
# browser berkunjung ke dua halaman, cookie yang diterima disimpan ke jar
curl -s -c toples.txt "http://localhost:8000/masuk" > /dev/null
curl -s -b toples.txt -c toples.txt "http://localhost:8000/pengaturan" > /dev/null
# permintaan ke /profil: hanya cookie yang cocok yang dikirim ulang
curl -s -b toples.txt "http://localhost:8000/profil"
echo
# permintaan ke /pengaturan/simpan
curl -s -b toples.txt "http://localhost:8000/pengaturan/simpan"
echo
# hentikan server lab dan bersihkan jar
kill $PID
wait $PID 2>/dev/null
rm -f toples.txt
echo "server lab dihentikan"Server menerima: sid=s-demo-2481
Server menerima: tema=gelap; sid=s-demo-2481
server lab dihentikanPermintaan pertama hanya menerima sid karena cookie tema terikat pada jalur /pengaturan, sedangkan permintaan ke /pengaturan/simpan menerima keduanya. Bukti tersebut menegaskan bahwa Path benar-benar ditegakkan pada saat pengiriman, bukan sekadar anotasi. Batasnya juga terlihat sekaligus, karena sid yang berakar pada jalur / tetap terkirim ke mana pun.
Session ID sebagai sasaran pencurian#
Pencurian cookie sesi setara dengan pencurian sesi, karena pelaku cukup menyalin pengenal yang dicuri ke browsernya sendiri tanpa menyentuh kata sandi. Jalurnya ada empat dan masing-masing sudah memiliki penangkalnya. Skrip pihak ketiga yang disisipkan melalui celah XSS membaca document.cookie ketika HttpOnly absen. Penyadap pada jaringan membaca cookie ketika Secure absen. Situs jahat memanfaatkan pengiriman otomatis cookie untuk memicu aksi atas nama korban ketika SameSite absen, pola yang dikenal sebagai CSRF (cross-site request forgery). Terakhir, pengenal yang pendek atau lemah ditebak melalui percobaan berulang.
Pengelolaan sesi yang sehat menambahkan dua kebiasaan pada sisi server. Pertama, buat pengenal menggunakan pembangkit acak kriptografis seperti secrets.token_urlsafe pada Python, dengan panjang yang membuat penebakan mustahil secara praktis. Kedua, regenerasikan pengenal setiap kali tingkat akses berubah, terutama setelah login, sehingga pengenal yang ditanam penyerang sebelum login menjadi tidak berlaku, pola yang disebut session fixation. Ketentuan panjang dan rotasi pengenal dirangkum dalam Session Management Cheat Sheet milik OWASP, dan kegagalan pada area ini tercatat sebagai A07 Authentication Failures pada OWASP Top 10:2025.
Kesalahan umum#
Beberapa kekeliruan berikut sering ditemukan saat atribut cookie ditetapkan.
- Menyimpan data aplikasi di dalam cookie. Isi cookie terlampir pada setiap permintaan dan terbaca di penyimpanan browser, sehingga menaruh data pengguna di sana menambah beban permintaan sekaligus risiko kebocoran. Simpan pengenalnya saja, dan letakkan datanya di server.
- Menganggap
Pathsebagai batas keamanan. Atribut ini hanya menyaring pengiriman otomatis, sedangkan skrip dari origin yang sama tetap dapat membaca seluruh cookie halaman melaluidocument.cookietanpa memandang jalur. Isolasi antar-origin yang sesungguhnya datang dari kebijakan same-origin, bukan dariPath. - Memberi masa berlaku panjang pada cookie sesi. Cookie yang bertahan berbulan-bulan memperpanjang jendela pencurian, sedangkan sesi yang sehat berumur pendek dan dapat dicabut di server. Gunakan
Max-Ageyang wajar, lalu catat sesi di server agar dapat diakhiri sebelum waktunya. - Mengatur
Domainpada cookie yang tidak perlu dibagikan. Cookie dengan atributDomain=contoh.testikut terkirim ke seluruh subdomain, sehingga satu subdomain yang disusupi cukup untuk menerima cookie itu. Tanpa atributDomain, cookie menjadi host-only dan hanya kembali ke host yang menyetorkannya.
Ringkasan#
- Cookie mengatasi sifat stateless HTTP: server mengirim
Set-Cookie, dan browser mengembalikannya pada permintaan berikutnya yang cocok lingkupnya. - Atribut
Path,Domain,Secure,HttpOnly,SameSite, danMax-Agemempersempit tempat, waktu, dan cara cookie digunakan. - Cookie sesi hanya membawa pengenal, sedangkan keadaannya disimpan di server, sehingga pencurian pengenal berarti pembajakan sesi.
- Pembangkit acak kriptografis dan regenerasi pengenal setelah login menutup serangan penebakan dan session fixation.
Referensi#
- Set-Cookie — MDN
— Dokumentasi header
Set-Cookiebeserta perilaku seluruh atributnya. - RFC 6265: HTTP State Management Mechanism — IETF — Spesifikasi yang mendefinisikan cookie, termasuk algoritma pencocokan jalur dan domain.
- Session Management Cheat Sheet — OWASP — Panduan pengelolaan sesi yang aman, termasuk panjang, rotasi, dan pengakhiran session ID.