JSON Web Jetonlarını Geçersiz Kılma


421

Üzerinde çalıştığım yeni bir node.js projesi için, çerez tabanlı oturum yaklaşımından geçiş yapmayı düşünüyorum (bununla, bir kullanıcının tarayıcısında kullanıcı oturumlarını içeren bir anahtar / değer deposuna bir kimlik depolamak) JSON Web Belirteçlerini (jwt) kullanarak belirteç tabanlı bir oturum yaklaşımına (anahtar / değer deposu yok).

Proje socket.io kullanan bir oyundur - jeton tabanlı bir oturuma sahip olmak, tek bir oturumda birden çok iletişim kanalının (web ve socket.io) olacağı böyle bir senaryoda yararlı olacaktır.

Jwt Yaklaşımı kullanılarak sunucudan jeton / oturum geçersiz kılma nasıl sağlanır?

Ayrıca, bu tür bir paradigma ile hangi yaygın (veya nadir) tuzakları / saldırıları dikkat etmem gerektiğini anlamak istedim. Örneğin, bu paradigma, oturum deposu / çerez tabanlı yaklaşımla aynı / farklı saldırı türlerine karşı savunmasızsa.

Yani, aşağıdakileri (uyarlanan söylüyorlar bu ve bu ):

Oturum Mağaza Girişi:

app.get('/login', function(request, response) {
    var user = {username: request.body.username, password: request.body.password };
    // Validate somehow
    validate(user, function(isValid, profile) {
        // Create session token
        var token= createSessionToken();

        // Add to a key-value database
        KeyValueStore.add({token: {userid: profile.id, expiresInMinutes: 60}});

        // The client should save this session token in a cookie
        response.json({sessionToken: token});
    });
}

Jeton Bazlı Giriş:

var jwt = require('jsonwebtoken');
app.get('/login', function(request, response) {
    var user = {username: request.body.username, password: request.body.password };
    // Validate somehow
    validate(user, function(isValid, profile) {
        var token = jwt.sign(profile, 'My Super Secret', {expiresInMinutes: 60});
        response.json({token: token});
    });
}

-

Oturum Deposu yaklaşımı için oturum kapatma (veya geçersiz kılma), belirtilen belirteci olan KeyValueStore veritabanında bir güncelleştirme gerektirir.

Token tabanlı yaklaşımda böyle bir mekanizma olmayacak gibi görünüyor çünkü token kendisi normalde anahtar / değer deposunda var olacak bilgileri içerecektir.


1
'Express-jwt' paketini kullanıyorsanız, isRevokedseçeneğe bakabilir veya aynı işlevselliği çoğaltmayı deneyebilirsiniz. github.com/auth0/express-jwt#revoked-tokens
Signus

1
Erişim belirtecinde kısa bir sona erme süresi kullanmayı ve kullanıcının bir veritabanındaki erişim durumunun (kara listeye alma) kontrol edilmesini sağlamak için uzun ömürlü bir yenileme belirteci kullanmayı düşünün. auth0.com/blog/…
Rohmer

başka bir seçenek, jwt jetonu oluştururken ve aynı IP adresi için gelen istekle birlikte depolanan IP'yi kontrol ederken, yüke IP adresi eklemek olabilir. örnek: nodeJ'lerde req.connection.remoteAddress. Müşteri başına statik IP vermeyen ISS sağlayıcıları var, bir istemci internete yeniden bağlanmadıkça bunun bir sorun olmayacağını düşünüyorum.
Gihan Sandaru

Yanıtlar:


392

Ben de bu soruyu araştırıyorum ve aşağıdaki fikirlerin hiçbiri tam çözüm olmasa da, başkalarının fikirleri dışlamasına veya başka şeyler sağlamasına yardımcı olabilirler.

1) Jetonu istemciden çıkarın

Açıkçası bu, sunucu tarafı güvenliği için hiçbir şey yapmaz, ancak jetonu varlığından kaldırarak bir saldırganı durdurur (yani, oturumu kapatmadan önce jetonu çalmaları gerekirdi).

2) Jeton kara listesi oluşturma

Geçersiz belirteçleri ilk son kullanma tarihlerine kadar saklayabilir ve gelen isteklerle karşılaştırabilirsiniz. Her istek için veritabanına dokunmanız gerektiğinden, bu ilk etapta tamamen jetona gitme nedenini ortadan kaldırıyor gibi görünüyor. Depolama boyutu büyük olasılıkla daha düşük olacaktır, çünkü yalnızca oturum kapatma ve son kullanma süresi arasındaki jetonları depolamanız gerekir (bu bir bağırsak hissidir ve kesinlikle bağlama bağlıdır).

3) Jeton son kullanma sürelerini kısa tutun ve sık sık döndürün

Jetonun sona erme sürelerini yeterince kısa aralıklarla tutarsanız ve çalışan istemcinin gerektiğinde güncellemeleri izlemesini ve güncellemesini istemeniz durumunda, 1 numaralı numara tam bir oturum kapatma sistemi olarak etkili bir şekilde çalışır. Bu yöntemle ilgili sorun, kullanıcının istemci kodunun kapatılması arasında (süre sonu aralığını ne kadar süre yaptığınıza bağlı olarak) oturum açmayı imkansız kılmasıdır.

Acil Durum planları

Bir acil durum meydana gelmişse veya bir kullanıcı belirtecinin güvenliği ihlal edilmişse, yapabileceğiniz bir şey, kullanıcının temel kimlik bilgileriyle oturum açma kimlik bilgilerini değiştirmesine izin vermektir. İlişkili kullanıcı artık bulunamayacağından, ilişkili tüm belirteçler geçersiz hale gelir.

Ayrıca, son giriş tarihini jetonla birlikte eklemenin iyi bir fikir olduğunu belirtmek istedim, böylece uzak bir süre sonra yeniden giriş yapabilmeniz mümkün.

Jeton kullanan saldırılarla ilgili benzerlikler / farklılıklar açısından, bu yazı şu soruyu ele almaktadır: https://github.com/dentarg/blog/blob/master/_posts/2014-01-07-angularjs-authentication-with-cookies -vs-token.markdown


3
Mükemmel yaklaşım. Bağırsaklar, 3'ün tümünün bir kombinasyonunu yapmak ve / veya her "n" talebinden sonra (bir zamanlayıcının aksine) yeni bir token talep etmek olacaktır. Bellek içi nesne depolaması için redis kullanıyoruz ve bunu durum # 2 için kolayca kullanabiliriz ve sonra gecikme YOLU azalır.
Aaron Wagner

2
Bu kodlama korku direği bazı tavsiyeler sunar: Oturum taşıyan çerezleri (veya jetonları) kısa tutun, ancak # 3 ile uyumlu gibi görünen kullanıcı için görünmez yapın. Kendi bağırsaklarım (belki de daha geleneksel olduğu için) sadece jetonun (veya bunun bir karmasının) beyaz listelenen oturum veritabanına (# 2'ye benzer) bir anahtar olarak hareket etmek
funseiki

8
Makale iyi yazılmış ve 2)yukarıdakilerin ayrıntılı bir versiyonudur . İyi çalışıyor olsa da, şahsen geleneksel oturum mağazalarında çok fazla fark görmüyorum. Depolama gereksinimi daha düşük olurdu, ama yine de bir veritabanı gerekir sanırım. JWT'nin benim için en büyük cazibesi, oturumlar için hiç bir veritabanı kullanmamaktı.
Matt Way

211
Bir kullanıcı parolasını değiştirdiğinde jetonları geçersiz kılmak için yaygın bir yaklaşım, jetonu bir parolasının karmasıyla imzalamaktır. Böylece şifre değişirse, önceki jetonlar otomatik olarak doğrulanamaz. Bunu, kullanıcının kaydına son oturum kapatma zamanı ekleyerek ve jetonu imzalamak için son oturum kapatma zamanı ve parola karmasının bir birleşimini kullanarak oturumu kapatmak için genişletebilirsiniz. Bu, belirteç imzasını her doğrulamanız gerektiğinde bir DB araması gerektirir, ancak muhtemelen kullanıcıyı yine de arıyorsunuz.
Travis Terry

4
Bir kara liste bellekte tutularak verimli hale getirilebilir, böylece DB geçersiz kılmaları kaydetmek ve süresi dolmuş geçersiz kılmaları kaldırmak ve sadece sunucu açılışında okumak için vurulması gerekir. Bir yük dengeleme mimarisi altında, bellek içi kara liste, DB'yi 10 saniye gibi kısa aralıklarla yoklayabilir ve geçersiz kılınmış belirteçlerin maruz kalmasını sınırlayabilir. Bu yaklaşımlar, sunucunun istek başına DB erişimi olmadan istekleri doğrulamaya devam etmesini sağlar.
Joe Lapp

86

Yukarıda yayınlanan fikirler iyi, ancak mevcut JWT'leri geçersiz kılmanın çok basit ve kolay bir yolu sırrını değiştirmek.

Sunucunuz JWT'yi oluşturuyorsa, bunu bir sırla (JWS) imzalar ve daha sonra istemciye gönderir, sırrın değiştirilmesi tüm mevcut belirteçleri geçersiz kılar ve tüm kullanıcıların eski belirteçleri aniden geçersizleştikçe kimlik doğrulaması için yeni bir belirteç kazanmasını gerektirir. sunucuya.

Gerçek belirteç içeriğinde (veya arama kimliğinde) herhangi bir değişiklik yapılmasını gerektirmez.

Açıkçası bu, yalnızca mevcut tüm belirteçlerin süresinin dolmasını istediğinizde, belirteç sona erme süresi için yukarıdaki çözümlerden biri gereklidir (kısa belirteç süre sonu süresi veya belirteç içinde saklanan bir anahtarı geçersiz kılmak gibi) için geçerlidir.


9
Bence bu yaklaşım ideal değil. Çalışıyor ve kesinlikle basit olsa da, ortak bir anahtar kullandığınız bir durumu düşünün - tek bir jetonu geçersiz kılmak istediğinizde o anahtarı gitmek ve yeniden oluşturmak istemezsiniz.
Signus

1
@KijanaWoodard, RS256 algoritmasındaki imzayı etkili bir şekilde sır olarak doğrulamak için bir genel / özel anahtar çifti kullanılabilir. Burada gösterilen örnekte bir JWT'yi geçersiz kılmak için sırrın değiştirildiğinden bahsediyor. Bu, a) imzayla uyuşmayan sahte bir pubkey sokulması veya b) yeni bir pubkey oluşturulması ile yapılabilir. Bu durumda, idealden daha azdır.
Signus

1
@Signus - yakaladım. ortak anahtarı gizli olarak kullanmaz, ancak diğerleri imzayı doğrulamak için ortak anahtara güveniyor olabilir.
Kijana Woodard

8
Bu çok kötü bir çözüm. JWT kullanmanın temel nedeni vatansız ve ölçeklidir. Dinamik bir sır kullanmak bir durumu ortaya çıkarır. Hizmet birden çok düğümde kümelenmişse, her yeni simge yayınlandığında sırrı senkronize etmeniz gerekir. Sırları, çerez tabanlı kimlik doğrulamasını yeniden icat edecek bir veritabanında veya başka bir harici hizmette depolamanız gerekir
Tuomas Toivonen

5
@TuomasToivonen, ancak bir JWT'yi bir sır ile imzalamalı ve aynı sır ile JWT'yi doğrulayabilmelisiniz. Bu yüzden sırrı korunan kaynaklarda saklamanız gerekir. Sır tehlikeye düşerse, onu değiştirmeli ve bu değişikliği düğümlerinizin her birine dağıtmalısınız. Kümeleme / ölçeklendirme içeren barındırma sağlayıcıları genellikle bu sırları kolay ve güvenilir bir şekilde dağıtmak için sırlarını hizmetlerinde saklamanızı sağlar.
Rohmer

67

Bu öncelikle @mattway'in cevabını destekleyen ve geliştiren uzun bir yorum

Verilen:

Bu sayfadaki önerilen diğer çözümlerden bazıları, her istek üzerine veri deposuna ulaşmayı savunmaktadır. Her kimlik doğrulama isteğini doğrulamak için ana veri deposuna basarsanız, diğer yerleşik belirteç kimlik doğrulama mekanizmaları yerine JWT kullanmak için daha az neden görüyorum. Her seferinde veri deposuna giderseniz vatansız yerine JWT'yi durum bilgisi haline getirdiniz.

(Siteniz yüksek miktarda yetkisiz istek alırsa, JWT veri deposuna çarpmadan onları reddeder, bu da yararlıdır. Muhtemelen böyle başka kullanım durumları vardır.)

Verilen:

Durum bilgisi olmayan JWT kimlik doğrulaması tipik, gerçek dünyadaki bir web uygulaması için gerçekleştirilemez, çünkü durum bilgisi olmayan JWT'nin aşağıdaki önemli kullanım durumları için anında ve güvenli destek sağlamanın bir yolu yoktur :

Kullanıcının hesabı silinir / engellenir / askıya alınır.

Kullanıcının şifresi değiştirildi.

Kullanıcının rolleri veya izinleri değiştirilir.

Kullanıcı yönetici tarafından oturumu kapattı.

JWT belirtecindeki diğer önemli uygulama verileri site yöneticisi tarafından değiştirilir.

Bu durumlarda jetonun sona ermesini bekleyemezsiniz. Belirteç geçersiz kılma derhal gerçekleşmelidir. Ayrıca, istemcinin kötü niyetli olarak veya olmasın eski belirtecin bir kopyasını kullanmamasına ve kullanmamasına güvenemezsiniz.

Bu nedenle: @ matt-way, # 2 TokenBlackList'in yanıtı, gerekli durumu JWT tabanlı kimlik doğrulamasına eklemenin en etkili yolu olacağını düşünüyorum.

Son kullanma tarihine kadar bu simgeleri tutan bir kara listeniz var. Jeton listesi, toplam kullanıcı sayısına kıyasla oldukça küçük olacaktır, çünkü yalnızca sona erene kadar kara listeye alınan tokenleri tutmak zorundadır. Yeniden anahtarlama, memcached veya bir anahtarda son kullanma süresi ayarlamayı destekleyen başka bir bellek içi veri deposuna geçersiz kılınan jetonlar koyarak uygularım.

Hala ilk JWT yetkilendirmesini geçen her kimlik doğrulama isteği için bellek içi db'nizi aramanız gerekir, ancak tüm kullanıcı grubunuz için anahtarları orada saklamanız gerekmez. (Bu, belirli bir site için önemli olabilir veya olmayabilir.)


15
Cevabınıza katılmıyorum. Veritabanına vurmak hiçbir şeyi bildirmez; arka ucunda devlet depolamak yapar. JWT, her istek üzerine veritabanına çarpmanız gerekmediği için oluşturulmamıştır. JWT kullanan her büyük uygulama bir veritabanı ile desteklenmektedir. JWT tamamen farklı bir problemi çözer. en.wikipedia.org/wiki/Stateless_protocol
Julian

6
@Julian bunu biraz ayrıntılandırabilir misin? O zaman JWT hangi problemi gerçekten çözüyor?
zero01alpha

8
@ zero01alpha Kimlik Doğrulama: Bu, JWT kullanmak için en yaygın senaryodur. Kullanıcı oturum açtıktan sonra, sonraki her istek JWT'yi içerecek ve kullanıcının bu belirteçle izin verilen rotalara, hizmetlere ve kaynaklara erişmesine izin verecektir. Bilgi Değişimi: JSON Web Jetonları, taraflar arasında güvenli bir şekilde bilgi iletmenin iyi bir yoludur. JWT'ler imzalanabildiğinden, gönderenlerin söyledikleri kişi olduğundan emin olabilirsiniz. Bkz. Jwt.io/introduction
Julian

7
@Julian Anlaşmazlığınızı kabul etmiyorum :) JWT, herhangi bir müşteri için yetkilendirme bilgileri sağlayan merkezi bir kuruluşa erişme ihtiyacını karşılamak için sorunu (hizmetler için) çözer. Bu nedenle, A hizmeti ve B hizmeti yerine, X istemcisinin bir şey yapma iznine sahip olup olmadığını öğrenmek için bazı kaynaklara erişmesi gerekir; A ve B hizmeti, X'ten izinlerini kanıtlayan bir token alır (en yaygın olarak 3. Parti). Her neyse, JWT, bir sistemdeki hizmetler arasında, özellikle birden fazla servis sağlayıcı tarafından kontrol edildiklerinde, paylaşılan bir durumdan kaçınmaya yardımcı olan bir araçtır.
LIvanov

1
Ayrıca jwt.io/introduction If the JWT contains the necessary data, the need to query the database for certain operations may be reduced, though this may not always be the case.
giantas

43

Kullanıcı modelinde jwt sürüm numarasının kaydını tutardım. Yeni jwt jetonları sürümlerini buna ayarlayacaktı.

Jwt'yi doğruladığınızda, kullanıcının geçerli jwt sürümüne eşit bir sürüm numarası olup olmadığını kontrol edin.

Eski jwts'ları geçersiz kılmak istediğinizde, kullanıcıların jwt sürüm numarasını çarpmanız yeterlidir.


15
Bu ilginç bir fikir, tek şey sürümün nerede saklanacağı, belirteçlerin amacının bir parçası olarak vatansız olması ve veritabanını kullanması gerekmiyor. Sabit kodlanmış bir sürüm çarpmayı zorlaştıracak ve veritabanındaki bir sürüm numarası belirteçleri kullanmanın bazı avantajlarını ortadan kaldıracaktır.
Stephen Smith

13
Muhtemelen zaten bir kullanıcı kimliğini jetonunuzda saklıyorsunuz ve daha sonra kullanıcının api uç noktasına erişiminin / yetkilendirildiğini kontrol etmek için veritabanını sorguluyorsunuz. Yani jwt token sürüm numarasını kullanıcı ile karşılaştırarak ekstra db sorguları yapmıyorsunuz.
DaftMonk

5
Muhtemelen söylememeliyim, çünkü veritabanına hiç dokunmayan doğrulamalarla belirteçleri kullanabileceğiniz birçok durum var. Ama bence bu durumda kaçınmak zor.
DaftMonk

11
Kullanıcı birden fazla cihazdan oturum açarsa ne olur? Bir jeton hepsinde kullanılmalı mı yoksa giriş öncekileri mi geçersiz kılmalı?
meeDamian

10
@SergioCorrea ile hemfikirim Bu, JWT'yi neredeyse diğer tüm belirteç kimlik doğrulama mekanizmaları kadar durumsal hale getirecektir.
Ed J

40

Bunu henüz denemedim ve diğer bazı cevaplara dayanan çok fazla bilgi kullanıyor. Buradaki karmaşıklık, kullanıcı bilgileri için istek başına sunucu tarafı veri deposu çağrısından kaçınmaktır. Diğer çözümlerin çoğu, bir kullanıcı oturum deposuna istek başına db araması gerektirir. Bu belirli senaryolarda sorun değil ama bu tür çağrıları önlemek ve sunucu tarafı durumunun çok küçük olmasını gerekli kılmak için yaratıldı. Tüm güç geçersizleştirme özelliklerini sağlamak için küçük olsa da, bir sunucu tarafı oturumu yeniden oluşturacaksınız. Ama burada yapmak istiyorsanız, buradaki öz:

Hedefler:

  • Bir veri deposunun kullanımını azaltın (eyaletsiz).
  • Tüm kullanıcıların oturumlarını kapatmaya zorlama yeteneği.
  • Herhangi bir kişiyi herhangi bir zamanda oturumu kapatmaya zorlama yeteneği.
  • Belli bir süre sonra tekrar şifre girilmesini gerektirme.
  • Birden fazla müşteriyle çalışabilme.
  • Kullanıcı belirli bir istemciden oturumu kapattığında yeniden oturum açmayı zorlayabilme. (Birisi, kullanıcı yürüdükten sonra bir istemci belirtecinin "silinmesini" önlemek için - ek bilgi için yorumlara bakın)

Çözüm:

  • Daha uzun ömürlü (birkaç saat) depolanan müşteriyle eşleştirilmiş kısa ömürlü (<5m) erişim belirteçleri kullanın yenileme belirteci kullanın .
  • Her istek, geçerlilik ya da yenileme jetonunun son geçerlilik tarihini kontrol eder.
  • Erişim belirtecinin süresi dolduğunda, istemci erişim belirtecini yenilemek için yenileme belirtecini kullanır.
  • Yenileme belirteci denetimi sırasında, sunucu küçük bir kullanıcı kimliği kara listesini denetler - bulunursa yenileme isteğini reddeder.
  • Bir istemcinin geçerli (süresi dolmamış) yenileme veya kimlik doğrulama belirteci yoksa, kullanıcının tüm diğer istekleri reddedileceği için yeniden oturum açması gerekir.
  • Oturum açma isteğinde, kullanıcı veri deposunda yasak olup olmadığını kontrol edin.
  • Oturum açıldığında - oturum açmaları için bu kullanıcıyı oturum kara listesine ekleyin. Çoklu cihaz ortamındaki tüm cihazlardan çıkış yapmamak için ek bilgileri depolamanız gerekir, ancak cihazınıza bir cihaz alanı ekleyerek yapılabilir kullanıcı kara listesi.
  • X zaman geçtikten sonra yeniden girişi zorlamak için - kimlik doğrulama simgesindeki son giriş tarihini koruyun ve istek başına kontrol edin.
  • Tüm kullanıcıların oturumunu kapatmaya zorlamak için - simge karma anahtarını sıfırlayın.

Bu, kullanıcı tablosunun yasaklanmış kullanıcı bilgileri içerdiği varsayılarak sunucuda bir kara liste (durum) tutmanızı gerektirir. Geçersiz oturumlar kara listesi - kullanıcı kimlikleri listesidir. Bu kara liste yalnızca yenileme jetonu isteği sırasında kontrol edilir. Yenileme belirteci TTL sürece girişlerin üzerinde yaşaması gerekir. Yenileme belirtecinin süresi dolduğunda kullanıcının tekrar oturum açması gerekir.

Eksileri:

  • Yenileme belirteci isteğinde bir veri deposu araması yapmak için yine de gereklidir.
  • Geçersiz kartlar erişim kodunun TTL'si için çalışmaya devam edebilir.

Artıları:

  • İstenilen işlevselliği sağlar.
  • Yenileme belirteci eylemi, normal çalışma sırasında kullanıcıdan gizlenir.
  • Yenileme isteklerinde her istek yerine yalnızca veri deposu araması yapılması gerekir. yani saniyede 1 yerine her 15 dakikada bir.
  • Sunucu tarafı durumunu çok küçük bir kara listeye indirir.

Bu çözümle, reddis gibi bir bellek veri deposuna gerek yoktur, en azından kullanıcı bilgileri için değil, çünkü sunucu sadece 15 dakikada bir db çağrısı yapıyor. Reddis kullanılıyorsa, geçerli / geçersiz bir oturum listesini orada saklamak çok hızlı ve daha basit bir çözüm olacaktır. Yenileme belirtecine gerek yok. Her kimlik doğrulama belirtecinin bir oturum kimliği ve aygıt kimliği vardır, oluşturma sırasında reddis tablosunda saklanabilir ve uygun olduğunda geçersiz kılınabilir. Daha sonra her istekte kontrol edilir ve geçersiz olduğunda reddedilir.


Bir kişinin bir bilgisayardan başka bir kişinin aynı bilgisayarı kullanmasına izin verdiği senaryoya ne dersiniz? 1. kişi oturumu kapatır ve oturumu kapatmanın 2. kişiyi anında engellemesini bekler. 2. kişi ortalama bir kullanıcıysa, müşteri belirteci silerek kullanıcıyı kolayca engelleyebilir. Ancak 2. kullanıcının bilgisayar korsanlığı becerileri varsa, kullanıcının ilk kullanıcı olarak kimlik doğrulaması yapmak için hala geçerli belirteci kurtarma zamanı vardır. Görünüşe göre, gecikmeden belirteçleri hemen geçersiz kılma ihtiyacından kaçınmanın bir yolu yoktur.
Joe Lapp

5
Veya JWT'nizi sesion / yerel depolama veya çerezden kaldırabilirsiniz.
Kamil Kiełczewski

1
Teşekkürler @Ashtonian. Kapsamlı bir araştırma yaptıktan sonra JWT'leri terk ettim. Gizli anahtarı güvence altına almak için olağanüstü uzunluklara gitmedikçe veya güvenli bir OAuth uygulamasına yetki vermedikçe, JWT'ler normal oturumlardan çok daha savunmasızdır. Raporumun tamamına bakın: by.jtl.xyz/2016/06/the-unspoken-vulnerability-of-jwts.html
Joe Lapp

2
Kara listeye almanın anahtarı, bir yenileme belirteci kullanmaktır. Büyük açıklama: auth0.com/blog/…
Rohmer

1
Kısa ömürlü bir erişim belirtecini kara listeye alınabilen uzun ömürlü bir yenileme belirteciyle birleştirdiği için bana en iyi yanıt bu gibi görünüyor. Oturum kapatıldığında, istemci erişim belirtecini silmelidir, böylece 2. bir kullanıcı erişemez (erişim belirtecinin oturumu kapattıktan sonra birkaç dakika daha geçerli kalmasına rağmen). @Joe Lapp, bir bilgisayar korsanının (2. kullanıcı) silindikten sonra bile erişim kodunu aldığını söylüyor. Nasıl?
M3RS

14

Düşündüğüm bir yaklaşım iatJWT'de her zaman (verilen bir) değere sahip olmaktır . Daha sonra bir kullanıcı oturumu kapattığında, o zaman damgasını kullanıcı kaydında saklayın. JWT'yi doğrularken iatson oturum kapatılan zaman damgasıyla karşılaştırın. Eğer iateski, o zaman geçerli değil. Evet, DB'ye gitmek zorundasınız, ancak JWT başka şekilde geçerliyse her zaman kullanıcı kaydını yine de çekeceğim.

Bunu gördüğüm en büyük dezavantajı, birden fazla tarayıcıda veya mobil bir istemciye sahip olmaları durumunda tüm oturumlarından çıkış yapmalarıdır.

Bu aynı zamanda bir sistemdeki tüm JWT'leri geçersiz kılmak için iyi bir mekanizma olabilir. Çekin bir kısmı, son geçerli iatzamanın global bir zaman damgasına karşı olabilir .


1
Güzel düşünce! "Bir cihaz" sorununu çözmek, bunu bir çıkış yerine bir acil durum özelliği haline getirmektir. Kullanıcı kaydında, ondan önce verilen tüm simgeleri geçersiz kılan bir tarih saklayın. Gibi bir şey token_valid_after, ya da bir şey. Müthiş!
OneHoopyFrood

1
Hey @OneHoopyFrood fikri daha iyi anlamama yardımcı olacak bir örnek kodunuz var mı? Yardımın için sağol!
alexventuraio

2
Önerilen tüm diğer çözümler gibi, bu da bu sorunun var olmasının sebebi olan bir veritabanı araması gerektirir, çünkü bu aramadan kaçınmak en önemli şeydir! Performans, ölçeklenebilirlik. Normal şartlar altında, kullanıcı verilerine sahip olmak için bir DB aramasına ihtiyacınız yoktur, zaten istemciden elde edersiniz.
Rob Evans

9

Buraya biraz geç kaldım ama bence iyi bir çözümüm var.

Veritabanımda parolanın en son değiştirildiği tarih ve saati saklayan bir "last_password_change" sütunu var. Ayrıca JWT'de tarih / saati de saklıyorum. Bir jetonu doğrularken, jeton verildikten sonra parolanın değiştirilip değiştirilmediğini ve jetonun henüz süresi dolmamış olsa bile reddedildiğini kontrol ediyorum.


1
Jetonu nasıl reddedersiniz? Kısa bir örnek kod gösterebilir misiniz?
alexventuraio

1
if (jwt.issue_date < user.last_pw_change) { /* not valid, redirect to login */}
Vanuan

15
Bir db araması gerektirir!
Rob Evans

5

DB'nizde, kullanıcı belgenizde / kaydınızda "last_key_used" alanına sahip olabilirsiniz.

Kullanıcı kullanıcı ile oturum açıp geçirdiğinde, yeni bir rastgele dize oluşturun, last_key_used alanında saklayın ve jetonu imzalarken bu yüke ekleyin.

Kullanıcı belirteci kullanarak oturum açtığında, belirteçteki ile eşleşmesi için DB'deki last_key_used öğesini kontrol edin.

Daha sonra, örneğin bir oturum kapatma işlemi gerçekleştirdiğinde veya belirteci geçersiz kılmak istediğinizde, bu "last_key_used" alanını başka bir rastgele değere değiştirin ve sonraki kontroller başarısız olur, böylece kullanıcıyı kullanıcı ile oturum açmaya ve tekrar geçirmeye zorlar.


Bu benim düşündüğüm çözüm, ancak şu dezavantajları var: (1) ya rastgele kontrol etmek için her istekte bir DB arama yapıyorsunuz (oturumlar yerine jeton kullanma nedenini geçersiz kılıyor) ya da yalnızca bir yenileme belirtecinin süresi dolduktan sonra aralıklı olarak denetleme (kullanıcıların hemen çıkış yapmasını veya oturumların hemen sonlandırılmasını önleme); ve (2) oturumu kapatmak kullanıcının tüm tarayıcılardan ve tüm cihazlardan çıkış yapmasını sağlar (geleneksel olarak beklenen davranış değildir).
Joe Lapp

Kullanıcı oturumu kapattığında anahtarı değiştirmenize gerek yoktur, yalnızca parolalarını değiştirdiklerinde veya
verdiyseniz

3

Bunun gibi bir bellek içi liste saklayın

user_id   revoke_tokens_issued_before
-------------------------------------
123       2018-07-02T15:55:33
567       2018-07-01T12:34:21

Jetonlarınızın süresi bir hafta içinde dolarsa, bundan daha eski kayıtları temizleyin veya yok sayın. Ayrıca her kullanıcının yalnızca en son kaydını tutun. Listenin boyutu, jetonlarınızı ne kadar süreyle sakladığınıza ve kullanıcıların jetonlarını ne sıklıkta iptal ettiğine bağlı olacaktır. Db'yi yalnızca tablo değiştiğinde kullanın. Uygulamanız başladığında tabloyu belleğe yükleyin.


2
Çoğu üretim sitesi birden fazla sunucuda çalışır, bu nedenle bu çözüm çalışmaz. Redis veya benzeri interpocess önbellek eklemek sistemi önemli ölçüde karmaşıklaştırır ve genellikle çözümlerden daha fazla sorun getirir.
user2555515

@ user2555515 tüm sunucular veritabanı ile senkronize edilebilir. Her zaman veritabanına isabet ya da değil seçimdir. Hangi sorunları getirdiğini söyleyebilirsin.
Eduardo

3

------------------------ Bu cevap için biraz geç ama belki birine yardımcı olacak ------------- -----------

İstemci Tarafından , en kolay yol, belirteci tarayıcının deposundan kaldırmaktır.

Ancak, Düğüm sunucusundaki belirteci yok etmek isterseniz -

JWT paketi ile ilgili sorun, jetonu yok etmek için herhangi bir yöntem veya yol sunmamasıdır. Yukarıda belirtilen JWT ile ilgili farklı yöntemler kullanabilirsiniz. Ama burada jwt-redis ile gidiyorum.

Yani sunucu tarafında jetonu yok etmek için JWT yerine jwt-redis paketini kullanabilirsiniz .

Bu kütüphane (jwt-redis), önemli bir ek ile jsonwebtoken kütüphanesinin tüm işlevselliğini tamamen tekrarlar. Jwt-redis, geçerliliği doğrulamak için jeton etiketini redis içinde saklamanızı sağlar. Bir yeniden etiketin redis'te olmaması, jetonu geçersiz kılar. Jwt-redis'de jetonu yok etmek için bir yok etme yöntemi var

şu şekilde çalışır:

1) npm'den jwt-redis'i yükleyin

2) Oluşturmak -

var redis = require('redis');
var JWTR =  require('jwt-redis').default;
var redisClient = redis.createClient();
var jwtr = new JWTR(redisClient);

jwtr.sign(payload, secret)
    .then((token)=>{
            // your code
    })
    .catch((error)=>{
            // error handling
    });

3) Doğrulamak için -

jwtr.verify(token, secret);

4) Yok etmek -

jwtr.destroy(token)

Not : jetonun imzalanması sırasında JWT'de sağlananla aynı şekilde expiresIn sağlayabilirsiniz.

Belki birine yardım edecek


2

Neden sadece jti iddiasını (nonce) kullanmıyorsunuz ve bunu bir listede kullanıcı kaydı alanı olarak saklamıyorsunuz (db bağımlı, ama en azından virgülle ayrılmış bir liste gayet iyi)? Başka bir aramaya gerek yoktur, çünkü diğerleri muhtemelen kullanıcı kaydını yine de almak istersiniz ve bu şekilde farklı müşteri örnekleri için birden fazla geçerli jetonunuz olabilir ("her yerde oturum aç" listeyi boş olarak sıfırlayabilir)


Evet bu. Kullanıcı tablosu ile yeni (oturum) tablosu arasında bire çok ilişki kurabilir, böylece jti talepleriyle birlikte meta verileri depolayabilirsiniz.
Peter Lada

2
  1. Jetonlar için 1 günlük süre sonu süresi verin
  2. Günlük bir kara liste oluşturun.
  3. Geçersiz / oturum kapatma jetonlarını kara listeye ekleyin

Jeton doğrulaması için, önce jeton son kullanma süresini ve ardından jetonun süresi dolmamışsa kara listeyi kontrol edin.

Uzun oturum ihtiyaçları için jeton son kullanma süresini uzatmak için bir mekanizma olmalıdır.


4
jetonları kara listeye koy ve vatansızlığınız gider
Kerem Baydoğan

2

Partinin geç saatlerinde, bazı araştırmalardan sonra MY iki sent aşağıda verilmiştir. Çıkış sırasında, aşağıdakilerin gerçekleştiğinden emin olun ...

İstemci depolamasını / oturumunu temizle

Kullanıcı tablosu son oturum açma tarihi ve oturum kapatma tarih-saatini sırasıyla oturum açma veya oturum kapatma gerçekleştiğinde güncelleyin. Bu nedenle, giriş tarihi saatinin her zaman oturumdan daha büyük olması gerekir (Veya geçerli durum giriş yapılırsa ve henüz çıkış yapılmadıysa çıkış tarihini boş bırakın)

Bu, ek kara liste tablosunu tutmak ve düzenli olarak boşaltmaktan çok daha basittir. Birden fazla cihaz desteği, oturum açmak için ek tablo gerektirir, oturum kapatma tarihleri, işletim sistemi veya istemci ayrıntıları gibi bazı ek ayrıntılarla birlikte.


2

Kullanıcı dizesi başına benzersiz ve genel dize birlikte karma

JWT gizli kısmı olarak hizmet etmek hem bireysel hem de global jeton geçersiz kılmaya izin verir. İstek kimlik doğrulaması sırasında bir db arama / okuma maliyetinde maksimum esneklik. Ayrıca nadiren değiştiği için önbellekleme de kolaydır.

İşte bir örnek:

HEADER:ALGORITHM & TOKEN TYPE

{
  "alg": "HS256",
  "typ": "JWT"
}
PAYLOAD:DATA

{
  "sub": "1234567890",
  "some": "data",
  "iat": 1516239022
}
VERIFY SIGNATURE

HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload), 
  HMACSHA256('perUserString'+'globalString')
)

where HMACSHA256 is your local crypto sha256
  nodejs 
    import sha256 from 'crypto-js/sha256';
    sha256(message);

örneğin kullanım bkz. https://jwt.io (dinamik 256 bit sırları işlediklerinden emin değilim)


1
Biraz daha detay yeterli olacaktır
giantas

2
@giantas, sanırım Mark'ın imza kısmı ne anlama geliyor. Bu nedenle, JWT'yi imzalamak için tek bir anahtar kullanmak yerine, her istemci için benzersiz bir anahtar birleştirin. Bu nedenle, bir kullanıcının tüm oturumunu geçersiz kılmak istiyorsanız, o kullanıcının anahtarını değiştirmeniz yeterlidir ve sisteminizdeki tüm oturumu geçersiz kılmak istiyorsanız, o genel tek anahtarı değiştirmeniz yeterlidir.
Tommy Aria Pradana

1

Bunu şu şekilde yaptım:

  1. Bir üret unique hash, sonra depolar REDIS ve JWT'de . Buna oturum denilebilir
    • Ayrıca , belirli bir JWT'nin yaptığı istek sayısını da depolayacağız - Sunucuya bir jwt her gönderildiğinde, isteklerin tamsayısını arttırırız. (bu isteğe bağlıdır)

Bir kullanıcı oturum açtığında, benzersiz karma oluşturulduğunda Yani, REDIS saklanan ve enjekte JWT'de .

Bir kullanıcı çalışır korumalı bir son nokta ziyarete ettiğinizde, benzersiz bir oturum karma kapmak edeceğiz JWT , sorgu REDIS ve bir maç olmadığını görmek!

Bundan uzatabilir ve JWT'mizi daha da güvenli hale getirebiliriz , şöyle:

Belirli bir JWT'nin yaptığı her X isteği , yeni ve benzersiz bir oturum oluşturur, JWT'mizde saklar ve bir öncekini kara listeye alırız .

Bu, JWT'nin sürekli değiştiği ve bayat JWT'nin saldırıya uğraması, çalınması veya başka bir şeyin durması anlamına gelir .


1
Jetonun kendisine hash verebilir ve jetona yeni bir hash enjekte etmek yerine bu değeri redis halinde saklayabilirsiniz.
Mart'ta

Ayrıca kontrol audve jtiJWT iddiaları, doğru yolda.
Peter Lada

1

Kullanıcı belirteçlerini iptal etmek istiyorsanız, DB'nizde verilen tüm belirteçleri takip edebilir ve oturum benzeri bir tabloda geçerli olup olmadığını (var olup olmadığını) kontrol edebilirsiniz. Dezavantajı her istek üzerine DB vurmak olacak.

Ben denemedim, ama DB isabet minimum tutarken belirteç iptal izin vermek için aşağıdaki yöntemi öneririz -

Veritabanı kontrol oranını düşürmek için, verilen tüm JWT jetonlarını bazı deterministik ilişkilere göre X gruplarına bölün (ör. Kullanıcı kimliğinin ilk basamağına göre 10 grup).

Her JWT jetonu grup kimliğini ve jeton oluşturulduğunda oluşturulan bir zaman damgasını tutacaktır. Örneğin,{ "group_id": 1, "timestamp": 1551861473716 }

Sunucu tüm grup kimliklerini bellekte tutacak ve her grubun o gruba ait bir kullanıcının son oturum kapatma olayının ne zaman olduğunu gösteren bir zaman damgası olacaktır. Örneğin,{ "group1": 1551861473714, "group2": 1551861487293, ... }

Daha eski bir grup zaman damgasına sahip bir JWT belirtecine sahip isteklerin geçerliliği (DB vuruşu) kontrol edilir ve geçerliyse, müşterinin gelecekteki kullanımı için yeni bir zaman damgasına sahip yeni bir JWT belirteci verilir. Simgenin grup zaman damgası daha yeniyse, JWT'ye (DB isabetsiz) güveniriz.

Yani -

  1. JWT belirtecini yalnızca, belirtecin eski bir grup zaman damgası varsa DB'yi kullanarak doğrularız, ancak gelecekteki istekler kullanıcının grubundaki bir kişi oturumu kapatıncaya kadar doğrulanmaz.
  2. Grupları, zaman damgası değişikliklerinin sayısını sınırlamak için kullanırız (yarın yokmuş gibi giriş yapan bir kullanıcı var demektir - herkes yerine yalnızca sınırlı sayıda kullanıcıyı etkiler)
  3. Bellekte tutulan zaman damgası miktarını sınırlamak için grup sayısını sınırlıyoruz
  4. Bir jetonu geçersiz kılmak çok kolaydır; yalnızca oturum tablosundan kaldırın ve kullanıcı grubu için yeni bir zaman damgası oluşturun.

Aynı liste bellekte tutulabilir (c # için uygulama) ve her istek için db'ye çarpma ihtiyacını ortadan kaldıracaktır. Liste uygulama başlangıcında
db'den

1

"Tüm cihazlardan çıkış" seçeneği kabul edilebilirse (çoğu durumda geçerlidir):

  • Jeton sürümü alanını kullanıcı kaydına ekleyin.
  • Bu alandaki değeri JWT'de depolanan taleplere ekleyin.
  • Kullanıcı her çıkış yaptığında sürümü artırın.
  • Simgeyi doğrularken sürüm talebini kullanıcı kaydında depolanan sürümle karşılaştırın ve aynı değilse reddedin.

Çoğu durumda kullanıcı kaydını almak için bir db gezisi yine de gereklidir, bu yüzden bu doğrulama sürecine fazla yük eklemez. Birleştirme veya ayrı bir çağrı kullanma gereği nedeniyle DB yükünün önemli olduğu bir kara listenin bakımının aksine, eski kayıtları temizleyin ve bu şekilde devam edin.


0

JWT kullanırken tüm cihazlardan çıkış sağlamamız gerekiyorsa cevaplayacağım. Bu yaklaşım, her istek için veritabanı aramaları kullanır. Çünkü sunucu çökse bile kalıcı bir güvenlik durumuna ihtiyacımız var. Kullanıcı tablosunda iki sütun olacak

  1. LastValidTime (varsayılan: oluşturma süresi)
  2. Giriş Yapıldı (varsayılan: doğru)

Kullanıcının oturumunu kapatma isteği olduğunda, LastValidTime öğesini geçerli saate ve Oturum Açılan değerini false olarak güncelleyeceğiz. Bir oturum açma isteği varsa, LastValidTime öğesini değiştirmeyiz, ancak Giriş Yapıldı true değerine ayarlanır.

JWT'yi oluşturduğumuzda, yükte JWT oluşturma süresine sahip olacağız. Bir hizmet için yetki verdiğimizde 3 koşulu kontrol edeceğiz

  1. JWT geçerli mi?
  2. JWT yükü oluşturma süresi, Kullanıcı LastValidTime değerinden fazla mı?
  3. Kullanıcı Oturum Açtı mı

Pratik bir senaryo görelim.

X kullanıcısında iki cihaz A, B vardır. A cihazını ve B cihazını kullanarak akşam saat 7'de sunucumuza giriş yaptı. A ve B'nin her ikisinde oluşturulan JWT var

Saat 9'da cihaz B'yi kaybetti. Hemen A cihazından çıkış yapıyor. Yani artık veritabanı X kullanıcı girişimiz LastValidTime'a "ThatDate: 9: 00: xx: xxx" ve Loged-In "false" olarak giriş yapıyor.

9: 30'da Mr.Thief, B cihazını kullanarak oturum açmaya çalışır.

Bay X saat 10'dan itibaren A cihazından oturum açın. Şimdi A cihazında JWT oluşturuldu ve saat: 10 pm. Oturum Açılan veritabanı "true" olarak ayarlandı

22: 30'da BayThief giriş yapmaya çalışır. Giriş Yapılmış olmasına rağmen. LastValidTime veritabanında 21:00, ancak B'nin JWT'si 19:00 olarak zaman oluşturdu. Bu yüzden hizmete erişmesine izin verilmeyecek. Bu nedenle, B şifresini kullanmadan, bir cihaz oturumunu kapattıktan sonra zaten oluşturulmuş JWT'yi kullanamaz.


0

Keycloak (üzerinde çalıştığım) gibi IAM çözümü, Token İptal bitiş noktası gibi

Jeton İptali Bitiş Noktası /realms/{realm-name}/protocol/openid-connect/revoke

Bir kullanıcı (veya kullanıcı) oturumunu kapatmak istiyorsanız, bir bitiş noktası da çağırabilirsiniz (bu sadece Jetonları geçersiz kılar). Yine, Keycloak söz konusu olduğunda, Güvenen Parti'nin sadece son noktayı araması gerekir

/realms/{realm-name}/protocol/openid-connect/logout

Daha fazla bilgi edinmek istiyorsanız bağlantı kurun


-1

Bu, her jeton doğrulamasında DB araması olmadan çözülmesi gerçekten zor görünüyor. Aklıma gelen alternatif, sunucu tarafında geçersiz kılınan jetonların kara listesini tutmaktır; mevcut kara listeyi yüklemek için sunucunun yeniden başlatma sırasında veritabanını kontrol etmesini sağlayarak, değişiklikleri yeniden başlatmaya devam edecek bir değişiklik olduğunda veritabanında güncelleştirilmesi gerekir.

Ancak sunucu belleğinde (global bir değişken değişkeni) tutarsanız, birden fazla sunucu kullanıyorsanız birden fazla sunucuda ölçeklendirilemez, bu durumda bunu paylaşılan bir Redis önbelleğinde tutabilirsiniz. yeniden başlatılması gerektiğinde verileri bir yerde (veritabanı? dosya sistemi?) devam ettirecek şekilde ayarlayın ve her yeni sunucu açıldığında Redis önbelleğine abone olması gerekir.

Kara listeye alternatif olarak, aynı çözümü kullanarak, bu yanıtı oturum başına redis olarak kaydedilmiş bir karma ile yapabilirsiniz. işaret ettiği (giriş yapan birçok kullanıcının daha verimli olacağından emin değilsiniz).

Kulağa oldukça karmaşık geliyor mu? bana öyle geliyor!

Feragatname: Redis kullanmadım.


-1

Axios veya benzer bir söz tabanlı http istek lib kullanıyorsanız, .then()parçanın içindeki ön uçta jetonu yok edebilirsiniz . Kullanıcı bu işlevi yürüttükten sonra yanıt .then () bölümünde başlatılır (sunucu bitiş noktasından sonuç kodu tamam olmalıdır, 200). Kullanıcı veri ararken bu yolu tıkladıktan sonra, veritabanı alanı user_enabledyanlışsa imha jetonunu tetikler ve kullanıcı hemen oturumu kapatır ve korumalı yollara / sayfalara erişmesini durdurur. Kullanıcı kalıcı olarak oturum açmışken belirtecin süresinin dolmasını beklememiz gerekmez.

function searchForData() {   // front-end js function, user searches for the data
    // protected route, token that is sent along http request for verification
    var validToken = 'Bearer ' + whereYouStoredToken; // token stored in the browser 

    // route will trigger destroying token when user clicks and executes this func
    axios.post('/my-data', {headers: {'Authorization': validToken}})
     .then((response) => {
   // If Admin set user_enabled in the db as false, we destroy token in the browser localStorage
       if (response.data.user_enabled === false) {  // user_enabled is field in the db
           window.localStorage.clear();  // we destroy token and other credentials
       }  
    });
     .catch((e) => {
       console.log(e);
    });
}

-3

Simgeyi kullanıcılar tablosuna kaydediyorum, kullanıcı girişinde yeni jetonu güncelleyeceğim ve auth kullanıcı geçerli jwt değerine eşit olduğunda.

Bence bu en iyi çözüm değil, bu benim için çalışıyor.


2
Tabii ki en iyisi değil! Db erişimi olan herkes kolayca herhangi bir kullanıcının kimliğine bürünebilir.
user2555515

1
@ user2555515 Bu çözüm, veritabanında depolanan jetonun şifrelenmesi durumunda, tıpkı veritabanında depolanan parolaların şifrelenmesi gerektiği gibi çalışır. Stateless JWTVe Stateful JWT(oturumlara çok benzer) arasında bir fark vardır . Stateful JWTjeton beyaz listesini korumanın avantajlarından yararlanabilir.
TheDarkIn1978
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.