HTTP membawa setiap permintaan (request) dari browser ke server dalam bentuk teks yang terbuka, sehingga pihak yang berada di antara keduanya dapat membaca, mengubah, dan mengirim ulang pesan tersebut. Topik ini membedah tiga kemampuan itu melalui sebuah server lab kecil dan curl. Setelah selesai membaca, intersepsi, manipulasi, dan replay dapat direproduksi di localhost, dan posisi proxy sekelas Burp Suite pada medan serangan menjadi jelas.
Sifat HTTP sebagai protokol terbuka#
Sebuah pesan HTTP adalah teks terstruktur yang terdiri atas baris pertama yang berisi method dan path, diikuti header, lalu tubuh pesan bila ada. Protokol ini juga bersifat stateless, yakni tidak menyimpan keadaan antara satu permintaan dan permintaan berikutnya, sehingga setiap pesan berdiri sendiri. Karena berupa teks biasa, pesan tersebut dapat dibaca oleh siapa pun yang berada pada jalur jaringannya, misalnya pengguna lain pada titik akses Wi-Fi yang sama.
Posisi terpenting bagi penyerang adalah di antara browser dan server. Program yang berada di posisi itu disebut proxy, yaitu perantara yang menerima permintaan dari klien, meneruskannya ke server, lalu mengembalikan responsnya. Alat pengujian seperti Burp Suite bekerja persis sebagai proxy: browser diarahkan kepadanya sehingga setiap permintaan tertahan untuk diamati sebelum diteruskan. Diagram berikut memperlihatkan posisi tersebut.
Dari posisi itu lahir tiga kemampuan serangan. Intersepsi berarti membaca salinan pesan saat melintas, manipulasi berarti mengubah isinya sebelum diteruskan, dan replay berarti mengirim ulang pesan yang sudah melintas, byte demi byte, tanpa harus memahaminya. Server tidak memiliki cara bawaan untuk membedakan permintaan yang datang dari browser asli atau dari proxy, dan pemberian hak berlebih akibat permintaan yang dimanipulasi termasuk kategori A01 Broken Access Control pada OWASP Top 10:2025. Rincian format pesan HTTP sendiri dibahas secara mendalam pada jalur Networking, sehingga fokus topik ini adalah sisi yang dapat diserang.
Server lab yang menggemakan permintaan#
Untuk melihat persis apa yang diterima server, gunakan server lab kecil berikut yang dibangun dengan pustaka bawaan Python. Server ini tidak memiliki logika aplikasi karena tugasnya hanya mengembalikan method, path, header, dan tubuh permintaan dalam bentuk JSON. Responsnya dengan demikian menjadi cermin permintaan, sehingga setiap percobaan curl memperlihatkan pandangan server. Berkas tersebut memiliki dua mode, yaitu uji mandiri tanpa argumen dan mode layanan dengan argumen --layani pada port 8000.
Alamat yang diikat juga sengaja dibatasi pada 127.0.0.1, sehingga lab hanya dapat dihubungi dari mesin yang sama dan tidak terjangkau dari jaringan luar. Pada aplikasi sungguhan, isi responsnya adalah data bisnis, sedangkan format permintaannya persis sama. Karena itu, apa pun yang diterima server lab di bawah ini akan diterima dengan cara yang sama oleh aplikasi nyata.
"""Server lab yang menggemakan method, path, header, dan tubuh permintaan.
Jalankan tanpa argumen untuk uji mandiri, atau dengan argumen --layani
agar server melayani permintaan sungguhan pada http://127.0.0.1:8000/.
"""
import json
import sys
import threading
import urllib.request
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
class PenanganEcho(BaseHTTPRequestHandler):
"""Mengembalikan detail permintaan sebagai JSON."""
protocol_version = "HTTP/1.1"
def _gemakan(self):
panjang = int(self.headers.get("Content-Length") or 0)
tubuh = self.rfile.read(panjang).decode("utf-8") if panjang else ""
jawaban = json.dumps({
"method": self.command,
"path": self.path,
"headers": {k.lower(): v for k, v in self.headers.items()},
"body": tubuh,
}, indent=2)
data = jawaban.encode("utf-8")
self.send_response(200)
self.send_header("Content-Type", "application/json; charset=utf-8")
self.send_header("Content-Length", str(len(data)))
self.end_headers()
self.wfile.write(data)
def do_GET(self):
self._gemakan()
def do_POST(self):
self._gemakan()
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), PenanganEcho)
try:
server.serve_forever()
finally:
server.server_close()
def uji_mandiri():
server = ThreadingHTTPServer(("127.0.0.1", 8000), PenanganEcho)
thread = threading.Thread(target=server.serve_forever, daemon=True)
thread.start()
try:
with urllib.request.urlopen(
"http://127.0.0.1:8000/buku?halaman=2", timeout=5
) as jawaban:
data = json.loads(jawaban.read().decode("utf-8"))
print(f"GET {data['path']} -> {jawaban.status}")
permintaan = urllib.request.Request(
"http://127.0.0.1:8000/buku",
data=json.dumps({"judul": "Catatan Lab"}).encode("utf-8"),
headers={"Content-Type": "application/json"},
method="POST",
)
with urllib.request.urlopen(permintaan, timeout=5) as jawaban:
data = json.loads(jawaban.read().decode("utf-8"))
print(f"POST {data['path']} -> {jawaban.status}, tubuh diterima: {data['body']}")
finally:
server.shutdown()
server.server_close()
if __name__ == "__main__":
if "--layani" in sys.argv:
jalankan()
else:
uji_mandiri()GET /buku?halaman=2 -> 200
POST /buku -> 200, tubuh diterima: {"judul": "Catatan Lab"}Dua baris keluaran di atas berasal dari uji mandiri: server dihidupkan pada thread terpisah, lalu satu permintaan GET dan satu permintaan POST dikirim melalui HTTP sungguhan. Perhatikan bahwa tubuh POST dikembalikan utuh, termasuk spasi dan tanda kutipnya. Server juga tidak mencatat siapa pengirimnya, dan hal ini akan berperan dalam percobaan replay di bagian akhir.
Intersepsi, manipulasi, dan replay#
Tiga percobaan berikut menggunakan server lab yang sama. Setiap blok menghidupkan server di latar belakang, menjalankan curl, lalu menghentikannya, sehingga setiap blok dapat dijalankan secara mandiri pada direktori topik.
Intersepsi: membaca salinan permintaan#
Intersepsi adalah menahan dan membaca pesan saat melintas. curl berperan menggantikan browser, sedangkan opsi -i menampilkan header respons di atas tubuhnya. Pada pengujian aplikasi sungguhan, proxy seperti Burp Suite melakukan hal yang sama terhadap trafik browser: permintaan ditahan, ditampilkan, baru diteruskan setelah diamati. Jalankan blok berikut untuk melihat pandangan server terhadap sebuah permintaan.
# jalankan server lab di latar belakang
python3 server.py --layani &
PID=$!
sleep 0.5
# amati permintaan dan respons lengkap dengan header
curl -s -i "http://localhost:8000/buku?halaman=2"
echo
# hentikan server lab
kill $PID
wait $PID 2>/dev/null
echo "server lab dihentikan"HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 167
{
"method": "GET",
"path": "/buku?halaman=2",
"headers": {
"host": "localhost:8000",
"user-agent": "curl/8.14.1",
"accept": "*/*"
},
"body": ""
}
server lab dihentikanRespons JSON memuat salinan permintaan yang persis sama: host dan user-agent terbaca, dan tidak ada atribut lain yang menyertai. Pada HTTP polos, baris-baris yang sama dapat dibaca oleh siapa pun di jalur jaringan. Inilah alasan intersepsi menjadi langkah pertama pada hampir semua pengujian aplikasi web.
Manipulasi: mengubah isi permintaan#
Manipulasi adalah langkah berikutnya, yaitu mengubah pesan sebelum diteruskan. Contoh di bawah mengirim permintaan pencatatan skor sebagai peran peserta, kemudian mengulang permintaan yang sama setelah header X-Peran diganti menjadi admin dan nilai skor dinaikkan. Server lab meneruskan keduanya tanpa keluhan karena tugasnya hanya menggemakan permintaan. Aplikasi nyata yang memercayai header atau nilai dari klien akan menerima hasil manipulasi tersebut seolah-olah sah.
# jalankan server lab di latar belakang
python3 server.py --layani &
PID=$!
sleep 0.5
# permintaan asli sebagaimana dikirim browser
curl -s -i -X POST "http://localhost:8000/nilai" \
-H "Content-Type: application/json" \
-H "X-Peran: peserta" \
-d '{"peserta": "citra", "skor": 70}'
echo
# permintaan yang sama setelah isi dan headernya diubah di tengah jalan
curl -s -i -X POST "http://localhost:8000/nilai" \
-H "Content-Type: application/json" \
-H "X-Peran: admin" \
-d '{"peserta": "citra", "skor": 100}'
echo
# hentikan server lab
kill $PID
wait $PID 2>/dev/null
echo "server lab dihentikan"HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 291
{
"method": "POST",
"path": "/nilai",
"headers": {
"host": "localhost:8000",
"user-agent": "curl/8.14.1",
"accept": "*/*",
"content-type": "application/json",
"x-peran": "peserta",
"content-length": "32"
},
"body": "{\"peserta\": \"citra\", \"skor\": 70}"
}
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 290
{
"method": "POST",
"path": "/nilai",
"headers": {
"host": "localhost:8000",
"user-agent": "curl/8.14.1",
"accept": "*/*",
"content-type": "application/json",
"x-peran": "admin",
"content-length": "33"
},
"body": "{\"peserta\": \"citra\", \"skor\": 100}"
}
server lab dihentikanPerbandingan kedua respons menunjukkan bahwa hanya dua bagian yang berubah, yaitu x-peran dan tubuh permintaan, sedangkan server menjawab 200 OK untuk keduanya. Kesimpulannya: klien mengendalikan seluruh isi permintaan, sehingga pemeriksaan otorisasi hanya sah bila dilakukan di server berdasarkan keadaan sesi, bukan berdasarkan header yang dikirim klien.
Replay: mengirim ulang permintaan yang sama#
Replay mengulang pesan yang sudah terkirim tanpa mengubah satu byte pun. Karena HTTP bersifat stateless dan server lab tidak menyimpan riwayat, permintaan kedua dijawab persis sama dengan yang pertama. Pada aplikasi nyata, replay menjadi serangan ketika pesan membawa efek, misalnya pencatatan nilai atau penukaran token, dan tidak dilindungi pengenal sekali pakai seperti nonce. Jalankan blok berikut, lalu bandingkan kedua respons.
# jalankan server lab di latar belakang
python3 server.py --layani &
PID=$!
sleep 0.5
# permintaan pertama
curl -s -i -X POST "http://localhost:8000/nilai" \
-H "Content-Type: application/json" \
-H "X-Peran: peserta" \
-d '{"peserta": "citra", "skor": 70}'
echo
# permintaan kedua, byte demi byte sama dengan yang pertama
curl -s -i -X POST "http://localhost:8000/nilai" \
-H "Content-Type: application/json" \
-H "X-Peran: peserta" \
-d '{"peserta": "citra", "skor": 70}'
echo
# hentikan server lab
kill $PID
wait $PID 2>/dev/null
echo "server lab dihentikan"HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 291
{
"method": "POST",
"path": "/nilai",
"headers": {
"host": "localhost:8000",
"user-agent": "curl/8.14.1",
"accept": "*/*",
"content-type": "application/json",
"x-peran": "peserta",
"content-length": "32"
},
"body": "{\"peserta\": \"citra\", \"skor\": 70}"
}
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
Content-Length: 291
{
"method": "POST",
"path": "/nilai",
"headers": {
"host": "localhost:8000",
"user-agent": "curl/8.14.1",
"accept": "*/*",
"content-type": "application/json",
"x-peran": "peserta",
"content-length": "32"
},
"body": "{\"peserta\": \"citra\", \"skor\": 70}"
}
server lab dihentikanDua respons identik menegaskan bahwa dari sudut pandang server, permintaan kedua tidak dapat dibedakan dari yang asli. Perlindungan atas replay harus dibangun di atas protokol, misalnya token sekali pakai, stempel waktu, atau nomor urut yang diperiksa server. Pembatasan semacam itu biasanya dipadukan dengan batas laju (rate limit) agar pengiriman ulang dalam jumlah besar terdeteksi lebih dini.
Batasan intersepsi pada HTTPS#
Pada HTTPS, seluruh isi HTTP dibungkus dengan enkripsi TLS, sehingga proxy pasif hanya melihat metadata seperti domain tujuan, ukuran pesan, dan waktu. Intersepsi isi memerlukan sertifikat yang dipercaya klien. Alat seperti Burp Suite menangani hal ini di lab dengan memasang otoritas sertifikat (certificate authority, CA) miliknya sendiri ke daftar kepercayaan browser lab, sehingga browser menerima sertifikat tiruan yang dibuat alat tersebut. Kondisi seperti itu sah hanya pada lab milik sendiri.
Bagi pengembang aplikasi, konsekuensinya sederhana. Aktifkan HTTPS untuk seluruh lalu lintas, lalu terapkan kebijakan HSTS (HTTP Strict Transport Security), yaitu instruksi agar browser menolak HTTP polos untuk domain tersebut. Intersepsi yang tersisa kemudian hanya mungkin dilakukan oleh pihak yang telah menguasai perangkat klien atau otoritas sertifikat, dua posisi yang jauh lebih mahal bagi penyerang.
Perlu dicatat bahwa metadata tetap bocor meskipun isinya terenkripsi. Nama domain masih tampak pada proses jabat tangan TLS, dan ukuran serta waktu pesan tetap dapat diamati. Perlindungan yang diberikan HTTPS karenanya menyasar kerahasiaan dan keutuhan isi pesan, bukan menyembunyikan fakta bahwa komunikasi itu terjadi.
Kesalahan umum#
Beberapa kekeliruan berikut paling sering muncul saat topik ini dipraktikkan.
- Menjalankan ulang server ketika yang lama masih hidup. Port 8000 masih terpakai sehingga muncul error
Address already in use, dancurljustru berbicara dengan server lama yang tidak sesuai dengan percobaan. Perbaikannya, hentikan proses lama terlebih dahulu sebelum menjalankan blok berikutnya. - Mengamati permintaan tanpa opsi
-i. Tanpa opsi itu,curlhanya menampilkan tubuh respons, sehingga header sepertiSet-CookieatauLocationtidak terlihat. Gunakan-iuntuk melihat keduanya, atau-vuntuk melihat arus komunikasi lengkap. - Menilai permintaan aman karena berasal dari browser. Server lab pada topik ini menerima semua permintaan, dan aplikasi nyata pun tidak memiliki cara memastikan asal sebuah pesan. Asumsi bahwa isi permintaan tidak akan diubah merupakan asumsi tersembunyi yang harus digantikan dengan pemeriksaan eksplisit di server.
- Menempatkan data sensitif pada URL. Path dan query tercatat pada log server dan proxy, sehingga token yang diletakkan di sana terbaca di tempat yang tidak disadari. Letakkan data semacam itu pada header atau tubuh permintaan, lalu kirimkan melalui HTTPS.
Ringkasan#
- HTTP mengirimkan pesan sebagai teks terbuka, sehingga pihak pada jalur jaringan dapat melakukan intersepsi, manipulasi, dan replay.
- Proxy seperti Burp Suite menempatkan diri di antara browser dan server untuk melaksanakan ketiganya secara sistematis.
- Server tidak dapat membedakan asal sebuah permintaan, sehingga seluruh keputusan keamanan wajib diambil di sisi server.
- HTTPS menyembunyikan isi pesan dari proxy pasif, sedangkan intersepsi penuh memerlukan sertifikat yang dipercaya klien.
Referensi#
- Web Security Academy — PortSwigger — Materi pengujian aplikasi web berbasis proxy yang menjadi rujukan teknik intersepsi, manipulasi, dan replay permintaan.
- OWASP Top 10:2025 — OWASP — Daftar kategori kerentanan terkini yang menjadi kerangka jalur ini, termasuk A01 Broken Access Control.