“Bazen çevrimdışı” bir web uygulamasında kullanılmak üzere benzersiz ve güvenli tanımlayıcılar oluşturma stratejisi


47

Kullanıcıların hem çevrimiçi hem de çevrimdışı çalışmasını sağlayan web tabanlı bir projem var ve müşteri tarafında kayıtlar için benzersiz kimlikler üretmenin bir yolunu arıyorum. Bir kullanıcı çevrimdışıyken (yani bir sunucu ile konuşamıyor) çalışan, benzersiz olduğu garantili ve güvenli bir yaklaşım istiyorum. "Güvenli" olarak, yinelenen kimlikleri (kötü niyetli veya başka şekilde) gönderen ve bu nedenle veri bütünlüğüne zarar veren müşteriler hakkında özellikle endişeleniyorum.

Bazı googling yapıyorum, umarım bu zaten çözülmüş bir problemdir. Özellikle üretim sistemlerinde kullanılan yaklaşımlar açısından çok kesin bir şey bulamadım. Kullanıcıların yalnızca oluşturdukları verilere erişebilecekleri sistemler için bazı örnekler buldum (örneğin, birden fazla cihazda erişilen bir Yapılacaklar listesi, ancak yalnızca oluşturan kullanıcılar tarafından). Ne yazık ki, biraz daha sofistike bir şeye ihtiyacım var. Ben gerçekten iyi fikirler buldunuz burada İşlerin işe yarayabilecek düşünüyordum nasıl ile uyumludur.

Aşağıda benim önerdiğim çözüm.

Bazı Gereksinimler

  1. Kimlikler küresel olarak benzersiz olmalıdır (veya sistem içinde en azından benzersiz olmalıdır)
  2. İstemcide oluşturulmuş (yani tarayıcıda javascript aracılığıyla)
  3. Güvenli (yukarıda belirtildiği gibi)
  4. Veriler, yazmamış kullanıcılar dahil olmak üzere birden fazla kullanıcı tarafından görüntülenebilir / düzenlenebilir
  5. Arka uç db'lerde (MongoDB veya CouchDB gibi) önemli performans sorunlarına neden olmaz

Önerilen çözüm

Kullanıcılar bir hesap oluşturduklarında, sunucu tarafından oluşturulan ve sistemde benzersiz olduğu bilinen bir kullanıcı adı verilir. Bu kimlik, kullanıcıların kimlik doğrulama belirteciyle aynı olmamalıdır. Kullanıcılara "id token" diyelim.

Bir kullanıcı yeni bir kayıt oluşturduğunda, javascript'te yeni bir uuid oluşturur (mevcut olduğunda window.crypto kullanılarak oluşturulur. Buradaki örneklere bakın ). Bu kimlik, kullanıcının hesabını oluştururken aldığı "kimlik simgesi" ile birleştirilir. Bu yeni bileşik kimliği (sunucu tarafı kimliği belirteci + istemci tarafı uuid) şimdi kayıt için benzersiz tanımlayıcıdır. Kullanıcı çevrimiçi olduğunda ve bu yeni kaydı arka uç sunucusuna gönderdiğinde, sunucu şunları yapar:

  1. Bunu bir "ekle" işlemi olarak tanımlayın (yani bir güncelleme veya silme değil)
  2. Bileşik anahtarın her iki parçasını doğrula geçerli uuids
  3. Bileşik kimliğin "kimlik belirteci" kısmının mevcut kullanıcı için doğru olduğunu doğrulayın (yani, hesaplarını oluştururken kullanıcıya atanmış kimliği belirten sunucuyla eşleşir)
  4. Her şey db içine veri ekleme, Copasetic ise (bir ekleme not olarak "Upsert" yapmak için dikkatli olmak çok id eğer yapar zaten yanlışlıkla varolan kaydı güncelleştir vermez var)

Sorgular, güncellemeler ve silme işlemleri özel bir mantık gerektirmez. Sadece kayıt için kimliği geleneksel uygulamalarla aynı şekilde kullanırlardı.

Bu yaklaşımın avantajları nelerdir?

  1. Müşteri kodu çevrimdışıyken yeni veriler oluşturabilir ve bu kaydın kimliğini hemen bilir. İstemci üzerinde geçici bir kimliğin üretileceği ve daha sonra sistem çevrimiçi olduğunda "son" bir kimliğe dönüştürülecek alternatif yaklaşımlar olduğunu düşündüm. Ancak, bu çok kırılgan hissettim. Özellikle yabancı anahtarlarla çocuk verisi oluşturmayı düşünmeye başladığınızda, bunun da güncellenmesi gerekir. Kimlik değiştiğinde değişecek olan URL'lerle uğraşmaktan bahsetmiyorum bile.

  2. ID'leri bir müşteri tarafından üretilen değerin VE bir sunucu tarafından üretilen değerin bir bileşiği haline getirerek, her kullanıcı etkin olarak bir sanal alanda kimlikler oluşturur. Bunun, kötü niyetli / hileli bir müşteri tarafından yapılabilecek zararı sınırlandırması amaçlanmıştır. Ayrıca, herhangi bir kimlik çarpışması tüm sistem için küresel değil kullanıcı bazındadır.

  3. Bir kullanıcı kimliği belirteci hesaplarına bağlı olduğundan, kimlikleri yalnızca kimliği doğrulanan istemciler tarafından bir kullanıcı sanal alanında oluşturulabilir (yani, kullanıcının başarıyla oturum açtığı yer). Bu, kötü niyetli istemcilerin bir kullanıcı için kötü kimlikler oluşturmasını engellemek içindir. Elbette bir kullanıcı kimlik belirteci kötü niyetli bir müşteri tarafından çalınmışsa, kötü şeyler yapabilir. Ancak, bir yetkili belirteç çalındığında, hesap yine de tehlikeye girer. Bunun gerçekleşmesi durumunda, verilen hasar, tehlikeye atılan hesapla sınırlı olacaktır (sistemin tamamı değil).

Endişeler

İşte bu yaklaşımla ilgili endişelerimden bazıları

  1. Bu, büyük ölçekli bir uygulama için yeterince benzersiz kimlikler üretecek mi? Bunun kimlik çarpışmalarına neden olacağını düşünmek için herhangi bir sebep var mı? Javascript bunun çalışması için yeterince rastgele bir uuid oluşturabilir mi? Window.crypto oldukça yaygın bir şekilde kullanılabilir durumda ve bu proje zaten oldukça modern tarayıcılar gerektiriyor. ( bu sorunun şimdi ayrı bir SO sorusu var )

  2. Kötü niyetli bir kullanıcının sistemin güvenliğini tehlikeye sokmasına izin verebilecek herhangi bir boşluk var mı?

  3. 2 uuid'den oluşan bir bileşik anahtar sorgulanırken DB performansı hakkında endişelenmek için bir neden var mı. Bu kimlik en iyi performans için nasıl saklanmalıdır? İki ayrı alan mı yoksa tek bir nesne alanı mı? Mongo'ya Couch'a karşı farklı bir "en iyi" yaklaşım olur mu? Sıralı olmayan bir birincil anahtara sahip olmanın kesici uçlar yaparken dikkate değer performans sorunlarına neden olabileceğini biliyorum. Birincil anahtar için otomatik olarak oluşturulan bir değere sahip olmak ve bu kimliği ayrı bir alan olarak depolamak daha akıllıca olur mu? ( bu sorunun şimdi ayrı bir SO sorusu var )

  4. Bu stratejiyle, aynı kullanıcı tarafından belirli bir kayıt kümesinin oluşturulduğunu belirlemek kolay olacaktır (çünkü herkes aynı genel olarak görülebilen kimlik kartını paylaşır). Bununla ilgili herhangi bir acil sorun görmemekle birlikte, iç detaylar hakkında gerekenden daha fazla bilgi sızdırmamak her zaman daha iyidir. Diğer bir olasılık, bileşik anahtarı kullanmak olabilir, ancak bu değerden daha fazla sorun olabilir gibi görünüyor.

  5. Bir kullanıcı için bir kimlik çarpışması olması durumunda, kurtarmanın basit bir yolu yoktur. Müşterinin yeni bir kimlik oluşturabileceğini düşünüyorum, ancak bu gerçekten gerçekleşmemesi gereken son bir vaka için çok iş gibi görünüyor. Bunu adressiz bırakmak niyetindeyim.

  6. Yalnızca kimliği doğrulanmış kullanıcılar verileri görüntüleyebilir ve / veya düzenleyebilir. Bu benim sistemim için kabul edilebilir bir sınırlamadır.

Sonuç

Makul bir planın üstünde mi? Bunun bir kısmının, söz konusu uygulamayı daha iyi anlayabilmesi için yapılan bir karar çağrısına bağlı olduğunun farkındayım.


Bence bu soru size iyi gelebilir stackoverflow.com/questions/105034/… Ayrıca bu bana GUID gibi okundu ama javascript'te yerli gibi görünmüyorlar
Rémi

2
UUID'lerin listelenen 5 gereksinimi zaten karşıladığını vurguluyor. Neden yetersizler?
Gabe

@Gabe Lie ryans post ile ilgili yorumlarıma bakın
herbrandson

Bu sorunun meta tartışması: meta.stackoverflow.com/questions/251215/…
gnat

"kötü niyetli / yönlendirici müşteri" - haydut.
David Conrad

Yanıtlar:


4

Yaklaşımınız işe yarayacak. Birçok belge yönetim sistemi bu tür bir yaklaşımı kullanır.

Dikkate alınması gereken bir şey, hem uuid kullanıcısını hem de rastgele öğe kimliğini dizenin bir parçası olarak kullanmanıza gerek kalmamasıdır. Bunun yerine her ikisinin de birleşmesini sağlayabilirsiniz. Bu, size daha kısa bir tanımlayıcı ve muhtemelen başka bazı yararlar sağlayacaktır, çünkü sonuçta elde edilen id'ler daha eşit bir şekilde dağıtılacaktır (indeksleme için daha iyi dengelenmiş ve eğer kendi kullanıcı adlarına göre dosyaları saklıyorsanız dosya depolama).

Sahip olduğunuz diğer bir seçenek de, her ürün için sadece geçici bir uuid üretmektir. Ardından bağlanıp bunları sunucuya gönderdiğinizde, sunucu her öğe için (garantili) uuid'ler oluşturur ve bunu size geri getirir. Daha sonra yerel kopyanızı güncellersiniz.


2
2 bir karma kullanarak kimliği kullanmayı düşünmüştüm. Bununla birlikte, bana desteklemesi gereken tüm tarayıcılarda sha256 üretmenin uygun bir yolu olmadığı görülmedi :(
herbrandson

12

İki endişeyi ayırmanız gerekir:

  1. Kimlik oluşturma: istemcinin dağıtık sistemde benzersiz bir tanımlayıcı oluşturabilmesi gerekir
  2. Güvenlik kaygısı: istemci geçerli bir kullanıcı kimlik doğrulama belirtecine sahip OLMALIDIR VE kimlik doğrulama belirteci oluşturulan / değiştirilen nesne için geçerlidir

Bu ikisinin çözümü ne yazık ki ayrı; ama neyse ki onlar uyumsuz değil.

Kimlik oluşturmayla ilgili endişe, UUID ile üretilmek suretiyle kolayca çözülür; Bununla birlikte, güvenlik kaygısı, sunucuda verilen kimlik doğrulama belirtecinin işlem için yetkili olup olmadığını kontrol etmenizi gerektirir (örneğin, kimlik doğrulama belirteci bu belirli nesne için gerekli izne sahip olmayan bir kullanıcı içinse, reddedildi).

Doğru kullanıldığında, çarpışma gerçekten bir güvenlik sorunu oluşturmaz (kullanıcıdan veya istemciden işlemi başka bir UUID ile yeniden denemesi istenir).


Bu gerçekten iyi bir nokta. Belki de tek gerekli olan şey budur ve onu çoktan gözden geçiriyorum. Ancak, bu yaklaşım hakkında birkaç endişem var. Bunlardan en büyüğü, javascript tarafından oluşturulan uuid'lerin umabileceği kadar rastgele görünmediğidir (tutuklamalar için stackoverflow.com/a/2117523/13181 adresindeki yorumlara bakın ). Window.crypto'nun bu sorunu çözmesi gerektiği anlaşılıyor, ancak bu desteklemem gereken tüm tarayıcı sürümlerinde mevcut görünmüyor.
herbrandson

devam ... İstemcide bir çarpışma durumunda yeni bir kullanıcı kimliği oluşturacak kod ekleme öneriniz hoşuma gidiyor. Bununla birlikte, bana bu yazının "Bu yaklaşımın avantajları nelerdir" bölümündeki 1 no'lu maddede bahsettiğim endişeyi tekrar gündeme getirdiği anlaşılıyor. Sanırım bu rotaya gidersem, müşteri tarafında geçici kimlikler oluşturmaktan ve daha sonra bunları bir kez sunucudan "nihai kimlik" ile güncellemekten daha iyi olacağımı düşünüyorum
herbrandson

yine devam etti ... Dahası, kullanıcıların kendi benzersiz kimliklerini göndermelerine izin vermek güvenlikle ilgili bir sorun gibi görünüyor. Belki bir uuidin büyüklüğü ve bir çarpışmanın yüksek istatistiksel olarak imkansızlığı, bu endişeyi kendi başlarına azaltmak için yeterlidir. Emin değilim. Her kullanıcıyı kendi "sanal alanı" nda tutmanın bu durumda sadece iyi bir fikir olduğuna dair endişe uyandırıcı bir şüphem var (yani, kullanıcı girişine güvenmeyin).
herbrandson

@herbrandson: Her zaman, kullanıcının işlem izinlerine sahip olduğunu kontrol ettiğiniz sürece kullanıcıların kendi benzersiz kimliklerini üretmelerine izin vermeyi düşünebileceğim bir güvenlik sorunu yok. Kimlik sadece nesneleri tanımlamak için kullanılabilecek bir şeydir, gerçekten değerinin ne olduğu önemli değildir. Tek olası zarar, kullanıcının kendi kullanımı için çeşitli kimlikler saklayabilmesidir, ancak diğer kullanıcılar da bu değerlere sadece şans eseri olarak ulaşma ihtimalleri düşük olduğu için sisteme bir sorun çıkarmaz.
Yalan Ryan

Geri bildiriminiz için teşekkürler. Düşüncemi netleştirmek için beni gerçekten zorladı! Yaklaşımınıza karşı temkinli olmamın bir nedeni vardı, ve bunu unutmuştum :). Korkum birçok tarayıcıda yoksul RNG'ye bağlı. Uuid nesil için, kriptografik olarak güçlü bir RNG tercih edilir. Yeni tarayıcıların çoğunda bunu window.crypto üzerinden alırlar, ancak eski tarayıcılarda yoktur. Bu nedenle, kötü niyetli bir kullanıcının başka bir kullanıcının RNG'sinin tohumunu bulması ve böylece ortaya çıkacak bir sonraki uuidi bilmesi mümkündür. Bu saldırıya uğradığını hissettiren kısımdır.
herbrandson
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.