Konteks dan cakupan
Anda mengelola API OAuth yang digunakan oleh klien seluler dan peramban (browser). Resource server saat ini hanya memvalidasi Bearer access token; satu kebocoran log dapat memungkinkan penyerang memutar ulang (replay) permintaan selama token tersebut masih valid. Rancang skema DPoP (Demonstrating Proof-of-Possession), termasuk authorization server, klien, resource server, dan perilaku saat DPoP tidak tersedia.
Ini adalah pertanyaan arsitektur keamanan backend, bukan kuis konfigurasi vendor. RFC 9449 mengikat access token ke kunci publik klien dan mewajibkan setiap panggilan membawa bukti yang terikat pada metode HTTP dan URI. RFC 9700 menambahkan panduan keamanan OAuth terkini seperti menghindari alur yang lemah dan menggunakan PKCE.
Apa yang sedang diuji oleh pewawancara
Jawaban yang kuat dimulai dari mode kegagalan Bearer token: siapa pun yang memegang string tersebut dapat menggunakannya, sehingga token yang dicuri tidak dapat dibedakan dari permintaan yang sah. Kemudian jawaban tersebut memberikan alur end-to-end: klien membuat kunci, token endpoint memverifikasi bukti DPoP dan mengikat kunci publik, lalu resource server memeriksa tanda tangan, metode, URI, hash token, dan jendela waktu.
Pewawancara juga mengharapkan rencana untuk replay jti, nonce server, kehilangan kunci, penulisan ulang URI oleh proxy, refresh token, klien lama, dan telemetri. Hanya mengatakan "tandatangani JWT" tidaklah cukup: tanda tangan JWT biasa membuktikan integritas penerbit, bukan membuktikan bahwa pemanggil masih memiliki kunci privat yang digunakan saat penerbitan.
Pertanyaan klarifikasi awal
Klien dan model ancaman
Konfirmasikan apakah klien berupa SPA peramban, aplikasi seluler native, atau aplikasi server. Siklus hidup akan berbeda tergantung pada apakah kunci privat dapat disimpan dalam penyimpanan berbasis perangkat keras (hardware-backed storage) dan apakah setiap instalasi mendapatkan kuncinya sendiri. Tanyakan apakah penyerang dapat membaca log, proxy, penyimpanan peramban, atau file perangkat, serta apakah mereka dapat mencegat permintaan secara real-time.
Batasan resource-server
Konfirmasikan gateway, terminasi TLS, dan penerusan internal. htu DPoP harus cocok dengan semantik URI yang diverifikasi oleh resource server. Jika gateway menulis ulang path, tentukan kanonikalisasi dan header penerusan yang tepercaya; jangan biarkan klien menandatangani satu alamat sementara backend memverifikasi alamat lain.
Kendala ketersediaan dan migrasi
Tanyakan apakah klien Bearer legacy harus tetap berfungsi, apakah periode dual-stack singkat dapat diterima, dan apakah proses login dan refresh menggunakan authorization server yang sama. Jika regulasi atau API bernilai tinggi memerlukan pembatasan pengirim yang ketat, wajibkan DPoP. Untuk resource berisiko lebih rendah, lakukan migrasi melalui negosiasi kemampuan dan peluncuran bertahap yang terukur.
Kerangka jawaban 30 detik
"Saya akan merancang DPoP sebagai protokol tiga pihak. Klien menghasilkan pasangan kunci per instalasi dan mengirimkan bukti DPoP satu kali selama permintaan token. Authorization server memverifikasinya dan mengikat cnf.jkt access token ke kunci publik. Setiap panggilan API membawa JWT DPoP baru; resource server memeriksa tanda tangannya, htm, htu, iat, jti yang unik, ath, dan cnf.jkt milik token, dengan nonce dan jendela waktu singkat untuk mengurangi replay. Kunci privat yang hilang akan mencabut token yang terikat. Klien legacy mendapatkan jalur Bearer dengan batas waktu hanya untuk resource yang secara eksplisit berisiko rendah sementara operasi bernilai tinggi beralih ke DPoP wajib."
Solusi langkah demi langkah
Langkah 1: Buat kunci dan ikat otorisasi
Pada proses pertama kali dijalankan, klien membuat pasangan kunci asimetris dan menyimpan kunci privat di penyimpanan perangkat yang aman. Klien mengirimkan kunci publik dalam JWK yang dibawa oleh bukti DPoP saat meminta token. Authorization server memvalidasi tanda tangan, tipe, dan waktu bukti, lalu mengikat data konfirmasi token ke thumbprint JWK kunci publik. Dengan alur authorization-code, tetap gunakan panduan PKCE dari RFC 9700: DPoP membuktikan kepemilikan token dan tidak menggantikan perlindungan kode.
Langkah 2: Hasilkan bukti per permintaan
Untuk setiap permintaan, klien membuat JWT baru yang setidaknya berisi jti, htm, htu, dan iat, lalu menandatanganinya dengan kunci privat yang terikat. Klien menempatkan hash dari access token di ath, memungkinkan resource server memverifikasi bahwa bukti tersebut menargetkan token ini, bukan token lain yang ditandatangani oleh kunci yang sama. Bukti dimasukkan ke dalam header DPoP, dan access token menggunakan skema otorisasi DPoP alih-alih Bearer.
Langkah 3: Verifikasi dalam urutan tetap
Resource server pertama-tama membatasi algoritma dan tipe JWK, lalu memverifikasi tanda tangan JWT, format kunci, jendela waktu, metode, dan URI. Server melakukan hashing pada access token dan membandingkan hasilnya dengan ath; server membaca cnf.jkt dari token dan membandingkannya dengan thumbprint kunci bukti. Server kemudian memeriksa apakah jti telah muncul di jendela saat ini sebelum menerapkan otorisasi scope, audience, dan resource biasa.
Langkah 4: Tangani nonce dan replay
Server dapat menerbitkan nonce untuk resource berisiko tinggi, mewajibkan klien menyertakannya dalam bukti berikutnya. Deduplikasi jti diperlukan dalam jendela penerimaan. Cache single-node berfungsi untuk deployment kecil; resource server multi-instance memerlukan cache bersama dengan masa kedaluwarsa (expiring shared cache), atau harus secara eksplisit menerima risiko duplikat dalam jendela singkat dan mencatatnya sebagai peristiwa keamanan. Telemetri penolakan harus membedakan kedaluwarsa, tanda tangan buruk, ketidakcocokan thumbprint, dan ketiadaan nonce tanpa mengekspos materi kunci.
Langkah 5: Rancang rotasi, pencabutan (revocation), dan fallback
Jika perangkat hilang, kunci privat terekspos, atau server mendeteksi penyalahgunaan, cabut access token dan refresh token yang terkait dengan thumbprint tersebut dan wajibkan otorisasi ulang. Rotasi tidak boleh berupa penggantian diam-diam hanya di sisi klien: kunci publik baru memerlukan pengikatan otorisasi baru. Selama migrasi, resource server dapat menerima kedua skema, tetapi operasi penulisan bernilai tinggi harus memiliki kebijakan wajib-DPoP yang terpisah agar kompatibilitas tidak pernah menjadi default keamanan.
Langkah 6: Bandingkan mTLS dengan Bearer biasa
mTLS menempatkan bukti pada lapisan sertifikat klien TLS dan cocok untuk sistem server-ke-server dengan operasional sertifikat yang matang. DPoP bekerja pada lapisan aplikasi dan cocok untuk klien seluler dan peramban, tetapi menambah operasi bukti, nonce, jti, dan penyimpanan kunci. Bearer biasa paling mudah diterapkan tetapi tidak dapat menahan replay setelah string token disalin. Pilihlah berdasarkan kemampuan klien, topologi proxy, kepatuhan, dan biaya operasional daripada mengaktifkan DPoP secara membabi buta untuk setiap endpoint.
Contoh jawaban berkualitas tinggi
Saya akan memulai dari model ancaman: kebocoran log, proxy, atau penyimpanan klien dapat mengekspos access token, setelah itu penyerang dapat memanggil API selama masa berlakunya. Masa kedaluwarsa yang lebih pendek mempersempit jendela tersebut tetapi tidak membuktikan bahwa pemanggil adalah klien asli.
Klien membuat pasangan kunci per instalasi dan melindungi kunci privat. Selama pertukaran authorization-code, saya mewajibkan PKCE dan bukti DPoP; authorization server memverifikasi bukti dan mengikat cnf.jkt milik token ke kunci publik. Di resource server, saya memverifikasi tanda tangan bukti, htm, htu, iat, jti, ath, dan nonce dalam urutan tetap, lalu membandingkan thumbprint kunci bukti dengan ikatan token. Ketidakcocokan apa pun dianggap tidak terotorisasi dan diklasifikasikan dalam metrik keamanan.
Saya menyimpan nilai jti dalam cache deduplikasi bersama berbasis TTL untuk deployment multi-instance. Penulisan berisiko tinggi memerlukan nonce dan DPoP. Selama migrasi, pembacaan berisiko rendah dapat berupa dual-stack, tetapi tenggat waktu terikat pada versi klien dan risiko resource. Kunci yang hilang akan mencabut tokennya dan memulai otorisasi baru; fallback Bearer tidak pernah berbagi jalur penulisan bernilai tinggi. Terakhir, saya menjalankan pengujian integrasi untuk bukti palsu, replay jti lama, perubahan URI, hash token yang salah, nonce yang kedaluwarsa, dan penulisan ulang proxy, lalu memantau tingkat penolakan, percobaan ulang nonce, dan thumbprint abnormal untuk membuktikan bahwa replay berhasil diblokir.
Kesalahan umum
- Kesalahan: Hanya menandatangani access token sebagai JWT. → Mengapa gagal: Tanda tangan membuktikan integritas penerbit tetapi bukan kepemilikan kunci privat klien. → Solusi: Ikat
cnf.jktdan verifikasi bukti DPoP baru pada setiap permintaan. - Kesalahan: Menggunakan kembali satu bukti DPoP. → Mengapa gagal: Penyerang dapat memutar ulang seluruh permintaan karena pemeriksaan
jtidan waktu tidak dapat membedakannya. → Solusi: Hasilkanjtibaru per permintaan dan lakukan deduplikasi dalam jendela penerimaan, serta tambahkan nonce jika diperlukan. - Kesalahan: Memverifikasi DPoP hanya di gateway sementara layanan internal mempercayai header pengguna yang tidak terikat. → Mengapa gagal: Penerusan dapat menghilangkan metode, URI, atau ikatan token dan membiarkan identitas palsu lolos. → Solusi: Teruskan hasil verifikasi yang dinormalisasi melintasi batas tepercaya dan batasi siapa yang dapat mengaturnya.
- Kesalahan: Melakukan fallback ke Bearer pada setiap kesalahan validasi. → Mengapa gagal: Penyerang dapat sengaja memicu kesalahan DPoP untuk mencapai jalur yang paling lemah. → Solusi: Izinkan fallback dengan batas waktu hanya untuk klien legacy terdaftar dan resource berisiko rendah.
Pertanyaan lanjutan dan tanggapan
Pertanyaan lanjutan 1: Bagaimana Anda mendeduplikasi jti lintas kawasan (cross-region)?
Pilih konsistensi berdasarkan risiko resource. Penulisan bernilai tinggi dapat menggunakan penyimpanan bersama lintas kawasan dan membayar satu round trip ekstra; pembacaan berisiko rendah dapat menggunakan cache regional dan jendela singkat, mencatat duplikat tanpa mengorbankan ketersediaan. Dalam kedua desain tersebut, tuliskan jendela waktu, perilaku kegagalan, dan tingkat duplikat ke dalam SLO.
Pertanyaan lanjutan 2: Bagaimana jika XSS membaca kunci privat peramban?
DPoP tidak dapat memperbaiki lingkungan eksekusi klien yang telah dikendalikan sepenuhnya. Prioritaskan kunci yang tidak dapat diekspor (non-exportable keys), Content Security Policy yang ketat, token berumur pendek, dan rotasi refresh; cabut pengikatan saat thumbprint menunjukkan perilaku abnormal. Nyatakan batasan perlindungan dengan jelas: DPoP menargetkan token yang disalin saat kunci privat tidak ikut disalin, bukan setiap bentuk pengambilalihan endpoint.
Pertanyaan lanjutan 3: Proxy mengubah URI. Bagaimana Anda memverifikasi htu?
Definisikan pemetaan dari URI kanonikal eksternal ke rute internal, dan hanya izinkan proxy tepercaya untuk menyediakan skema, host, dan path asli yang terotentikasi. Resource server menerapkan kanonikalisasi yang sama sebelum perbandingan dan menolak header penerusan yang tidak terotentikasi dan dikontrol oleh klien. Jika tidak ada pemetaan tepercaya, akhiri DPoP di batas gateway dan terbitkan identitas internal berumur pendek daripada menebak URI asli.
Pertanyaan lanjutan 4: Klien legacy hanya dapat mengirimkan Bearer. Bisakah Anda mendukungnya selamanya?
Kompatibilitas permanen bukanlah desain keamanan. Tetapkan batas waktu migrasi berdasarkan versi klien, risiko pengguna, dan jenis resource; wajibkan DPoP terlebih dahulu untuk penulisan bernilai tinggi, kembalikan kesalahan peningkatan (upgrade error) yang dapat ditindaklanjuti, dan pantau lalu lintas residual. Pertahankan rute Bearer terbatas hanya jika tinjauan risiko membenarkannya serta menambahkan pengikatan perangkat atau pembatasan laju (rate limits).