City Courier

Saya menyerang sistem autentikasi saya sendiri sepuluh kali, lalu menerbitkan hasilnya.

Problem

Hampir setiap aplikasi modern memakai JSON Web Token untuk menjaga sesi login, dan sebagian besar tutorial mengajarkannya dengan cara yang sama: satu kunci rahasia, algoritma HS256, selesai. Cara itu berhasil — sampai tidak lagi. JWT punya sejumlah celah yang sudah lama terdokumentasi: serangan `alg: none` yang membuat token tanpa tanda tangan diterima, kebingungan algoritma yang menipu server memverifikasi dengan cara yang salah, dan penyuntikan `kid` yang mengarahkan verifikasi ke kunci milik penyerang. Yang mengganggu saya bukan keberadaan celah itu, melainkan satu pertanyaan yang tidak terjawab tuntas di materi berbahasa Indonesia yang saya temukan: kalau saya menerapkan pengamanan yang katanya benar, apakah ia benar-benar bertahan saat diserang? Sebagian besar tulisan berhenti pada "gunakan RS256". Nyaris tidak ada yang menunjukkan buktinya.

User

Langsung: pengguna sistem kurir — pengirim, kurir, admin — yang sesi loginnya bergantung pada mekanisme ini. Mereka tidak akan pernah tahu ia ada, dan memang begitu seharusnya. Keamanan yang bekerja itu tidak terlihat. Sesungguhnya: pengembang lain yang menghadapi pertanyaan yang sama. Itulah sebabnya hasilnya saya terbitkan, bukan saya simpan.

Solution

Alih-alih satu kunci rahasia bersama, sistem ini memakai kriptografi asimetris RS256 dan mendistribusikan kunci publiknya lewat JWKS sesuai RFC 7517. Perbedaannya bisa dijelaskan tanpa istilah teknis sama sekali:

HS256 seperti satu kunci yang dipakai bersama. Semua pihak yang perlu memeriksa keaslian surat harus memegang kunci yang sama — dan siapa pun yang memegangnya juga bisa membuat surat palsu. RS256 + JWKS seperti tanda tangan dan contoh tanda tangan. Hanya saya yang bisa menandatangani. Semua orang boleh memegang contohnya untuk mencocokkan, dan tak seorang pun bisa memalsukannya hanya dari contoh itu.

Ditambah satu hal: kunci-kunci itu berotasi. Kalau satu kunci bocor, jendela kerugiannya sempit, bukan selamanya. Soal pilihan algoritma, saya menulisnya apa adanya: Perbandingan yang benar-benar saya lakukan saat itu adalah HS256 versus RS256. Saya memilih RS256 karena memisahkan kewenangan menandatangani dari kewenangan memverifikasi — pihak yang memeriksa token tidak perlu memegang kunci yang bisa membuat token. ES256 baru saya ketahui belakangan. Kalau proyek ini saya mulai hari ini, ES256 akan masuk daftar pertimbangan karena ukuran tanda tangannya lebih ringkas pada tingkat keamanan setara.

Key Features

Menurut artikel yang terbit, pengujian mencakup sepuluh skenario serangan kritis, termasuk kebingungan algoritma dan penyuntikan kunci. Sistem yang diuji menerapkan:

  • Rotasi kunci JWKS dinamis
  • Distribusi kunci publik sesuai RFC 7517
  • Penyimpanan kunci privat terenkripsi
  • Pembatasan algoritma secara eksplisit
  • Identifikasi kunci berbasis UUID
  • Pencatatan menyeluruh

Hasilnya: sistem bertahan terhadap seluruh skenario yang diuji, dengan dampak performa minimal — dan bagian terakhir ini penting, karena keamanan yang membuat aplikasi lambat akan dimatikan orang.

Challenge — Yang tidak ada di jurnal

Artikel itu menceritakan bagian yang berhasil. Bagian ini menceritakan yang tidak. Sistemnya berhasil. Kodenya tidak. Riset saya lolos sidang dan terbit di jurnal terakreditasi. Tapi basis kodenya tumbuh cepat, dari lebih dari satu tangan, tanpa peninjauan kode, tanpa perlindungan branch, tanpa pemeriksaan otomatis, dan tanpa kesepakatan arsitektur tertulis. Akibatnya bisa diduga: peringatan menumpuk sampai ratusan, dan satu berkas antarmuka membengkak hingga hampir empat ribu baris. Di sebelahnya tersimpan salinan cadangan berkas itu, lebih besar lagi — pengakuan yang tidak saya sadari sedang saya tulis: saya sudah tidak percaya pada kode saya sendiri sampai harus menyalinnya sebelum berani mengubah apa pun. Saya sempat menyalahkan keadaan. Belakangan saya sadar diagnosis itu tidak menyelesaikan apa pun. Sebagai orang yang memulai proyek ini, sayalah yang seharusnya memasang pagar pengamannya sejak awal. Tidak ada peninjauan kode karena tidak ada yang mensyaratkannya. Tidak ada standar arsitektur karena tidak ada yang menuliskannya. Ada dua pilihan waktu itu: terus menambal, atau berhenti dan mengakui fondasinya salah sejak awal. Saya memilih berhenti dan menulis ulang dari nol. Keputusan itu mahal, dan saya yakin itu benar. Menulis kode yang berfungsi bisa dipelajari siapa saja. Mengenali kapan kode yang berfungsi tetap harus dibuang adalah keterampilan yang berbeda, dan jauh lebih sulit. Pelajaran yang saya bawa: kualitas kode bukan hasil dari orang yang disiplin, melainkan hasil dari sistem yang membuat ketidakdisiplinan sulit dilakukan. Dan ada ironi yang tidak bisa saya hindari: beberapa bulan kemudian saya merancang kurikulum yang mengajarkan peninjauan sejawat kepada murid kelas 5 — praktik yang absennya justru menghancurkan proyek saya sendiri.

Impact

Model autentikasiSebelum: Kunci rahasia bersama, satu titik kegagalan · Sesudah: Kunci asimetris berotasi, kunci privat terenkripsi
Ketahanan seranganSebelum: Belum teruji · Sesudah: Diuji terhadap 10 skenario terdokumentasi, bertahan seluruhnya
Status pengetahuanSebelum: Anggapan pribadi · Sesudah: Terbit dan dapat dikutip publik
Kondisi basis kodeSebelum: Utang teknis menumpuk, satu berkas ~4.000 baris · Sesudah: Diakui tidak layak, ditulis ulang dari nol
Proses pengembanganSebelum: Tanpa review, tanpa pagar · Sesudah: Pelajaran yang kini saya terapkan sejak commit pertama

Dampak terkuatnya bukan angka:

Riset ini melewati peer review dan terbit di JUTIF Vol 7 No 2 (2026), hal. 1834–1852 — jurnal terakreditasi SINTA 2. DOI `10.52436/1.jutif.2026.7.2.5662`. Siapa pun bisa membacanya, mengutipnya, atau membantahnya. Ini bukan klaim tentang keahlian saya; ini catatan permanen yang dapat diverifikasi siapa saja, kapan saja.

Tech Choices

RS256 (bukan HS256)Memisahkan kewenangan menandatangani dari memverifikasi
JWKS / RFC 7517Standar terbuka, bukan buatan sendiri. Rotasi kunci mungkin tanpa mengubah kode klien
Pembatasan algoritma eksplisitMenutup keluarga serangan kebingungan algoritma di akarnya
kid berbasis UUIDMencegah penyuntikan kid yang menebak lokasi kunci
Kunci privat terenkripsi saat disimpanBocornya berkas tidak otomatis berarti bocornya kunci
Pencatatan menyeluruhSerangan yang tidak tercatat adalah serangan yang tidak pernah kamu ketahui

Satu trade-off yang saya kalah:

Demi pencarian wilayah yang cepat, saya menyertakan data wilayah Indonesia langsung di dalam aplikasi. Kecepatannya tercapai — tapi ukuran aplikasi ikut membengkak sampai batas yang menurut saya sendiri tidak masuk akal untuk sebuah aplikasi kurir. Saya menang di kecepatan dan kalah di ukuran. Hari ini saya akan memilih berbeda: memuat data sesuai kebutuhan, bukan memaketkan semuanya di muka.

Screenshot — Placeholder

1 · Sampul artikel jurnalJUTIF Vol 7 No 2 (2026), terakreditasi SINTA 2
2 · Diagram alur JWKS buatanmu sendiriKaryamu, bukan aset perusahaan — aman sepenuhnya
3 · Bagan HS256 vs RS256Menjelaskan hal rumit secara sederhana
4 · Tautan DOIBukti yang bisa diklik siapa pun