Latar Belakang:
Saat ini sedang dalam proses membangun REST API, menggunakan simpul dengan express dan dikonsumsi oleh aplikasi seluler dan akhirnya situs web (berbasis browser modern).
Saya mencoba mengidentifikasi cara terbaik untuk mengotorisasi pembaruan / permintaan tindakan pengguna sehingga mereka hanya diizinkan untuk memodifikasi sumber daya mereka sendiri. Tindakan akan terjadi pada tingkat semi tinggi karenanya menjadi perhatian.
Catatan: Kepemilikan entitas tidak dapat ditransfer dalam kasus penggunaan ini.
Solusi Potensial:
Solusi : Simpan dan simpan daftar sumber daya setiap pengguna dalam sesi server yang didukung oleh sesuatu seperti reddis.
Masalah : Bertahan pada sesi sisi server memiliki kerumitannya sendiri, khususnya penskalaan dengan beberapa server. Ini juga melanggar REST. do-session-really-violate-restfulness untuk informasi lebih lanjut.
Solusi: Lakukan kueri baca sebelum kueri / tindakan pengguna. Yaitu memberi saya item pengguna ini maka jika dalam daftar melanjutkan dengan pembaruan.
Kekhawatiran: Overhead dari bacaan tambahan setiap kali pengguna bertindak atas nama sumber daya.
Solusi: Lewatkan id pengguna ke lapisan db dan menjadikannya bagian dari pembaruan bersyarat atau jika Anda ingin menggunakan sesuatu seperti keamanan tingkat baris Postgres untuk sumber daya itu tergantung pada backend data.
Kekhawatiran: Ini agak terlambat dalam siklus hidup permintaan untuk memeriksa apakah sumber daya adalah pengguna yang meminta. Kesalahan harus dibuang jauh-jauh dari backend data. Pada catatan yang sama itu juga akan sedikit keluar dari tempat mengingat otentikasi dan otorisasi berbasis peran sering dilakukan pada awal siklus hidup permintaan. Implementasinya juga tergantung pada backend data. Ini juga mendorong logika bisnis di backend data.
Solusi: Sesi ditandatangani pihak klien. Baik dengan JWT atau cookie terenkripsi / ditandatangani. Pada dasarnya mempertahankan sesi tepercaya yang berisi daftar id sumber daya pengguna.
Kekhawatiran: Ukuran sesi sisi klien. Fakta bahwa itu akan dikirim dengan setiap permintaan bahkan ketika itu tidak diperlukan. Pemeliharaan menjadi sangat kompleks ketika Anda memperkenalkan kemungkinan beberapa sesi / klien aktif. Bagaimana Anda memperbarui status sisi klien ketika sumber daya ditambahkan pada klien lain.
Solusi: Berikan token pembaruan yang ditandatangani (JWT) atau url ke klien dengan sumber daya saat sumber diambil. Harapkan bahwa ketika sumber daya diperbarui / ditindaklanjuti. Token yang ditandatangani akan berisi id pengguna dan id sumber daya dan Anda dapat memverifikasi dengan mudah terhadap itu.
Kekhawatiran: Mendapat kompleksitas jika kepemilikan sumber daya dapat ditransfer tetapi dalam kasus saya itu bukan masalah. Memperkenalkan kompleksitas pada pembacaan sebelum pembaruan. Agak aneh?
Pikiran terakhir:
Saya condong ke solusi terakhir, tetapi karena saya tidak melihat itu terjadi sangat sering saya bertanya-tanya apakah saya kehilangan sesuatu? atau mungkin itu bagian dari pola desain yang tidak saya ketahui.
Jawaban:
Saya pikir ada dua solusi dalam daftar Anda yang paling sesuai dengan kasus penggunaan Anda; permintaan baca sebelum pembaruan (solusi 2) dan mengirim token pembaruan dengan permintaan baca (solusi 5).
Jika Anda khawatir tentang kinerja, saya akan memutuskan satu atau yang lain tergantung pada berapa banyak membaca vs pembaruan ke sumber daya yang Anda harapkan. Jika Anda mengharapkan pembaruan jauh lebih banyak daripada membaca, maka solusi 5 jelas lebih baik, karena Anda tidak perlu melakukan database tambahan untuk mencari tahu pemilik sumber daya.
Namun, Anda tidak boleh melupakan implikasi keamanan dari pemberian token pembaruan. Dengan solusi 2, anggap otentikasi aman, kemudian memperbarui sumber daya mungkin juga aman karena Anda menentukan pemilik sumber daya di server.
Dengan solusi 5, Anda tidak memeriksa ulang klaim yang dibuat klien, kecuali Anda memeriksa tanda tangan yang valid. Jika Anda membiarkan pembaruan terjadi melalui tautan teks-bersih (mis., Tidak ada SSL), maka hanya menyandikan sumber daya dan id pengguna di token tidak aman. Untuk satu, Anda membuka diri untuk memutar ulang serangan, jadi Anda harus memasukkan angka yang tumbuh dengan setiap permintaan. Juga, jika Anda tidak menyandikan stempel waktu / tanggal kedaluwarsa dalam token pembaruan, Anda pada dasarnya memberikan akses pembaruan tidak terbatas kepada siapa pun yang memiliki token itu. Akhirnya, jika Anda tidak menginginkan notce, maka Anda setidaknya harus menyertakan hmac dari sumber daya yang diambil, sehingga token pembaruan hanya berlaku untuk keadaan sumber daya seperti yang diambil. Ini membuat serangan replay lebih sulit dan selanjutnya membatasi pengetahuan kerusakan dari token pembaruan dapat dilakukan,
Saya pikir bahkan jika Anda berkomunikasi melalui tautan aman, menambahkan angka (atau setidaknya hmac dari keadaan sumber daya) dan tanggal kedaluwarsa untuk token pembaruan akan menjadi hal yang cerdas untuk dilakukan. Saya tidak tahu domain aplikasi, tetapi Anda mungkin berurusan dengan pengguna jahat dan mereka mungkin menghancurkan semua jenis malapetaka dengan kekuatan akses pembaruan tanpa batas ke sumber daya mereka sendiri.
Menambahkan nonce berarti kolom tambahan per sumber daya di database Anda tempat Anda menyimpan nilai nonce yang Anda kirim dengan permintaan pengambilan terakhir. Anda dapat menghitung hmac melalui representasi serial json dari sumber daya Anda. Anda kemudian dapat mengirim token Anda bukan sebagai bagian dari badan pesan tetapi sebagai header HTTP tambahan.
Oh, dan saya akan menggunakan token, bukan URL; di REST, URL pembaruan untuk sumber daya yang diberikan harus URL yang sama dengan sumber daya diambil, bukan? Saya akan memindahkan semua hal terkait otentikasi & otorisasi ke header HTTP, mis. Anda bisa memberikan token pembaruan di Header Otorisasi dari permintaan PUT.
Perhatikan bahwa menambahkan nonce yang tumbuh dengan masing-masing pengambilan sumber daya juga akan memerlukan pembaruan basis data (nilai baru untuk nonce) untuk setiap permintaan pengambilan sumber daya (meskipun Anda bisa lolos dengan hanya memperbarui ketidaksesuaian ketika keadaan sumber daya benar-benar berubah , jadi itu akan bebas dari sudut pandang kinerja), kecuali jika Anda ingin menyimpan informasi itu dalam memori sementara dan hanya meminta klien mencoba lagi ketika server Anda restart antara pengambilan dan permintaan pembaruan.
Catatan tambahan untuk solusi Anda 4 (sesi sisi klien): Bergantung pada jumlah sumber daya yang dapat dibuat pengguna Anda, ukuran sesi mungkin tidak menjadi masalah. Masalah pembaruan sesi sisi klien mungkin cukup mudah untuk diselesaikan, juga: Jika permintaan pembaruan ke sumber daya gagal karena sumber daya tidak tercantum dalam data sesi yang Anda terima dari klien Anda, tetapi pengguna itu benar, maka Anda melakukan periksa di backend apakah data sesi dari klien itu sudah usang. Jika ya, Anda mengizinkan pembaruan dan mengirim kembali cookie yang diperbarui dengan jawaban atas permintaan pembaruan. Ini hanya akan menyebabkan pencarian basis data ketika pengguna mencoba memperbarui sumber daya dari klien dengan data sesi lokal yang kedaluwarsa (atau ketika ia berbahaya dan mencoba untuk melakukan Anda, tetapi tingkat membatasi untuk penyelamatan!). Namun, Saya setuju bahwa solusi 4 lebih rumit daripada yang lain dan Anda lebih baik dengan salah satu ide Anda yang lain. Solusi 4 juga memiliki berbagai pertimbangan keamanan yang harus Anda perhitungkan; menyimpan status otorisasi sebanyak ini pada klien benar-benar membutuhkan keamanan Anda agar kedap air.
sumber
Solusi lain yang dapat Anda pertimbangkan:
Jika sumber daya benar-benar tidak dapat ditransfer ke pengguna lain setelah dibuat, Anda dapat berpikir tentang penyandian id pengguna di id sumber daya.
Misalnya semua sumber daya milik pengguna 'alice' diberi nama 'alice-1', 'alice-2' dll.
Maka sepele untuk memeriksa apakah 'eve' memiliki akses ke sumber daya bernama 'alice-1'.
Jika Anda tidak ingin kepemilikan sumber daya menjadi pengetahuan publik, Anda dapat menggunakan fungsi hash kriptografis seperti:
Hasilkan
hmac(password, u | '-' | r )
, di mana | adalah penggabungan. Gunakan hasilnya, bersama dengan nama sumber daya r, sebagai id dari sumber daya. Secara opsional tambahkan nilai garam (seperti untuk menyimpan kata sandi).Ketika permintaan pembaruan datang dari 'alice' pengguna untuk id sumber daya yang diberikan, Anda mengekstrak r dari id sumber daya, menghitung hmac (kata sandi, 'alice' | '-' | r) dan membandingkannya dengan bagian yang tersisa dari id sumber daya . Jika cocok, Alice diizinkan untuk memperbarui sumber daya, jika tidak cocok, dia tidak diotorisasi. Dan tanpa kata sandi, tidak ada yang bisa mengetahui siapa yang memiliki sumber daya mana.
Saya pikir itu aman, tetapi Anda mungkin ingin memoles kualitas hmacs.
sumber
Tampaknya ada dua masalah terpisah yang perlu Anda kelola. Mereka adalah (1) Bagaimana Anda mengotentikasi pengguna yang membuat permintaan? dan (2) bagaimana Anda tahu objek back end mana yang dikaitkan dengan pengguna itu. Saya juga memiliki pengamatan yang mungkin cocok untuk kasus penggunaan Anda (3)
(1) Otentikasi pengguna dengan cara tenang tidak pernah cantik. Saya cenderung lebih suka menggunakan header HTTP Authentication / Authorization dan tanggapan HTTP 401 untuk memicu otentikasi, yang berarti bahwa Anda berpotensi mengotentikasi pengguna pada setiap permintaan. Karena ini adalah aplikasi seluler, Anda memiliki opsi untuk membuat skema otentikasi kustom Anda sendiri, yang dapat memungkinkan Anda untuk menyediakan token sesi daripada rincian otentikasi lengkap dalam permintaan di masa mendatang.
(2) Pengguna yang terkait dengan satu set objek tentu saja data kunci yang perlu Anda simpan bersama dengan objek aktual di penyimpanan data Anda. Sebagai bagian dari logika bisnis Anda, perlu memeriksa siapa pemilik objek dan membandingkannya dengan identitas yang mengakses objek. Saya akan melakukan ini secara eksplisit dalam kode, sebagai bagian dari lapisan bisnis yang memvalidasi permintaan. Jika hit basis data adalah masalah yang cukup besar, maka masuk akal untuk menyimpan informasi ini di penyimpanan nilai kunci, seperti memcache atau (untuk volume yang sangat tinggi, situs yang sangat tersedia, riak) dan hanya mengenai basis data jika cache dingin untuk kombinasi pengguna / objek ini.
(3) Jika Anda memiliki jumlah pengguna dan objek yang cukup kecil, Anda bisa menggabungkan id pengguna dan objek ke dalam bidang yang sama, dan menggunakan bidang ini sebagai kunci utama database untuk tabel objek. misalnya angka 64bit memberikan 24 bit ruang untuk ID pengguna dan 40 bit ruang untuk id objek. Dengan begitu, ketika Anda meminta objek, Anda bisa 100% yakin itu untuk pengguna Anda.
sumber