Sunucu tarafı geçiş anahtarı kimlik doğrulaması

Genel bakış

Geçiş anahtarı kimlik doğrulamasıyla ilgili temel adımlara genel bakışı burada bulabilirsiniz:

Geçiş anahtarı kimlik doğrulama akışı

  • Geçiş anahtarı ile kimlik doğrulamak için gereken sorgulamayı ve diğer seçenekleri tanımlayın. Bunları, geçiş anahtarı kimlik doğrulama aramanıza (web'de navigator.credentials.get) iletebilmeniz için istemciye gönderin. Kullanıcı, geçiş anahtarı kimlik doğrulamasını onayladıktan sonra geçiş anahtarı kimlik doğrulama çağrısı çözümlenir ve bir kimlik bilgisi (PublicKeyCredential) döndürür. Kimlik bilgisi, bir kimlik doğrulama onayı içerir.
  • Kimlik doğrulama onayını doğrulayın.
  • Kimlik doğrulama onayı geçerliyse kullanıcının kimliğini doğrulayın.

Aşağıdaki bölümlerde her adımın ayrıntıları açıklanmaktadır.

Meydan okumayı oluşturun

Pratikte sorgulama, ArrayBuffer nesnesi olarak temsil edilen bir rastgele bayt dizisidir.

// Example challenge, base64URL-encoded
weMLPOSx1VfSnMV6uPwDKbjGdKRMaUDGxeDEUTT5VN8

Yarışmanın amacına ulaşmasını sağlamak için şunları yapmalısınız:

  1. Aynı giriş sorgulamasının hiçbir zaman bir defadan fazla kullanılmadığından emin olun. Her oturum açma denemesinde yeni bir giriş sorgulaması oluştur. Başarılı veya başarısız olsa da her oturum açma denemesinden sonra sorgulamayı silin. Ayrıca, belirli bir süre sonra meydan okumayı sil. Bir yanıtta aynı sorgulamayı hiçbir zaman bir defadan fazla kabul etmeyin.
  2. Sorgunun kriptografik olarak güvenli olduğundan emin olun. Bir zorluğun tahmin edilmesi neredeyse imkansız olmalıdır. Sunucu tarafında kriptografik olarak güvenli bir giriş sorgulaması oluşturmak için güvendiğiniz bir FIDO sunucu tarafı kitaplığını kullanmanız en iyisidir. Bunun yerine kendi girişlerinizi oluşturursanız teknoloji yığınınızda bulunan yerleşik şifreleme işlevini kullanın veya kriptografik kullanım alanları için tasarlanmış kitaplıkları arayın. Node.js'de iso-crypto veya Python'daki secrets örnek olarak verilebilir. Spesifikasyona göre, sorgulamanın güvenli olarak kabul edilmesi için en az 16 bayt uzunluğunda olması gerekir.

Bir egzersiz oluşturduktan sonra, daha sonra doğrulamak için bu testi kullanıcının oturumuna kaydedin.

Kimlik bilgisi isteği seçeneklerini oluşturma

publicKeyCredentialRequestOptions nesnesi olarak kimlik bilgisi isteği seçenekleri oluşturun.

Bunu yapmak için FIDO sunucu tarafı kitaplığınızı kullanın. Bu özellik genellikle sizin için bu seçenekleri oluşturabilecek bir yardımcı program işlevi sunar. SimpleWebAuthn teklifleri (örneğin, generateAuthenticationOptions).

publicKeyCredentialRequestOptions, geçiş anahtarı kimlik doğrulaması için gereken tüm bilgileri içermelidir. Bu bilgileri, FIDO sunucu tarafı kitaplığınızda bulunan publicKeyCredentialRequestOptions nesnesini oluşturmaktan sorumlu olan işleve iletin.

publicKeyCredentialRequestOptions'lik alanların bazıları sabit değer olabilir. Diğerleri sunucuda dinamik olarak tanımlanmalıdır:

  • rpId: Kimlik bilgisinin ilişkilendirilmesini beklediğiniz Kısıtlanmış Taraf Kimliği (ör. example.com). Kimlik doğrulama, yalnızca burada sağladığınız RP Kimliği, kimlik bilgisiyle ilişkilendirilen RP Kimliği ile eşleşirse başarılı olur. RP kimliğini doldurmak için kimlik bilgisi kaydı sırasında publicKeyCredentialCreationOptions alanında belirlediğiniz RP Kimliği ile aynı değeri kullanın.
  • challenge: Kimlik doğrulama isteği yapılırken kullanıcının şifre anahtarını elinde bulundurduğunu kanıtlamak için geçiş anahtarı sağlayıcısının imzalayacağı veri parçasıdır. Ayrıntıları Yarışma oluşturma başlıklı makalede inceleyebilirsiniz.
  • allowCredentials: Bu kimlik doğrulama için kabul edilebilir kimlik bilgileri dizisidir. Kullanıcının, tarayıcı tarafından gösterilen listeden kullanılabilir bir geçiş anahtarı seçmesine izin vermek için boş bir dizi iletin. Ayrıntılı bilgi için Get a egzersiz from the RP server (RP sunucusundan bir giriş sorgulaması alma) ve Keşfedilebilir kimlik bilgilerinin ayrıntılı incelemesi sayfalarını inceleyin.
  • userVerification: Cihaz ekran kilidi kullanılarak yapılan kullanıcı doğrulamasının "gerekli", "tercih edilen" veya "önerilmez" olup olmadığını belirtir. RP sunucusundan bir giriş sorgulaması getir konusunu inceleyin.
  • timeout: Kullanıcının kimlik doğrulamayı tamamlama süresi (milisaniye cinsinden). Makul düzeyde cömert olmalı ve challenge kullanım ömründen daha kısa sürmelidir. Önerilen varsayılan değer 5 dakika'dır ancak bu değeri 10 dakikaya kadar artırabilirsiniz. Bu değer hâlâ önerilen aralık dahilindedir. Kullanıcıların, genellikle biraz daha uzun süren karma iş akışını kullanmasını bekliyorsanız uzun zaman aşımları mantıklıdır. İşlem zaman aşımına uğrarsa bir NotAllowedError atılır.

publicKeyCredentialRequestOptions dosyasını oluşturduktan sonra müşteriye gönderin.

Sunucu tarafından gönderilen PublicKeyCredentialCreationOptions
Sunucu tarafından gönderilen seçenekler. challenge kod çözme işlemi istemci tarafında gerçekleşir.

Örnek kod: kimlik bilgisi isteği seçenekleri oluşturma

Örneklerimizde SimpleWebAuthn kitaplığını kullanıyoruz. Burada, kimlik bilgisi isteği seçeneklerini oluşturma işlemini generateAuthenticationOptions işlevine aktarıyoruz.

import {
  generateRegistrationOptions,
  verifyRegistrationResponse,
  generateAuthenticationOptions,
  verifyAuthenticationResponse
} from '@simplewebauthn/server';

router.post('/signinRequest', csrfCheck, async (req, res) => {

  // Ensure you nest calls in try/catch blocks.
  // If something fails, throw an error with a descriptive error message.
  // Return that message with an appropriate error code to the client.
  try {
    // Use the generateAuthenticationOptions function from SimpleWebAuthn
    const options = await generateAuthenticationOptions({
      rpID: process.env.HOSTNAME,
      allowCredentials: [],
    });
    // Save the challenge in the user session
    req.session.challenge = options.challenge;

    return res.json(options);
  } catch (e) {
    console.error(e);
    return res.status(400).json({ error: e.message });
  }
});

Kullanıcıyı doğrulayın ve oturum açın

navigator.credentials.get, istemcide başarıyla çözümlendiğinde PublicKeyCredential nesnesi döndürür.

Sunucu tarafından gönderilen PublicKeyCredential nesnesi
navigator.credentials.get, PublicKeyCredential döndürür.

response bir AuthenticatorAssertionResponse. Geçiş anahtarı sağlayıcısının, istemcinin RP'deki geçiş anahtarıyla kimlik doğrulamayı denemek için gereken işlemi oluşturma talimatına verdiği yanıtı temsil eder. Şunları içerir:

  • response.authenticatorDataveresponse.clientDataJSON (ör. geçiş anahtarı kaydı adımında olduğu gibi).
  • Bu değerlerin üzerinde bir imza içeren response.signature.

PublicKeyCredential nesnesini sunucuya gönderin.

Sunucuda aşağıdakileri yapın:

Veritabanı şeması
Önerilen veritabanı şeması. Bu tasarım hakkında daha fazla bilgi için Sunucu tarafı geçiş anahtarı kaydı bölümüne bakın.
  • Onayı ve kullanıcının kimliğini doğrulamak için ihtiyacınız olan bilgileri toplayın:
    • Kimlik doğrulama seçeneklerini oluştururken oturumda sakladığınız beklenen giriş sorgulamasını alın.
    • Beklenen kaynak ve RP kimliğini alın.
    • Veritabanınızda kullanıcının kim olduğunu bulun. Bulunabilir kimlik bilgileri söz konusu olduğunda, kimlik doğrulama isteğinde bulunan kullanıcının kim olduğunu bilemezsiniz. Bunu öğrenmek için iki seçeneğiniz vardır:
      • 1. Seçenek: PublicKeyCredential nesnesinde response.userHandle öğesini kullanın. Kullanıcılar tablosunda userHandle ile eşleşen passkey_user_id olup olmadığına bakın.
      • 2. Seçenek: PublicKeyCredential nesnesinde bulunan id kimlik bilgisini kullanın. Ortak anahtar kimlik bilgileri tablosunda, PublicKeyCredential nesnesinde bulunan id kimlik bilgisi ile eşleşen id kimlik bilgisini arayın. Ardından, Kullanıcılar tablonuza passkey_user_id yabancı anahtarını kullanarak karşılık gelen kullanıcıyı bulun.
    • Aldığınız kimlik doğrulama onayıyla eşleşen ortak anahtar kimlik bilgisi bilgilerini veritabanınızda bulun. Bunu yapmak için Ortak anahtar kimlik bilgileri tablosunda, PublicKeyCredential nesnesinde id bulunan kimlik bilgileriyle eşleşen id kimlik bilgisini arayın.
  • Kimlik doğrulama onayını doğrulayın. Bu doğrulama adımını, genellikle bu amaç için bir yardımcı program işlevi sunacak olan FIDO sunucu tarafı kitaplığınıza aktarın. SimpleWebAuthn teklifleri (örneğin, verifyAuthenticationResponse). Ek: kimlik doğrulama yanıtının doğrulanması başlıklı makaleden, arka planda neler olduğunu öğrenebilirsiniz.

  • Tekrar oynatma saldırılarını önlemek için doğrulama başarılı olsun veya olmasın, sorgulamayı silin.

  • Kullanıcının oturum açmasını sağlayın. Doğrulama başarılı olduysa kullanıcıyı oturum açmış olarak işaretlemek için oturum bilgilerini güncelleyin. Ayrıca, ön ucun yeni oturum açan kullanıcıyla ilişkili bilgileri kullanabilmesi için istemciye bir user nesnesi döndürmek isteyebilirsiniz.

Örnek kod: kullanıcıyı doğrulama ve oturum açma

Örneklerimizde SimpleWebAuthn kitaplığını kullanıyoruz. Bu bölümde, kimlik doğrulama yanıtının doğrulamasını, verifyAuthenticationResponse işlevine devrediyoruz.

import {
  generateRegistrationOptions,
  verifyRegistrationResponse,
  generateAuthenticationOptions,
  verifyAuthenticationResponse
} from '@simplewebauthn/server';
import { isoBase64URL } from '@simplewebauthn/server/helpers';

router.post('/signinResponse', csrfCheck, async (req, res) => {
  const response = req.body;
  const expectedChallenge = req.session.challenge;
  const expectedOrigin = getOrigin(req.get('User-Agent'));
  const expectedRPID = process.env.HOSTNAME;

  // Ensure you nest verification function calls in try/catch blocks.
  // If something fails, throw an error with a descriptive error message.
  // Return that message with an appropriate error code to the client.
  try {
    // Find the credential stored to the database by the credential ID
    const cred = Credentials.findById(response.id);
    if (!cred) {
      throw new Error('Credential not found.');
    }
    // Find the user - Here alternatively we could look up the user directly
    // in the Users table via userHandle
    const user = Users.findByPasskeyUserId(cred.passkey_user_id);
    if (!user) {
      throw new Error('User not found.');
    }
    // Base64URL decode some values
    const authenticator = {
      credentialPublicKey: isoBase64URL.toBuffer(cred.publicKey),
      credentialID: isoBase64URL.toBuffer(cred.id),
      transports: cred.transports,
    };

    // Verify the credential
    const { verified, authenticationInfo } = await verifyAuthenticationResponse({
      response,
      expectedChallenge,
      expectedOrigin,
      expectedRPID,
      authenticator,
      requireUserVerification: false,
    });

    if (!verified) {
      throw new Error('User verification failed.');
    }

    // Kill the challenge for this session.
    delete req.session.challenge;

    req.session.username = user.username;
    req.session['signed-in'] = 'yes';

    return res.json(user);
  } catch (e) {
    delete req.session.challenge;

    console.error(e);
    return res.status(400).json({ error: e.message });
  }
});

Ek: kimlik doğrulama yanıtının doğrulanması

Kimlik doğrulama yanıtının doğrulanması aşağıdaki kontrollerden oluşur:

  • RP kimliğinin sitenizle eşleştiğinden emin olun.
  • İsteğin kaynağının sitenizin oturum açma kaynağıyla eşleştiğinden emin olun. Android uygulamaları için Kaynağı doğrulama bölümünü inceleyin.
  • Cihazın, verdiğiniz giriş sorgulamasını yapabildiğinden emin olun.
  • Kimlik doğrulama sırasında kullanıcının, kısıtlanmış taraf olarak zorunlu kıldığınız koşullara uyduğunu doğrulayın. Kullanıcı doğrulamasını zorunlu tutuyorsanız authenticatorData öğesindeki uv (kullanıcı tarafından doğrulanmış) işaretinin true olduğundan emin olun. Geçiş anahtarları için kullanıcının varlığı her zaman gereklidir. Bu nedenle authenticatorData öğesindeki up (kullanıcı mevcut) işaretinin true olduğundan emin olun.
  • İmzayı doğrulayın. İmzayı doğrulamak için ihtiyacınız olanlar:
    • İmzalı sınama olan imza: response.signature
    • İmzayı doğrulamak için kullanılacak ortak anahtar.
    • Orijinal imzalı veriler. Bunlar, imzası doğrulanacak verilerdir.
    • İmzayı oluşturmak için kullanılan şifreleme algoritması.

Bu adımlar hakkında daha fazla bilgi edinmek için SimpleWebAuthn'un verifyAuthenticationResponse kaynak kodunu kontrol edin veya spesifikasyondaki doğrulamaların tam listesini inceleyin.