Strategi untuk menghasilkan pengidentifikasi unik dan aman untuk digunakan dalam aplikasi web "terkadang offline"

47

Saya memiliki proyek berbasis web yang memungkinkan pengguna untuk bekerja baik online maupun offline dan saya sedang mencari cara untuk menghasilkan id unik untuk catatan di sisi klien. Saya ingin pendekatan yang berfungsi saat pengguna offline (tidak dapat berbicara dengan server), dijamin unik, dan aman. Dengan "aman", saya secara khusus khawatir tentang klien yang mengirimkan duplikat id (jahat atau sebaliknya) dan dengan demikian mendatangkan malapetaka pada integritas data.

Saya sudah melakukan beberapa googling, berharap ini sudah masalah yang diselesaikan. Saya belum menemukan apa pun yang sangat pasti, terutama dalam hal pendekatan yang digunakan dalam sistem produksi. Saya menemukan beberapa contoh untuk sistem di mana pengguna hanya akan mengakses data yang telah mereka buat (misalnya daftar Todo yang diakses di beberapa perangkat, tetapi hanya oleh pengguna yang membuatnya). Sayangnya, saya butuh sesuatu yang sedikit lebih canggih. Saya memang menemukan beberapa ide yang sangat bagus di sini , yang sejalan dengan cara saya berpikir bahwa segala sesuatunya mungkin berhasil.

Di bawah ini adalah solusi yang saya usulkan.

Beberapa persyaratan

  1. ID harus unik secara global (atau setidaknya unik dalam sistem)
  2. Dihasilkan pada klien (yaitu melalui javascript di browser)
  3. Aman (seperti yang dijelaskan di atas dan lainnya)
  4. Data dapat dilihat / diedit oleh banyak pengguna, termasuk pengguna yang tidak mengarangnya
  5. Tidak menyebabkan masalah kinerja yang signifikan untuk backend db (seperti MongoDB atau CouchDB)

Solusi yang Diusulkan

Saat pengguna membuat akun, mereka akan diberikan uuid yang dihasilkan oleh server dan dikenal unik di dalam sistem. Id ini TIDAK harus sama dengan token otentikasi pengguna. Sebut id ini pengguna "id token".

Ketika pengguna membuat catatan baru, mereka menghasilkan uuid baru dalam javascript (dihasilkan menggunakan window.crypto bila tersedia. Lihat contoh di sini ). Id ini digabungkan dengan "id token" yang diterima pengguna ketika mereka membuat akun mereka. ID komposit baru ini (token id sisi server + sisi klien uuid) sekarang menjadi pengidentifikasi unik untuk catatan. Ketika pengguna sedang online dan mengirimkan catatan baru ini ke server backend, server akan:

  1. Identifikasi ini sebagai tindakan "sisipkan" (yaitu bukan pembaruan atau penghapusan)
  2. Validasi kedua bagian kunci komposit adalah uuids yang valid
  3. Validasi bahwa bagian "id token" yang disediakan dari id komposit benar untuk pengguna saat ini (yaitu cocok dengan token id yang diberikan server kepada pengguna ketika mereka membuat akun mereka)
  4. Jika semuanya copasetic, insert data ke db (berhati-hati untuk melakukan insert dan bukan "upsert" sehingga jika id tidak sudah ada tidak update data yang sudah ada dengan kesalahan)

Kueri, pembaruan, dan penghapusan tidak memerlukan logika khusus. Mereka hanya akan menggunakan id untuk catatan dengan cara yang sama seperti aplikasi tradisional.

Apa kelebihan dari pendekatan ini?

  1. Kode klien dapat membuat data baru saat offline dan tahu id untuk catatan itu segera. Saya mempertimbangkan pendekatan alternatif di mana id sementara akan dihasilkan pada klien yang nantinya akan ditukar dengan id "final" ketika sistem sedang online. Namun, ini terasa sangat rapuh. Terutama ketika Anda mulai berpikir tentang membuat data anak dengan kunci asing yang juga perlu diperbarui. Belum lagi berurusan dengan url yang akan berubah ketika id berubah.

  2. Dengan membuat id gabungan dari nilai yang dihasilkan klien DAN nilai server yang dihasilkan, setiap pengguna secara efektif membuat id di kotak pasir. Ini dimaksudkan untuk membatasi kerusakan yang dapat dilakukan oleh klien jahat / jahat. Juga, setiap tabrakan id adalah berdasarkan per pengguna, bukan global ke seluruh sistem.

  3. Karena token id pengguna diikatkan ke akun mereka, id hanya dapat dibuat di kotak pasir pengguna oleh klien yang diautentikasi (yaitu tempat pengguna berhasil masuk). Ini dimaksudkan untuk menjaga klien jahat dari membuat id buruk untuk pengguna. Tentu saja jika token pengguna otomatis dicuri oleh klien jahat, mereka dapat melakukan hal-hal buruk. Tapi, begitu token autentik telah dicuri, akun itu dikompromikan. Jika hal ini terjadi, kerusakan yang terjadi akan terbatas pada akun yang dikompromikan (bukan seluruh sistem).

Kekhawatiran

Inilah beberapa keprihatinan saya dengan pendekatan ini

  1. Apakah ini menghasilkan id yang cukup unik untuk aplikasi skala besar? Apakah ada alasan untuk berpikir ini akan menghasilkan tabrakan id? Bisakah javascript menghasilkan uuid yang cukup agar ini berfungsi? Sepertinya window.crypto cukup banyak tersedia dan proyek ini sudah membutuhkan browser yang cukup modern. ( pertanyaan ini sekarang memiliki pertanyaan SO sendiri )

  2. Apakah ada celah yang saya lewatkan yang dapat memungkinkan pengguna jahat untuk membahayakan sistem?

  3. Apakah ada alasan untuk khawatir tentang kinerja DB ketika meminta kunci komposit yang terdiri dari 2 uuids. Bagaimana seharusnya id ini disimpan untuk kinerja terbaik? Dua bidang terpisah atau satu bidang objek tunggal? Apakah akan ada pendekatan "terbaik" yang berbeda untuk Mongo vs Couch? Saya tahu bahwa memiliki kunci primer non-sekuensial dapat menyebabkan masalah kinerja penting saat melakukan sisipan. Apakah akan lebih pintar untuk memiliki nilai yang dibuat secara otomatis untuk kunci utama dan menyimpan id ini sebagai bidang terpisah? ( pertanyaan ini sekarang memiliki pertanyaan SO sendiri )

  4. Dengan strategi ini, akan mudah untuk menentukan bahwa serangkaian catatan tertentu dibuat oleh pengguna yang sama (karena mereka semua akan membagikan token id yang terlihat secara publik yang sama). Meskipun saya tidak melihat masalah langsung dengan ini, selalu lebih baik untuk tidak membocorkan lebih banyak info tentang detail internal daripada yang dibutuhkan. Kemungkinan lain adalah dengan hash kunci komposit, tetapi sepertinya itu mungkin lebih banyak masalah daripada nilainya.

  5. Jika ada tabrakan id untuk pengguna, tidak ada cara mudah untuk memulihkan. Saya kira klien dapat menghasilkan id baru, tetapi ini sepertinya banyak pekerjaan untuk kasus tepi yang benar-benar tidak boleh terjadi. Saya berniat untuk membiarkan ini tidak terselesaikan.

  6. Hanya pengguna terautentikasi yang dapat melihat dan / atau mengedit data. Ini adalah batasan yang dapat diterima untuk sistem saya.

Kesimpulan

Apakah di atas rencana yang masuk akal? Saya menyadari beberapa dari ini datang ke panggilan penilaian berdasarkan pemahaman yang lebih lengkap dari aplikasi yang bersangkutan.

herbrandson
sumber
Saya pikir pertanyaan ini mungkin menginterupsi Anda stackoverflow.com/questions/105034/... Juga ini membacakan kepada saya seperti GUID tetapi mereka tampaknya tidak asli dalam javascript
Rémi
2
Menurut saya, UUID sudah memenuhi 5 persyaratan yang tercantum. Mengapa tidak mencukupi?
Gabe
@ Gabe Lihat komentar saya pada posting ryan berbohong di bawah ini
herbrandson
meta diskusi pertanyaan ini: meta.stackoverflow.com/questions/251215/…
agas
"klien jahat / pemalsuan" - nakal.
David Conrad

Jawaban:

4

Pendekatan Anda akan berhasil. Banyak sistem manajemen dokumen menggunakan jenis pendekatan ini.

Satu hal yang perlu dipertimbangkan adalah Anda tidak perlu menggunakan user uuid dan id item acak sebagai bagian dari string. Sebagai gantinya, Anda dapat hash gabungan keduanya. Ini akan memberi Anda pengenal yang lebih pendek, dan mungkin beberapa manfaat lainnya karena id yang dihasilkan akan lebih merata (lebih seimbang untuk pengindeksan, dan penyimpanan file jika Anda menyimpan file berdasarkan pada cairannya).

Opsi lain yang Anda miliki adalah menghasilkan hanya cairan sementara untuk setiap item. Kemudian ketika Anda terhubung dan mempostingnya ke server, server menghasilkan (dijamin) uuid untuk setiap item dan mengembalikannya kepada Anda. Anda kemudian memperbarui salinan lokal Anda.

GrandmasterB
sumber
2
Saya telah mempertimbangkan menggunakan hash 2 sebagai id. Namun, bagi saya tampaknya tidak ada cara yang cocok untuk menghasilkan sha256 di semua browser yang saya perlukan :(
herbrandson
12

Anda perlu memisahkan dua masalah:

  1. Pembuatan ID: klien harus dapat menghasilkan pengidentifikasi unik dalam sistem terdistribusi
  2. Masalah keamanan: klien HARUS memiliki token otentikasi pengguna yang valid DAN token otentikasi valid untuk objek yang sedang dibuat / dimodifikasi

Solusi untuk keduanya sayangnya terpisah; tapi untungnya mereka tidak kompatibel.

Kekhawatiran tentang pembuatan ID mudah diselesaikan dengan menghasilkan dengan UUID, itulah yang dirancang untuk UUID; Namun masalah keamanan akan mengharuskan Anda melakukan pemeriksaan di server bahwa token otentikasi yang diberikan diizinkan untuk operasi (yaitu jika token auth untuk pengguna yang tidak memiliki izin yang diperlukan pada objek tertentu, maka itu HARUS ditolak).

Ketika ditangani dengan benar, tabrakan tidak akan menimbulkan masalah keamanan (pengguna atau klien hanya akan diminta untuk mencoba kembali operasi dengan UUID lain).

Lie Ryan
sumber
Ini adalah poin yang sangat bagus. Mungkin hanya itu yang diperlukan dan saya terlalu memikirkannya. Namun, saya memiliki beberapa kekhawatiran tentang pendekatan ini. Yang terbesar adalah bahwa uuids yang dihasilkan javascript tampaknya tidak acak seperti yang diharapkan (lihat komentar di stackoverflow.com/a/2117523/13181 untuk penahanan). Tampaknya window.crypto harus menyelesaikan masalah ini, tetapi sepertinya tidak tersedia di semua versi browser yang perlu saya dukung.
herbrandson
lanjutan ... Saya menyukai saran Anda untuk menambahkan kode di klien yang akan membuat ulang uuid baru dalam kasus tabrakan. Namun, menurut saya hal ini memperkenalkan kembali kekhawatiran yang saya miliki di pos saya di bawah poin # 1 dari bagian "Apa kelebihan dari pendekatan ini". Saya berpikir bahwa jika saya menempuh rute itu, saya akan lebih baik hanya membuat id sementara di sisi klien dan kemudian memperbarui mereka dengan "id akhir" dari server yang pernah terhubung
herbrandson
dilanjutkan lagi ... Selanjutnya, memungkinkan pengguna untuk mengirimkan id unik mereka sendiri sepertinya adalah masalah keamanan. Mungkin ukuran uuid dan statistik yang tinggi dari tabrakan sudah cukup untuk mengurangi masalah ini di dalam dan dari diri mereka sendiri. Saya tidak yakin. Saya punya kecurigaan yang mengganggu bahwa menjaga setiap pengguna di "kotak pasir" mereka sendiri adalah ide yang bagus dalam kasus ini (yaitu jangan percaya pada input pengguna).
herbrandson
@herbrandson: Tidak ada masalah keamanan yang dapat saya pikirkan dalam memungkinkan pengguna untuk menghasilkan id unik mereka sendiri selama Anda selalu memeriksa bahwa pengguna memiliki izin untuk operasi. ID hanyalah sesuatu yang dapat digunakan untuk mengidentifikasi objek, tidak peduli apa nilainya. Satu-satunya potensi bahaya adalah bahwa pengguna dapat memesan berbagai ID untuk penggunaan mereka sendiri, tetapi itu tidak benar-benar menimbulkan masalah pada sistem secara keseluruhan karena pengguna lain sama tidak mungkin untuk sampai pada nilai-nilai itu secara kebetulan.
Lie Ryan
Terima kasih atas tanggapan Anda. Ini benar-benar memaksa saya untuk mengklarifikasi pemikiran saya! Ada alasan mengapa saya waspada dengan pendekatan Anda, dan saya telah melupakannya sepanjang jalan :). Ketakutan saya terkait dengan RNG yang buruk di banyak browser. Untuk generasi uuid, orang akan lebih memilih RNG yang kuat secara kriptografis. Banyak browser yang lebih baru memilikinya melalui window.crypto, tetapi browser lama tidak. Karena hal ini, mungkin bagi pengguna jahat untuk mengetahui benih pengguna lain RNG dan dengan demikian mengetahui pengguna berikutnya yang akan dihasilkan. Ini adalah bagian yang rasanya bisa diserang.
herbrandson