Praktik terbaik untuk penanganan sisi server token JWT [ditutup]

111

(muncul dari utas ini karena ini benar-benar pertanyaannya sendiri dan tidak khusus untuk NodeJS dll)

Saya menerapkan server REST API dengan otentikasi, dan saya telah berhasil menerapkan penanganan token JWT sehingga pengguna dapat masuk melalui / login endpoint dengan nama pengguna / kata sandi, di mana token JWT dibuat dari rahasia server dan dikembalikan ke klien. Token kemudian diteruskan dari klien ke server di setiap permintaan API yang diautentikasi, di mana rahasia server digunakan untuk memverifikasi token.

Namun, saya mencoba untuk memahami praktik terbaik tentang bagaimana dan sejauh mana token harus divalidasi, untuk membuat sistem yang benar-benar aman. Persis apa yang harus dilibatkan dalam "memvalidasi" token? Apakah cukup bahwa tanda tangan dapat diverifikasi menggunakan rahasia server, atau haruskah saya juga memeriksa ulang token dan / atau muatan token terhadap beberapa data yang disimpan di server?

Sistem otentikasi berbasis token hanya akan seaman meneruskan nama pengguna / kata sandi di setiap permintaan asalkan sama atau lebih sulit untuk mendapatkan token daripada mendapatkan kata sandi pengguna. Namun, dalam contoh yang saya lihat, satu-satunya informasi yang diperlukan untuk menghasilkan token adalah nama pengguna dan rahasia sisi server. Bukankah ini berarti bahwa dengan asumsi selama satu menit bahwa pengguna yang jahat memperoleh pengetahuan tentang rahasia server, dia sekarang dapat menghasilkan token atas nama pengguna mana pun , sehingga memiliki akses tidak hanya untuk satu pengguna tertentu seperti halnya fakta jika kata sandi digunakan. diperoleh, tetapi sebenarnya untuk semua akun pengguna?

Ini membawa saya pada pertanyaan:

1) Apakah validasi token JWT harus dibatasi untuk memverifikasi tanda tangan token itu sendiri, mengandalkan integritas rahasia server saja, atau disertai dengan mekanisme validasi terpisah?

  • Dalam beberapa kasus saya telah melihat penggunaan gabungan dari token dan sesi server di mana setelah berhasil masuk melalui / login endpoint sesi dibuat. Permintaan API memvalidasi token, dan juga membandingkan data yang didekodekan yang ditemukan di token dengan beberapa data yang disimpan dalam sesi. Namun, menggunakan sesi berarti menggunakan cookie, dan dalam beberapa hal hal itu mengalahkan tujuan penggunaan pendekatan berbasis token. Ini juga dapat menyebabkan masalah bagi klien tertentu.

  • Dapat dibayangkan server menyimpan semua token yang saat ini digunakan di memcache atau yang serupa, untuk memastikan bahwa meskipun rahasia server dikompromikan sehingga penyerang dapat menghasilkan token "valid", hanya token persis yang dihasilkan melalui titik akhir / login akan diterima. Apakah ini masuk akal atau hanya berlebihan / berlebihan?

2) Jika verifikasi tanda tangan JWT adalah satu-satunya cara untuk memvalidasi token, yang berarti integritas rahasia server adalah titik puncaknya, bagaimana seharusnya rahasia server dikelola? Membaca dari variabel lingkungan dan dibuat (diacak?) Sekali per tumpukan yang diterapkan? Dibarui kembali atau dirotasi secara berkala (dan jika demikian, bagaimana menangani token valid yang ada yang dibuat sebelum rotasi tetapi perlu divalidasi setelah rotasi, mungkin cukup jika server menyimpan rahasia saat ini dan sebelumnya pada waktu tertentu) ? Sesuatu yang lain?

Mungkin saya hanya terlalu paranoid ketika menghadapi risiko rahasia server disusupi, yang tentu saja merupakan masalah yang lebih umum yang perlu ditangani dalam semua situasi kriptografi ...

JHH
sumber
1
Ada pertanyaan bagus. Re: pertanyaan 2. Saya memiliki masalah yang sama dengan kunci rahasia APAPUN yang disimpan di sisi server. Jika Anda melakukan kecocokan hash atau dekripsi asimetris apa pun, - apakah ini menandatangani info jwt atau mendekripsi cc yang disimpan di db, Anda harus memiliki kunci rahasia yang dapat diakses oleh kode di server. Jadi di mana kamu menyimpannya ?? Inilah jawaban terbaik yang saya temukan: pcinetwork.org/forum/index.php?threads/… - mungkin seaman yang didapat untuk kunci jwt juga.
jbd
Apa kunci rahasia di token jwt? Saya pikir jwt token itu sendiri rahasia. Atau bisa jadi kunci rahasia RSAPrivateKey privateKey??
kittu
3
Ini telah ditanyakan beberapa waktu yang lalu, tetapi mungkin seseorang akan menganggapnya berguna. Dalam kasus saya, saya memiliki "kunci rahasia" untuk setiap pengguna. Jadi setiap kali pengguna login, saya menghasilkan rahasia itu dan menyimpan dengan catatan pengguna di DB. Saya memvalidasi token menggunakan rahasia itu. Setelah keluar, saya menghapus nilai itu. Ini secara otomatis membatalkan token lain yang dibuat sebelumnya (Itulah yang saya butuhkan).
Nelson Rodriguez

Jawaban:

52

Saya telah bermain dengan token untuk aplikasi saya juga. Meskipun saya bukan ahli dengan cara apa pun, saya dapat berbagi beberapa pengalaman dan pemikiran saya tentang masalah ini.

Inti dari JWT pada dasarnya adalah integritas. Ini menyediakan mekanisme untuk server Anda memverifikasi bahwa token yang diberikan kepadanya adalah asli dan disediakan oleh server Anda. Tanda tangan yang dihasilkan melalui rahasia Anda adalah yang menyediakan untuk ini. Jadi, ya, jika rahasia Anda bocor, orang tersebut dapat menghasilkan token yang menurut server Anda adalah miliknya sendiri. Sistem berbasis token masih akan lebih aman daripada sistem nama pengguna / kata sandi Anda hanya karena verifikasi tanda tangan. Dan dalam kasus ini, jika seseorang memiliki rahasia Anda, sistem Anda memiliki masalah keamanan lain yang harus dihadapi selain seseorang yang membuat token palsu (dan bahkan kemudian, hanya dengan mengubah rahasianya memastikan bahwa token yang dibuat dengan rahasia lama sekarang tidak valid).

Sedangkan untuk payload, tanda tangan hanya akan memberi tahu Anda bahwa token yang diberikan kepada Anda persis seperti saat server Anda mengirimkannya. memverifikasi bahwa konten payload valid atau sesuai untuk aplikasi Anda jelas terserah Anda.

Untuk pertanyaan Anda:

1.) Dalam pengalaman saya yang terbatas, lebih baik memverifikasi token Anda dengan sistem kedua. Memvalidasi tanda tangan berarti token dibuat dengan rahasia Anda. Menyimpan token yang dibuat dalam beberapa jenis DB (redis, memcache / sql / mongo, atau beberapa penyimpanan lainnya) adalah cara yang fantastis untuk memastikan bahwa Anda hanya menerima token yang telah dibuat oleh server Anda. Dalam skenario ini, meskipun rahasia Anda bocor, itu tidak terlalu menjadi masalah karena token yang dibuat tidak akan valid. Ini adalah pendekatan yang saya ambil dengan sistem saya - semua token yang dihasilkan disimpan dalam DB (redis) dan pada setiap permintaan, saya memverifikasi bahwa token ada di DB saya sebelum saya menerimanya. Dengan cara ini token dapat dicabut untuk alasan apa pun, seperti token yang entah bagaimana dilepaskan ke alam liar, logout pengguna, perubahan kata sandi, perubahan rahasia, dll.

2.) Ini adalah sesuatu yang tidak banyak saya alami dan masih saya teliti secara aktif karena saya bukan seorang profesional keamanan. Jika Anda menemukan sumber daya apa pun, silakan posting di sini! Saat ini, saya hanya menggunakan kunci pribadi yang saya muat dari disk, tetapi jelas itu jauh dari solusi terbaik atau paling aman.

Akshay Dhalwala
sumber
5
Untuk poin kedua di sini adalah jawaban yang bagus: security.stackexchange.com/questions/87130/…
Bossliaw
1
Karena token tersedia di header, bagaimana jika token dicuri dan orang jahat mencoba masuk dengan token itu (mengetahui alamat email pengguna)?
kittu
22
Jika Anda menyimpan setiap JWT, maka tidak ada manfaat untuk JWT dan Anda mungkin juga tetap menggunakan id sesi acak.
ColinM
46

Berikut beberapa hal yang perlu dipertimbangkan saat menerapkan JWT di aplikasi Anda:

  • Jaga agar masa pakai JWT Anda relatif singkat, dan kelola seumur hidup di server. Jika tidak, dan nanti perlu informasi lebih lanjut di JWT Anda, Anda harus mendukung 2 versi, atau menunggu sampai JWT lama Anda kedaluwarsa sebelum Anda dapat menerapkan perubahan Anda. Anda dapat dengan mudah mengelolanya di server jika Anda hanya melihat iatbidang di jwt, dan mengabaikan expbidang tersebut.

  • Pertimbangkan untuk menyertakan url permintaan di JWT Anda. Misalnya, jika Anda ingin JWT Anda digunakan di titik akhir /my/test/path, sertakan bidang seperti 'url':'/my/test/path'di JWT Anda, untuk memastikannya hanya digunakan di jalur ini. Jika tidak, Anda mungkin menemukan bahwa orang-orang mulai menggunakan JWT Anda di titik akhir lain, bahkan yang bukan tujuan pembuatannya. Anda juga dapat mempertimbangkan untuk menyertakan md5 (url) sebagai gantinya, karena memiliki url besar di JWT akan membuat JWT menjadi jauh lebih besar, dan mereka bisa menjadi cukup besar.

  • Kedaluwarsa JWT harus dapat dikonfigurasi oleh setiap kasus penggunaan jika JWT sedang diterapkan di API. Misalnya, jika Anda memiliki 10 titik akhir untuk 10 kasus penggunaan berbeda untuk JWT, pastikan Anda dapat membuat setiap titik akhir menerima JWT yang kedaluwarsa pada waktu yang berbeda. Ini memungkinkan Anda untuk mengunci beberapa titik akhir lebih dari yang lain, jika misalnya, data yang disajikan oleh satu titik akhir sangat sensitif.

  • Alih-alih hanya mengakhiri JWT setelah waktu tertentu, pertimbangkan untuk menerapkan JWT yang mendukung keduanya:

    • N penggunaan - hanya dapat digunakan N kali sebelum kadaluwarsa dan
    • kedaluwarsa setelah jangka waktu tertentu (jika Anda memiliki token sekali pakai, Anda tidak ingin token itu hidup selamanya jika tidak digunakan, bukan?)
  • Semua kegagalan autentikasi JWT seharusnya menghasilkan header respons "error" yang menyatakan mengapa autentikasi JWT gagal. misalnya "kedaluwarsa", "tidak ada penggunaan yang tersisa", "dicabut", dll. Hal ini membantu pelaksana mengetahui mengapa JWT mereka gagal.

  • Pertimbangkan untuk mengabaikan "header" JWT Anda karena JWT tersebut membocorkan informasi dan memberikan ukuran kontrol kepada peretas. Ini sebagian besar menyangkut algbidang di header - abaikan ini dan anggap saja header adalah yang ingin Anda dukung, karena ini menghindari peretas yang mencoba menggunakan Nonealgoritme, yang menghapus pemeriksaan keamanan tanda tangan.

  • JWT harus menyertakan pengenal yang merinci aplikasi mana yang menghasilkan token. Misalnya jika JWT Anda dibuat oleh 2 klien berbeda, mychat, dan myclassifiedsapp, maka masing-masing harus menyertakan nama proyeknya atau sesuatu yang serupa di kolom "iss" di JWT, misalnya "iss": "mychat"

  • JWT tidak boleh login file log. Konten JWT dapat dicatat, tetapi tidak dapat mencatat JWT itu sendiri. Ini memastikan devs atau orang lain tidak dapat mengambil JWT dari file log dan melakukan sesuatu ke akun pengguna lain.
  • Pastikan penerapan JWT Anda tidak mengizinkan algoritme "Tidak Ada", untuk menghindari peretas membuat token tanpa menandatanganinya. Kelas kesalahan ini dapat dihindari sepenuhnya dengan mengabaikan "header" JWT Anda.
  • Sangat pertimbangkan untuk menggunakan iat(diterbitkan pada) alih-alih exp(kedaluwarsa) di JWT Anda. Mengapa? Karena iatpada dasarnya berarti kapan JWT dibuat, ini memungkinkan Anda untuk menyesuaikan di server ketika JWT berakhir, berdasarkan tanggal pembuatan. Jika seseorang lewat dalam exp20 tahun ke depan, JWT pada dasarnya hidup selamanya! Perhatikan bahwa Anda secara otomatis kedaluwarsa JWT jika mereka iatada di masa mendatang, tetapi memungkinkan untuk sedikit ruang gerak (misalnya 10 detik), jika waktu klien sedikit tidak sinkron dengan waktu server.
  • Pertimbangkan untuk menerapkan titik akhir untuk membuat JWT dari muatan json, dan paksa semua klien penerapan Anda untuk menggunakan titik akhir ini untuk membuat JWT mereka. Ini memastikan bahwa Anda dapat mengatasi masalah keamanan apa pun yang Anda inginkan dengan cara JWT dibuat di satu tempat, dengan mudah. Kami tidak melakukan ini langsung di aplikasi kami, dan sekarang harus perlahan-lahan mengeluarkan pembaruan keamanan sisi server JWT karena 5 klien kami yang berbeda membutuhkan waktu untuk menerapkan. Selain itu, buat titik akhir buat Anda menerima larik muatan json untuk dibuat JWT, dan ini akan mengurangi # permintaan http yang masuk ke titik akhir ini untuk klien Anda.
  • Jika JWT Anda akan digunakan di titik akhir yang juga mendukung penggunaan berdasarkan sesi, pastikan Anda tidak memasukkan apa pun di JWT yang diperlukan untuk memenuhi permintaan. Anda dapat dengan mudah melakukan ini jika Anda memastikan titik akhir Anda berfungsi dengan sesi, ketika tidak ada JWT yang disediakan.
  • Jadi JWT secara umum akhirnya berisi semacam userId atau groupId, dan mengizinkan akses ke bagian dari sistem Anda berdasarkan informasi ini. Pastikan Anda tidak mengizinkan pengguna di satu area aplikasi Anda untuk meniru pengguna lain, terutama jika ini memberikan akses ke data sensitif. Mengapa? Bahkan jika proses pembuatan JWT Anda hanya dapat diakses oleh layanan "internal", pengembang atau tim internal lainnya dapat membuat JWT untuk mengakses data untuk pengguna mana pun, misalnya CEO dari beberapa perusahaan klien acak. Misalnya, jika aplikasi Anda menyediakan akses ke catatan keuangan untuk klien, maka dengan membuat JWT, pengembang dapat mengambil catatan keuangan perusahaan mana pun! Dan jika seorang peretas masuk ke jaringan internal Anda, mereka dapat melakukan hal yang sama.
  • Jika Anda akan mengizinkan url apa pun yang berisi JWT untuk di-cache dengan cara apa pun, pastikan bahwa izin untuk pengguna yang berbeda disertakan dalam url, dan bukan JWT. Mengapa? Karena pengguna mungkin mendapatkan data yang seharusnya tidak mereka dapatkan. Misalnya, pengguna super masuk ke aplikasi Anda, dan meminta url berikut:, /mysite/userInfo?jwt=XXXdan url ini akan disimpan dalam cache. Mereka keluar dan beberapa menit kemudian, pengguna biasa masuk ke aplikasi Anda. Mereka akan mendapatkan konten yang disimpan dalam cache - dengan info tentang pengguna super! Hal ini cenderung lebih jarang terjadi pada klien, dan lebih banyak pada server, terutama dalam kasus di mana Anda menggunakan CDN seperti Akamai, dan Anda membiarkan beberapa file hidup lebih lama. Ini dapat diperbaiki dengan memasukkan info pengguna yang relevan di url, dan memvalidasinya di server, bahkan untuk permintaan yang disimpan dalam cache, misalnya/mysite/userInfo?id=52&jwt=XXX
  • Jika jwt Anda dimaksudkan untuk digunakan seperti cookie sesi, dan seharusnya hanya bekerja pada mesin yang sama dengan jwt itu dibuat, Anda harus mempertimbangkan untuk menambahkan bidang jti ke jwt Anda. Ini pada dasarnya adalah token CSRF, yang memastikan JWT Anda tidak dapat diteruskan dari satu browser pengguna ke pengguna lain.
Brad Parks
sumber
1
Apa yang Anda maksud created_by, sudah ada klaim untuk itu di JWT dan itu disebut iss(penerbit).
Fred
ya poin yang bagus - saya akan memperbarui dengan itu ... terima kasih!
Brad Parks
8

Saya tidak berpikir saya seorang ahli tetapi saya ingin berbagi beberapa pemikiran tentang Jwt.

  • 1: Seperti yang dikatakan Akshay, lebih baik memiliki sistem kedua untuk memvalidasi token Anda.

    a .: Cara saya menanganinya: Saya menyimpan hash yang dihasilkan ke dalam penyimpanan sesi dengan waktu kedaluwarsa. Untuk memvalidasi sebuah token, itu harus sudah diterbitkan oleh server.

    b.:Setidaknya ada satu hal yang harus diperiksa metode tanda tangan yang digunakan. misal:

    header :
    {
      "alg": "none",
      "typ": "JWT"
    }
    

Beberapa perpustakaan yang memvalidasi JWT akan menerima yang ini tanpa memeriksa hash. Itu berarti bahwa tanpa mengetahui salt Anda yang digunakan untuk menandatangani token tersebut, seorang hacker dapat memberikan dirinya beberapa hak. Selalu pastikan ini tidak bisa terjadi. https://auth0.com/blog/2015/03/31/critical-vulnerabilities-in-json-web-token-libraries/

c .: Menggunakan cookie dengan ID sesi tidak akan berguna untuk memvalidasi token Anda. Jika seseorang ingin membajak sesi pengguna lambda, dia hanya perlu menggunakan sniffer (misalnya: wireshark). Peretas ini akan memiliki kedua informasi pada saat yang bersamaan.

  • 2: Itu sama untuk setiap rahasia. Selalu ada cara untuk mengetahuinya.

Cara saya menanganinya terkait dengan poin 1.a. : Saya memiliki rahasia yang dicampur dengan variabel acak. Rahasianya unik untuk setiap token.

Namun, saya mencoba untuk memahami praktik terbaik tentang bagaimana dan sejauh mana token harus divalidasi, untuk membuat sistem yang benar-benar aman.

Jika Anda menginginkan keamanan terbaik, Anda tidak boleh mengikuti praktik terbaik secara membabi buta. Cara terbaik adalah memahami apa yang Anda lakukan (menurut saya tidak masalah jika saya melihat pertanyaan Anda), lalu evaluasi keamanan yang Anda butuhkan. Dan jika Mossad ingin memiliki akses ke data rahasia Anda, mereka akan selalu menemukan caranya. (Saya suka posting blog ini: https://www.schneier.com/blog/archives/2015/08/mickens_on_secu.html )

Deblaton Jean-Philippe
sumber
Poin bagus untuk memiliki rahasia unik untuk setiap token tetapi bagaimana Anda membuat rahasia unik setiap saat? Saya menggunakan perpustakaan nimbus jwt
kittu
1
mungkin menggunakan kata sandi hash pengguna Anda.
momokjaaaaa
1
"Jika Anda tidak melakukan sesuatu dengan cara yang sama seperti yang dilakukan orang lain, akan lebih sulit bagi orang untuk menemukan cara melalui keamanan Anda." Ini terdengar seperti Keamanan Melalui Ketidakjelasan bagi saya. Praktik terbaik disebut demikian karena mereka mengurangi risiko paling umum dengan cara yang praktis.
Mnebuerquo
@Mnebuerquo Saya sangat setuju dengan Anda, orang yang menulis itu tidak boleh dipercaya ;-)
Deblaton Jean-Philippe
1
Dia benar meskipun bahwa seseorang tidak boleh mengikuti praktik terbaik secara membabi buta . Adalah baik untuk memahami mengapa praktik terbaik dianggap terbaik . Dalam setiap keputusan desain keamanan, ada pertukaran antara keamanan dan kegunaan. Memahami mengapa berarti Anda dapat membuat keputusan itu dengan cerdas. (Tetap ikuti praktik terbaik karena pengguna Anda tidak akan melakukannya.)
Mnebuerquo
3

Banyak jawaban bagus di sini. Saya akan mengintegrasikan beberapa jawaban yang menurut saya paling relevan dan menambahkan beberapa saran lagi.

1) Apakah validasi token JWT harus dibatasi untuk memverifikasi tanda tangan token itu sendiri, mengandalkan integritas rahasia server saja, atau disertai dengan mekanisme validasi terpisah?

Tidak, karena alasan yang tidak terkait dengan penyusupan rahasia token. Setiap kali pengguna masuk melalui nama pengguna dan kata sandi, server otorisasi harus menyimpan baik token yang dibuat, atau metadata tentang token yang dibuat. Pikirkan metadata ini sebagai catatan otorisasi. Pasangan pengguna dan aplikasi tertentu hanya boleh memiliki satu token atau otorisasi yang valid pada waktu tertentu. Metadata yang berguna adalah id pengguna yang terkait dengan token akses, id aplikasi, dan waktu ketika token akses diterbitkan (yang memungkinkan pencabutan token akses yang ada dan penerbitan token akses baru). Pada setiap permintaan API, validasi bahwa token berisi metadata yang sesuai. Anda perlu menyimpan informasi tentang kapan setiap token akses diterbitkan, sehingga pengguna dapat mencabut token akses yang ada jika kredensial akun mereka disusupi, dan masuk lagi dan mulai menggunakan token akses baru. Itu akan memperbarui database dengan waktu ketika token akses dikeluarkan (waktu otorisasi dibuat). Pada setiap permintaan API, periksa apakah waktu penerbitan token akses setelah waktu otorisasi dibuat.

Langkah-langkah keamanan lainnya termasuk tidak mencatat JWT dan memerlukan algoritme penandatanganan aman seperti SHA256.

2) Jika verifikasi tanda tangan JWT adalah satu-satunya cara untuk memvalidasi token, yang berarti integritas rahasia server adalah titik puncaknya, bagaimana seharusnya rahasia server dikelola?

Penyusupan rahasia server akan memungkinkan penyerang mengeluarkan token akses untuk pengguna mana pun, dan menyimpan data token akses pada langkah 1 tidak akan serta merta mencegah server menerima token akses tersebut. Misalnya, pengguna telah diberi token akses, dan kemudian, penyerang membuat token akses untuk pengguna tersebut. Waktu otorisasi token akses akan valid.

Seperti yang dikatakan Akshay Dhalwala, jika rahasia sisi server Anda dikompromikan, maka Anda memiliki masalah yang lebih besar untuk ditangani karena itu berarti penyerang telah menyusupi jaringan internal Anda, repositori kode sumber Anda, atau keduanya.

Namun, sistem untuk mengurangi kerusakan rahasia server yang disusupi dan menghindari penyimpanan rahasia dalam kode sumber melibatkan rotasi rahasia token menggunakan layanan koordinasi seperti https://zookeeper.apache.org. Gunakan tugas cron untuk membuat rahasia aplikasi setiap beberapa jam atau lebih (berapa lama token akses Anda valid), dan serahkan rahasia yang diperbarui ke Zookeeper. Di setiap server aplikasi yang perlu mengetahui rahasia token, konfigurasikan klien ZK yang diperbarui setiap kali nilai node ZK berubah. Simpan rahasia utama dan sekunder, dan setiap kali rahasia token diubah, setel rahasia token baru ke yang utama dan rahasia token lama ke sekunder. Dengan begitu, token valid yang ada akan tetap valid karena akan divalidasi dengan rahasia sekunder. Pada saat rahasia sekunder diganti dengan rahasia primer lama, semua token akses yang dikeluarkan dengan rahasia sekunder akan kedaluwarsa.

skeller88
sumber