Bir güvenlik anahtarıyla (WebAuthn) iki faktörlü kimlik doğrulamayla sitenizin güvenliğini sağlayın

1. Derlemeniz istenen nedir?

Şifre tabanlı girişi destekleyen temel bir web uygulamasıyla başlarsınız.

Ardından, WebAuthn'a bağlı olarak güvenlik anahtarı aracılığıyla iki faktörlü kimlik doğrulama için destek ekleyebilirsiniz. Bunun için aşağıdakileri uygulamanız gerekir:

  • Kullanıcının WebAuthn kimlik bilgisini kaydetmenin bir yoludur.
  • Kullanıcı, iki faktörlü kimlik doğrulama akışı olduğunda, kullanıcının ikinci faktörden (WebAuthn kimlik bilgisi) sorulması istenir.
  • Kimlik bilgisi yönetim arayüzü: Kullanıcıların kimlik bilgilerini yeniden adlandırmasına ve silmesine olanak tanıyan kimlik bilgilerinin listesi.

16ce77744061c5f7.png

Tamamlanan web uygulamasına göz atın ve deneyin.

2. WebAuthn hakkında

WebAuthn ile ilgili temel bilgiler

Neden WebAuthn?

Kimlik avı web'de büyük bir güvenlik sorunudur. Hesap ihlallerinin çoğunda, siteler arasında yeniden kullanılan zayıf veya çalınan şifreler kullanılır. Sektörün bu soruna toplu olarak verdiği yanıtlar çok öğeli kimlik doğrulamasıdır, ancak uygulamalar çok parçalıdır ve bunların çoğu hâlâ kimlik avını yeterli düzeyde ele almaz.

Web Authentication API veya WebAuthn, herhangi bir web uygulaması tarafından kullanılabilen, kimlik avına dayanıklı, standartlaştırılmış bir protokoldür.

İşleyiş şekli

Kaynak: webauthn.guide

WebAuthn, sunucuların şifre yerine ortak anahtar kriptografisini kullanarak kullanıcıları kaydettirmesine ve kimlik doğrulaması yapmasına olanak tanır. Web siteleri, özel-ortak anahtar çiftinden oluşan bir kimlik bilgisi oluşturabilir.

  • Özel anahtar, kullanıcının cihazında güvenli bir şekilde saklanır.
  • Ortak anahtar ve rastgele oluşturulan kimlik bilgisi, depolama için sunucuya gönderilir.

Ortak anahtar, sunucu tarafından kullanıcının kimliğini kanıtlamak için kullanılır. Karşılık gelen özel anahtar olmadan yarar sağlamadığından gizli değildir.

Avantajlar

WebAuthn'un iki temel avantajı vardır:

  • Paylaşılan gizli anahtar yok: Sunucu gizli anahtarı depolamaz. Bu, ortak anahtarların kullanıcılar için yararlı olmamasından dolayı veritabanlarının bilgisayar korsanları için daha az çekici olmasını sağlar.
  • Kapsamlı kimlik bilgileri: site.example için kaydedilen kimlik bilgileri evil-site.example tarihinde kullanılamaz. Bu sayede WebAuthn kimlik avına dayanıklı hale gelir.

Kullanım alanları

WebAuthn için bir kullanım alanı, güvenlik anahtarıyla iki faktörlü kimlik doğrulamadır. Bu, özellikle kurumsal web uygulamaları için geçerli olabilir.

Tarayıcı desteği

Google, Mozilla, Microsoft, Yubico ve diğer şirketlerin katılımıyla W3C ve FIDO tarafından yazılmıştır.

Sözlük

  • Kimlik doğrulayıcı: Bir kullanıcıyı kaydettirip daha sonra kayıtlı kimlik bilgilerine sahip olduğunu iddia eden bir yazılım veya donanım varlığı. İki tür kimlik doğrulayıcı vardır:
  • Dolaşım kimlik doğrulayıcı: Kullanıcının oturum açmaya çalıştığı cihazda kullanılabilen kimlik doğrulayıcı. Örnek: USB güvenlik anahtarı, akıllı telefon.
  • Platform kimlik doğrulayıcı: Kullanıcı cihazında yerleşik bir kimlik doğrulayıcı. Örnek: Apple' Touch ID.
  • Kimlik bilgisi: Özel-ortak anahtar çifti
  • Bağlı taraf: Kullanıcının kimliğini doğrulamaya çalışan web sitesi (sunucusu)
  • FIDO sunucusu: Kimlik doğrulama için kullanılan sunucu. FIDO, FIDO ittifakının geliştirdiği protokollerden oluşan bir ailedir. Bu protokollerden biri WebAuthn'dur.

Bu atölyede dolaşım kimlik doğrulayıcısını kullanacağız.

3. Başlamadan önce

Gerekenler

Bu codelab'i tamamlamak için gerekenler:

  • WebAuthn'la ilgili temel bilgiler.
  • JavaScript ve HTML hakkında temel bilgi.
  • WebAuthn'u destekleyen güncel bir tarayıcı.
  • U2F uyumlu bir güvenlik anahtarı.

Aşağıdakilerden birini güvenlik anahtarı olarak kullanabilirsiniz:

  • Chrome çalıştıran Android>=7 (Nougat) yüklü bir Android telefon. Bu durumda, çalışan bir Bluetooth özellikli Windows, macOS veya Chrome OS makineye de ihtiyacınız olur.
  • YubiKey gibi bir USB anahtarı.

6539dc7ffec2538c.png

Kaynak: https://www.yubico.com/products/security-key/

dd56e2cfe0f7ced2.png

Neler öğreneceksiniz?

Öğrenecekleriniz ✨

  • WebAuthn kimlik doğrulaması için ikinci faktör olarak güvenlik anahtarının nasıl kaydedileceği ve kullanılacağı.
  • Bu işlemi nasıl kullanıcı dostu hale getirebilirsiniz?

Öğrenemeyeceksiniz ❌

  • FIDO sunucusu (kimlik doğrulama için kullanılan sunucu) nasıl oluşturulur? Bir web uygulaması veya site geliştiricisi olarak genellikle mevcut FIDO sunucu uygulamalarını kullanacağınız için bu bir sorun teşkil etmez. Güvendiğiniz sunucu uygulamalarının işlevselliğini ve kalitesini her zaman doğruladığınızdan emin olun. Bu codelab'de, FIDO sunucusu SimpleWebAuthn kullanır. Diğer seçenekler için FIDO Alliance resmi sayfasını ziyaret edin. Açık kaynak kitaplıklar için webauthn.io veya AwesomeWebAuthn'a bakın.

Sorumluluk Reddi Beyanı

Kullanıcının oturum açmak için şifre girmesi gerekir. Ancak bu codelab'de kolaylık sağlamak için şifre saklanmaz veya kontrol edilmez. Gerçek bir uygulamada, uygulamanın sunucu tarafında doğru olup olmadığını kontrol edersiniz.

CSRF kontrolleri, oturum doğrulaması ve temizlik gibi temel güvenlik kontrolleri bu codelab'de belirtilmiştir. Ancak birçok güvenlik önlemi yoktur. Örneğin, kaba kuvvet saldırılarının engellenmesi için şifrelerde giriş sınırı yoktur. Şifreler depolanmadığı için burada önemli değildir, ancak bu kodu üretimde olduğu gibi kullanmadığınızdan emin olun.

4. Kimlik doğrulayıcınızı ayarlama

Bir Android telefonu kimlik doğrulayıcı olarak kullanıyorsanız

  • Hem masaüstü hem de telefonunuzda Chrome'un güncel olduğundan emin olun.
  • Hem masaüstü bilgisayarınızda hem de telefonunuzda, Chrome'u açın ve bu atölyede kullanmak istediğiniz profille oturum açın.
  • Masaüstü ve telefonunuzda bu profil için Senkronizasyonu etkinleştirin. Bunun için chrome://settings/syncSetup'ı kullanın.
  • Hem masaüstü hem de telefonunuzda Bluetooth'u açın.
  • Aynı profille giriş yapılan Chrome masaüstünde webauthn.io sayfasını açın.
  • Basit bir kullanıcı adı girin. Onay türü ve Kimlik doğrulayıcı türü değerlerini Yok ve Belirtilmedi (varsayılan) değerlerine bırakın. Kaydol'u tıklayın.

6b49ff0298f5a0af.png

  • Kimliğinizi doğrulamanızı isteyen bir tarayıcı penceresi açılır. Listeden telefonunuzu seçin.

ffebe58ac826eaf2.png 852de328fcd4eb42.png

  • Telefonunuzda Kimliğinizi doğrulayın başlıklı bir bildirim alırsınız. Uygulamaya dokunun.
  • Telefonunuzda telefonunuzun PIN kodunu girmeniz (veya parmak izi sensörüne dokunmanız) istenir. Bunu girin.
  • Masaüstünüzdeki webauthn.io'da "Başarılı" göstergesi görünür.

fc0acf00a4d412fa.png

  • Masaüstünüzde webauthn.io uygulamasında Giriş düğmesini tıklayın.
  • Yine, bir tarayıcı penceresi açılmalıdır; listeden telefonunuzu seçin.
  • Telefonunuzda açılan bildirime dokunun ve PIN'inizi girin (veya parmak izi sensörüne dokunun).
  • webauthn.io, giriş yaptığınızı size bildirmelidir. Telefonunuz güvenlik anahtarı olarak düzgün çalışıyor. Artık atölyeye hazırsınız!

Kimlik doğrulayıcı olarak bir USB güvenlik anahtarı kullanıyorsanız

  • Chrome masaüstünde webauthn.io sayfasını açın.
  • Basit bir kullanıcı adı girin. Onay türü ve Kimlik doğrulayıcı türü değerlerini Yok ve Belirtilmedi (varsayılan) değerlerine bırakın. Kaydol'u tıklayın.
  • Kimliğinizi doğrulamanızı isteyen bir tarayıcı penceresi açılır. Listeden USB güvenlik anahtarı'nı seçin.

ffebe58ac826eaf2.png 9fe75f04e43da035.png

  • Güvenlik anahtarınızı masaüstünüze yerleştirip anahtara dokunun.

923d5adb8aa8286c.png

  • Masaüstünüzdeki webauthn.io'da "Başarılı" göstergesi görünür.

fc0acf00a4d412fa.png

  • Masaüstünüzde webauthn.io uygulamasında Giriş yap düğmesini tıklayın.
  • Yine, bir tarayıcı penceresi açılır. Listede USB güvenlik anahtarını seçin.
  • Tuşa dokunun.
  • Webauthn.io, giriş yaptığınızı size bildirmelidir. USB güvenlik anahtarınız düzgün çalışıyor. Atölyeye hazırsınız!

7e1c0bb19c9f3043.png

5. Hazırlanın

Bu codelab'de, kodunuzu otomatik ve anında dağıtacak online kod düzenleyici olan Glitch'i kullanacaksınız.

Başlangıç kodunu çatallayın

Başlangıç projesini açın.

Remiks düğmesini tıklayın.

Bu işlem, başlangıç kodunun bir kopyasını oluşturur. Artık düzenlemek için kendi kodunuz var. Bu lab'la ilgili tüm işleri çatalınız ("Glitch'te remiks" olarak adlandırılır) ile yaparsınız.

cf2b9f552c9809b6.png

Başlangıç kodunu keşfedin

Kısa bir süre önce aldığınız başlangıç kodunu keşfedin.

libs altında auth.js adlı bir kitaplığın zaten mevcut olduğunu unutmayın. Sunucu tarafı kimlik doğrulama mantığını işleyen özel bir kitaplıktır. Bağımlılık olarak fido kitaplığını kullanır.

6. Kimlik bilgisi kaydını uygulama

Kimlik bilgisi kaydını uygulama

Güvenlik anahtarıyla iki faktörlü kimlik doğrulamayı ayarlamak için gereken ilk şey, kullanıcının kimlik bilgisi oluşturmasını sağlamaktır.

Önce istemci tarafı kodumuzda bunu yapan bir işlev ekleyelim.

public/auth.client.js işlevinde, henüz registerCredential()hiçbir şey yapmayan bir işlev olduğunu unutmayın. Aşağıdaki kodu ekleyin:

async function registerCredential() {
  // Fetch the credential creation options from the backend
  const credentialCreationOptionsFromServer = await _fetch(
    "/auth/credential-options",
    "POST"
  );
  // Decode the credential creation options
  const credentialCreationOptions = decodeServerOptions(
    credentialCreationOptionsFromServer
  );
  // Create a credential via the browser API; this will prompt the user to touch their security key or tap a button on their phone
  const credential = await navigator.credentials.create({
    publicKey: {
      ...credentialCreationOptions,
    }
  });
  // Encode the newly created credential to send it to the backend
  const encodedCredential = encodeCredential(credential);
  // Send the encoded credential to the backend for storage
  return await _fetch("/auth/credential", "POST", encodedCredential);
}

Bu işlevin zaten sizin için dışa aktarıldığını unutmayın.

registerCredential şunları yapar:

  • Sunucudan kimlik bilgisi oluşturma seçeneklerini getirir (/auth/credential-options)
  • Sunucu seçenekleri kodlanmış olduğundan, bunların kodunu çözmek için decodeServerOptions yardımcı işlevini kullanır.
  • Web API'sini (navigator.credential.create) çağırarak kimlik bilgisi oluşturur. navigator.credential.create çağrıldığında, tarayıcı devreye girer ve kullanıcıdan bir güvenlik anahtarı seçmesini ister.
  • Yeni oluşturulan kimlik bilgilerinin kodunu çözer
  • Kodlanmış kimlik bilgisini içeren /auth/credential için bir istekte bulunarak yeni kimlik bilgisini sunucu tarafında kaydeder.

Ayrıca, sunucu koduna göz atabilirsiniz.

registerCredential(), sunucuya iki çağrı yapıyor. Bu nedenle, arka uçta neler olduğunu kontrol edelim.

Kimlik bilgisi oluşturma seçenekleri

İstemci (/auth/credential-options) için bir istek yaptığında, sunucu bir seçenekler nesnesi oluşturur ve bunu istemciye geri gönderir.

Bu nesne daha sonra istemci tarafından gerçek kimlik bilgisi oluşturma çağrısında kullanılır:

navigator.credentials.create({
    publicKey: {
    // Options generated server-side
    ...credentialCreationOptions
// ...
}

Peki ya önceki adımda registerCredential istemci tarafında kullandığınız credentialCreationOptions credentialCreationOptions ne anlama geliyor?

router.post("/credential-options", ...) altındaki sunucu koduna göz atın.

Her bir mülke bakmayalım, ancak fido2 kitaplığı kullanılarak oluşturulan ve sonunda istemciye döndürülen sunucu kodu seçenekleri nesnesinde görebileceğiniz birkaç ilginç özelliği burada görebilirsiniz:

  • rpName ve rpId, kullanıcıyı kaydeden ve kimlik doğrulaması yapan kuruluşu tanımlar. WebAuthn'ta kimlik bilgilerinin belirli bir alan adı kapsamında olduğunu unutmayın. Bu durum güvenlik avantajıdır; buradaki rpName ve rpId, kimlik bilgilerini kapsamak için kullanılır. Geçerli bir rpId, örneğin sitenizin ana makine adıdır. Başlangıç projesini oluştururken ne kadar otomatik olarak güncelleneceğini unutmayın 🧘🏻 ♀️
  • excludeCredentials bir kimlik bilgileri listesidir; yeni kimlik bilgisi, excludeCredentials içinde listelenen kimlik bilgilerinden birini de içeren kimlik doğrulayıcı üzerinde oluşturulamaz. Codelab'imizde excludeCredentials, bu kullanıcının mevcut kimlik bilgilerinin bir listesidir. Bu user.id ve kullanıcı tarafından oluşturulan her kimlik bilgisinin farklı bir kimlik doğrulayıcı (güvenlik anahtarı) üzerinde kullanılmasını sağlıyoruz. Bu, iyi bir uygulamadır. Bir kullanıcı birden fazla kimlik bilgisi kaydettirdiyse farklı kimlik doğrulayıcılar (güvenlik anahtarları) kullanacağı için bir güvenlik anahtarının kaybedilmesi kullanıcının hesabına kilitlenmesine neden olmaz.
  • authenticatorSelection, web uygulamanızda izin vermek istediğiniz kimlik doğrulayıcıların türünü tanımlar. authenticatorSelection adlı anlaşmayı daha yakından inceleyelim:
    • residentKey: preferred bu uygulamanın, istemci tarafında bulunabilir kimlik bilgilerini zorunlu kılmadığı anlamına gelir. İstemci tarafı bulunabilir kimlik bilgisi, kullanıcının tanımlanmasına gerek kalmadan kimlik doğrulamasının yapılmasını sağlayan özel bir kimlik bilgisi türüdür. Bu codelab'in temel uygulamaya odaklandığı için preferred işlevini ayarladık. Bulunabilir kimlik bilgileri daha gelişmiş akışlar içindir.
    • requireResidentKey yalnızca WebAuthn v1 ile backwards-uyumluluk için kullanılabilir.
    • userVerification: preferred, kimlik doğrulayıcının kullanıcı doğrulamasını desteklemesi (ör. bir biyometrik güvenlik anahtarı veya yerleşik PIN özelliği olan bir anahtar olması durumunda) bağımlı tarafın, kimlik bilgilerini oluştururken bu anahtarı isteyeceği anlamına gelir. Kimlik doğrulayıcı (temel güvenlik anahtarı) değilse sunucu, kullanıcı doğrulaması istemez.
  • ​​pubKeyCredParam, tercih sırasına göre kimlik bilgisinin istenen şifreleme özelliklerini açıklar.

Tüm bu seçenekler web uygulamasının güvenlik modeli için alması gereken kararlardır. Sunucuda bu seçeneklerin tek bir authSettings nesnesinde tanımlandığını unutmayın.

Meydan okuma

Daha ilgi çekici bir nokta da req.session.challenge = options.challenge;.

WebAuthn bir kriptografik protokol olduğundan tekrar oynama saldırılarını önlemek için rastgele hale getirilmiş görevlere bağlıdır. Bir saldırgan, kimlik doğrulamayı tekrar etkinleştirecek özel anahtarın sahibi olmadığında, kimlik doğrulama işlemini tekrar yapmak için bir yükü oynattığında.

Bu sorunu en aza indirmek için sunucuda bir sorgulama oluşturulur ve işlem anında imzalanır. Ardından imza, beklenen bilgilerle karşılaştırılır. Bu kullanıcının kimlik bilgisi oluşturulurken özel anahtarı tuttuğunu doğrular.

Kimlik bilgisi kayıt kodu

router.post("/credential" ...) altındaki sunucu koduna göz atın.

Kimlik bilgisi burada sunucu tarafında kaydedilir.

Peki, neler oluyor?

Bu koddaki en önemli bitlerden biri, fido2.verifyAttestationResponse aracılığıyla yapılan doğrulama çağrısıdır:

  • İmzalanmış meydan okuma kontrol ediliyor. Bu kimlik bilgisi, kimlik bilgilerinin oluşturma zamanında özel anahtarı tutuklayan biri tarafından oluşturulmasını sağlar.
  • Kaynağı olan bağlı tarafın kimliği de doğrulanır. Bu, kimlik bilgilerinin bu web uygulamasına (ve yalnızca bu web uygulamasına) bağlı olmasını sağlar.

Bu işlevi kullanıcı arayüzüne ekleyin

Kimlik bilgisi oluşturma işleviniz artık hazır olduğuna göre, "`registerCredential(),kullanıcının kullanımına hazır hale getirelim.

Kimlik doğrulama yönetimi için normal bir konum olduğundan bu işlemi Hesap sayfasından yapabilirsiniz.

account.html&#39 işaretlemesinde, kullanıcı adının altında class="flex-h-between" düzen sınıfına sahip çok boş bir div var. Bu div, 2FA işleviyle ilgili kullanıcı arayüzü öğeleri için kullanılacaktır.

Bu div'den iOS'e ekleyin:

  • "İki faktörlü kimlik doğrulama" yazılı bir başlık
  • Kimlik bilgisi oluşturma düğmesi
 <div class="flex-h-between">
    <h3>
        Two-factor authentication
    </h3>
    <button class="create" id="registerButton" raised>
        ➕ Add a credential
    </button>
</div>

Bu div'in altına, daha sonra ihtiyaç duyacağımız bir kimlik bilgisi div öğesi ekleyin:

<div class="flex-h-between">
(HTML you've just added)
</div>
<div id="credentials"></div>

account.html satır içi komut dosyasında, yeni oluşturduğunuz işlevi ve register ifadesini çağıran bir işlevi içe aktarıp, oluşturduğunuz düğmeye eklenmiş bir etkinlik işleyici ekleyin.

// Set up the handler for the button that registers credentials
const registerButton = document.querySelector('#registerButton');
registerButton.addEventListener('click', register);

// Register a credential
async function register() {
  let user = {};
  try {
    const user = await registerCredential();
  } catch (e) {
    // Alert the user that something went wrong
    if (Array.isArray(e)) {
      alert(
        // `msg` not `message`, this is the key's name as per the express validator API
        `Registration failed. ${e.map((err) => `${err.msg} (${err.param})`)}`
      );
    } else {
      alert(`Registration failed. ${e}`);
    }
  }
}

Kullanıcının göreceği kimlik bilgilerini göster

Kimlik bilgisi oluşturma işlevini eklediğinize göre, kullanıcıların ekledikleri kimlik bilgilerini görmenin bir yolunu bulmaları gerekir.

Hesap sayfası bunun için iyi bir yerdir.

account.html içinde updateCredentialList() adlı işlevi bulun.

Giriş yapmış olan kullanıcının tüm kayıtlı kimlik bilgilerini almak için arka uç çağrısı yapan ve döndürülen kimlik bilgilerini gösteren aşağıdaki kodu ekleyin:

// Update the list that displays credentials
async function updateCredentialList() {
  // Fetch the latest credential list from the backend
  const response = await _fetch('/auth/credentials', 'GET');
  const credentials = response.credentials || [];
  // Generate the credential list as HTML and pass remove/rename functions as args
  const credentialListHtml = getCredentialListHtml(
    credentials,
    removeEl,
    renameEl
  );
  // Display the list of credentials in the DOM
  const list = document.querySelector('#credentials');
  render(credentialListHtml, list);
}    

Şu an için removeEl ve renameEl politikalarını unutmayın. Bu kod laboratuvarının ilerleyen bölümlerinde bunlar hakkında bilgi edineceksiniz.

account.html içinde satır içi komut dosyanızın başına updateCredentialList çağrısı ekleyin. Bu çağrıda, kullanıcı hesap sayfasına ulaştığında kullanılabilir kimlik bilgileri getirilir.

<script type="module">
    // ... (imports)
    // Initialize the credential list by updating it once on page load
    updateCredentialList();

Şimdi, updateCredentialList kodunu çağırarak registerCredential işlemini başarıyla tamamlayın. Böylece listeler, yeni oluşturulan kimlik bilgisini görüntüler:

async function register() {
  let user = {};
  try {
    // ...
  } catch (e) {
    // ...
  }
  // Refresh the credential list to display the new credential
  await updateCredentialList();
}

Bu özelliği kullanmayı deneyin. 👩🏻 💻

Kimlik bilgisi kaydını tamamladınız. Kullanıcılar artık güvenlik anahtarı tabanlı kimlik bilgileri oluşturabilir ve bunları Hesap sayfalarında görselleştirebilir.

Şunları deneyin:

  • Oturumu kapat'ı tıklayın.
  • Herhangi bir kullanıcı ve şifre ile giriş yapın. Daha önce de belirtildiği gibi, bu kod laboratuvarında işlemleri basit hale getirmek için şifrenin doğru olup olmadığı kontrol edilmez. Boş olmayan şifreleri girin.
  • Account (Hesap) sayfasına geldiğinizde Kimlik bilgisi ekle'yi tıklayın.
  • Bir güvenlik anahtarı takmanız ve bu anahtara dokunmanız istenir. Bu işlemi mutlaka yapın.
  • Kimlik bilgisi başarıyla oluşturulduktan sonra, kimlik bilgisi hesap sayfasında gösterilmelidir.
  • Hesap sayfasını yeniden yükleyin. Kimlik bilgileri görüntülenir.
  • İki anahtarınız varsa kimlik bilgisi olarak iki farklı güvenlik anahtarı eklemeyi deneyin. Her ikisi de gösterilmelidir.
  • Aynı kimlik doğrulayıcı (anahtar) ile iki kimlik bilgisi oluşturmayı deneyin. Desteklenmediğini fark edeceksiniz. Bu işlem bilerek yapılmıştır. Bunun nedeni, arka uçta excludeCredentials kullanmamızdır.

7. İkinci faktörlü kimlik doğrulamayı etkinleştir

Kullanıcılarınız kimlik bilgilerini kaydedip iptal edebilir, ancak kimlik bilgileri yalnızca gösterilir ve henüz kullanılmaz.

Şimdi bunları kullanma ve iki faktörlü kimlik doğrulama ayarlarını yapma zamanı.

Bu bölümde, web uygulamanızdaki kimlik doğrulama akışını şu temel akıştan değiştireceksiniz:

6ff49a7e520836d0.png

Bu iki faktörlü akışa:

e7409946cd88efc7.png

İkinci etmen kimlik doğrulamasını uygulama

Öncelikle ihtiyacımız olan işlevselliği ekleyelim ve arka uçla iletişim uygulayalım. Sonraki adımda bunu ön uçta ekleyeceğiz.

Burada uygulamanız gereken, kimlik bilgisi ile kullanıcının kimliğini doğrulayan bir işlevdir.

public/auth.client.js içinde boş işlevi (authenticateTwoFactor) bulup ona aşağıdaki kodu ekleyin:

async function authenticateTwoFactor() {
  // Fetch the 2F options from the backend
  const optionsFromServer = await _fetch("/auth/two-factor-options", "POST");
  // Decode them
  const decodedOptions = decodeServerOptions(optionsFromServer);
  // Get a credential via the browser API; this will prompt the user to touch their security key or tap a button on their phone
  const credential = await navigator.credentials.get({
    publicKey: decodedOptions
  });
  // Encode the credential
  const encodedCredential = encodeCredential(credential);
  // Send it to the backend for verification
  return await _fetch("/auth/authenticate-two-factor", "POST", {
    credential: encodedCredential
  });
}

Bu işlevin zaten sizin için dışa aktarıldığını unutmayın. Bu işleve sonraki adımda ihtiyaç duyacağız.

authenticateTwoFactor şunları yapar:

  • Sunucudan iki faktörlü kimlik doğrulama seçenekleri ister. Daha önce gördüğünüz kimlik bilgisi oluşturma seçenekleri gibi bunlar da sunucuda tanımlanır ve web uygulamasının güvenlik modeline bağlıdır. Ayrıntılar için router.post("/two-factors-options", ... altındaki sunucu kodunu inceleyin.
  • navigator.credentials.get ifadesini çağırırken tarayıcının devralmasına izin verir ve kullanıcıdan daha önce kaydedilmiş bir anahtarı ekleyip dokunmasını ister. Bu, söz konusu ikinci faktör kimlik doğrulaması işlemi için kimlik bilgilerinin seçilmesine neden olur.
  • Daha sonra, seçilen kimlik bilgisi getirmeleri için arka uç isteğinde iletilir(&; kimlik doğrulama/kimlik doğrulama-iki faktörlü&#).; Kimlik bilgisi bu kullanıcı için geçerliyse kullanıcının kimliği doğrulanır.

Ayrıca, sunucu koduna göz atabilirsiniz.

server.js uygulaması zaten gezinme ve erişimle ilgilenir: Hesap sayfasına yalnızca kimliği doğrulanmış kullanıcılar tarafından erişilebilmesini sağlar ve bazı gerekli yönlendirmeleri gerçekleştirir.

Şimdi router.post("/initialize-authentication", ... altındaki sunucu koduna göz atın.

Dikkat edilmesi gereken iki ilginç nokta vardır:

  • Bu aşamada hem şifre hem kimlik bilgisi aynı anda kontrol edilir. Bu bir güvenlik önlemidir: İki faktörlü kimlik doğrulama özelliğini ayarlanan kullanıcılar için, şifrenin doğru olup olmamasına bağlı olarak kullanıcı arayüzü akışlarının farklı görünmesini istemeyiz. Bu nedenle, bu adımda hem şifreyi hem de kimlik bilgilerini kontrol ederiz.
  • Hem şifre hem de kimlik bilgisi geçerliyse completeAuthentication(req, res); hizmetini çağırarak kimlik doğrulama işlemini tamamlarız. Pratikte bu, oturumları henüz kullanıcının kimliği doğrulanmamış geçici bir auth oturumundan kullanıcının kimliğinin doğrulandığı ana oturuma main geçirdiğimiz anlamına gelir.

İkinci faktör kimlik doğrulama sayfasını kullanıcı akışına dahil et

views klasöründe, second-factor.html adlı yeni sayfaya dikkat edin.

Güvenlik anahtarı kullan yazılı bir düğme vardır, ancak şu an için hiçbir şey yapmamaktadır.

Bu düğmeyi tıkladığınızda authenticateTwoFactor() düğmesini çağırın.

  • authenticateTwoFactor() başarılı olursa kullanıcıyı Hesap sayfasına yönlendirin.
  • İşlem başarısız olursa kullanıcıyı bir hata oluştuğu konusunda uyarın. Gerçek bir uygulamada, daha faydalı hata mesajları uygularsınız. Bu demoda kolaylık sağlamak amacıyla yalnızca pencere uyarısı kullanacağız.
    <main>
...
    </main>
    <script type="module">
      import { authenticateTwoFactor, authStatuses } from "/auth.client.js";

      const button = document.querySelector("#authenticateButton");
      button.addEventListener("click", async e => {
        try {
          // Ask the user to authenticate with the second factor; this will trigger a browser prompt
          const response = await authenticateTwoFactor();
          const { authStatus } = response;
          if (authStatus === authStatuses.COMPLETE) {
            // The user is properly authenticated => Navigate to the Account page
            location.href = "/account";
          } else {
            throw new Error("Two-factor authentication failed");
          }
        } catch (e) {
          // Alert the user that something went wrong
          alert(`Two-factor authentication failed. ${e}`);
        }
      });
    </script>
  </body>
</html>

İkinci faktörlü kimlik doğrulamayı kullanın

Artık iki faktörlü kimlik doğrulama adımı eklemeye hazırsınız.

Şimdi, iki faktörlü kimlik doğrulamayı yapılandırmış olan kullanıcılar için bu adımı index.html adlı iş ortağından eklemeniz gerekir.

322a5c49d865a0d8.png

index.html içinde, location.href = "/account"; altında, 2FA'yı ayarlamışsa kullanıcıyı koşullu olarak ikinci faktör kimlik doğrulama sayfasına yönlendiren bir kod ekleyin.

Bu codelab'de kimlik bilgisi oluşturulduğunda kullanıcı otomatik olarak iki faktörlü kimlik doğrulamayı etkinleştirir.

server.js ürününün sunucu tarafı oturum denetimi de uyguladığını unutmayın. Bu sayede, yalnızca kimliği doğrulanmış kullanıcılar account.html ürününe erişebilir.

const { authStatus } = response;
if (authStatus === authStatuses.COMPLETE) {
  // The user is properly authenticated => navigate to account
  location.href = '/account';
} else if (authStatus === authStatuses.NEED_SECOND_FACTOR) {
  // Navigate to the two-factor-auth page because two-factor-auth is set up for this user
  location.href = '/second-factor';
}

Bu özelliği kullanmayı deneyin. 👩🏻 💻

  • Yeni bir kullanıcı olan burakbilgili ile giriş yapın.
  • Çıkış yap.
  • Hesabınıza johndoe olarak giriş yapın ve yalnızca şifre girmeniz gerektiğini doğrulayın.
  • Kimlik bilgisi oluşturun. Bu, johndoe olarak iki faktörlü kimlik doğrulamayı etkinleştirdiğiniz anlamına gelir.
  • Çıkış yap.
  • johndoe kullanıcı adınızı ve şifrenizi girin.
  • İkinci faktör kimlik doğrulaması sayfasına otomatik olarak nasıl gittiğinizi öğrenin.
  • (/account adresinde Hesap sayfasına erişmeyi deneyin. Tam olarak kimlik doğrulaması yapmadığınız için dizin sayfasına nasıl yönlendirildiğinizi not edin: İkinci bir faktör eksik)
  • İkinci faktörlü kimlik doğrulama sayfasına dönün ve ikinci faktör kimlik doğrulaması için Güvenlik anahtarını kullan'ı tıklayın.
  • Artık giriş yapmış durumdasınız ve Hesap sayfanızı görmeniz gerekir.

8. Kimlik bilgilerinin kullanımını kolaylaştırma

Güvenlik anahtarıyla iki faktörlü kimlik doğrulamayla temel işlevleri tamamladınız 📲

Ama... Fark ettiniz mi?

Kimlik bilgisi listemiz şu anda çok kullanışlı değil: Kimlik bilgisi kimliği ve ortak anahtar, kimlik bilgilerini yönetirken yararlı olmayan uzun dizelerdir! İnsanlar uzun dizeler ve rakamlarla çok iyi değil 🤖

Bu nedenle, bunu geliştirelim ve kimlik bilgilerini, insanların okuyabileceği dizelerle adlandırıp yeniden adlandıralım.

Yeniden adlandırma kimlik bilgilerine göz atın

Çok fazla çığır açan bir işlem yapmayarak bu işlevi uygularken size zaman kazandırmak amacıyla başlangıç kodundaki auth.client.js adresinde, kimlik bilgilerini yeniden adlandırmak için bir işlev eklendi:

async function renameCredential(credId, newName) {
  const params = new URLSearchParams({
    credId,
    name: newName
  });
  return _fetch(
    `/auth/credential?${params}`,
    "PUT"
  );
}

Bu, normal bir veritabanı güncelleme çağrısıdır: İstemci, arka uça bir kimlik bilgisi ve bu kimlik bilgisi için yeni bir adla bir PUT isteği gönderir.

Özel kimlik bilgisi adlarını uygulayın

account.html içinde boş rename işlevine dikkat edin.

Aşağıdaki kodu ekleyin:

// Rename a credential
async function rename(credentialId) {
  // Let the user input a new name
  const newName = window.prompt(`Name this credential:`);
  // Rename only if the user didn't cancel AND didn't enter an empty name
  if (newName && newName.trim()) {
    try {
      // Make the backend call to rename the credential (the name is sanitized) server-side
      await renameCredential(credentialId, newName);
    } catch (e) {
      // Alert the user that something went wrong
      if (Array.isArray(e)) {
        alert(
          // `msg` not `message`, this is the key's name as per the express validator API
          `Renaming failed. ${e.map((err) => `${err.msg} (${err.param})`)}`
        );
      } else {
        alert(`Renaming failed. ${e}`);
      }
    }
    // Refresh the credential list to display the new name
    await updateCredentialList();
  }
}

Kimlik bilgilerini yalnızca kimlik bilgisi başarıyla oluşturulduktan sonra adlandırmanız daha mantıklı olabilir. Bu yüzden, ad olmadan bir kimlik bilgisi oluşturalım ve başarıyla oluşturduktan sonra kimlik bilgisini yeniden adlandıralım. Ancak bu durumda iki arka uç araması elde edilir.

Kullanıcıların kaydolduktan sonra kimlik bilgilerini adlandırmasını sağlamak için register() içinde rename işlevini kullanın:

async function register() {
  let user = {};
  try {
    const user = await registerCredential();
    // Get the latest credential's ID (newly created credential)
    const allUserCredentials = user.credentials;
    const newCredential = allUserCredentials[allUserCredentials.length - 1];
    // Rename it
    await rename(newCredential.credId);
  } catch (e) {
    // ...
  }
  // Refresh the credential list to display the new credential
  await updateCredentialList();
}

Kullanıcı girişinin arka uçta doğrulanıp temizleneceğini unutmayın:

  check("name")
    .trim()
    .escape()

Görünen kimlik bilgisi adları

templates.js içinde getCredentialHtml adlı işletmeye gidin.

Kimlik bilgisi kartının üst kısmında kimlik bilgisi adını görüntülemek için zaten kod bulunduğunu unutmayın:

// Register credential
const getCredentialHtml = (credential, removeEl, renameEl) => {
 const { name, credId, publicKey } = credential;
 return html`
    <div class="credential-card">
      <div class="credential-name">
        ${name
          ? html`
              ${name}
            `
          : html`
              <span class="unnamed">(Unnamed)</span>
            `}
      </div>
     // ...
    </div>
  `;
};

Bu özelliği kullanmayı deneyin. 👩🏻 💻

  • Kimlik bilgisi oluşturun.
  • Adlandırmanız istenir.
  • Yeni bir ad girip Tamam'ı tıklayın.
  • Kimlik bilgisi artık yeniden adlandırıldı.
  • İşlemi tekrarlayın ve ad alanını boş bırakıp her şeyin düzgün çalışıp çalışmadığını kontrol edin.

Kimlik bilgisi yeniden adlandırmayı etkinleştir

Kullanıcıların kimlik bilgilerini yeniden adlandırması gerekebilir. Örneğin, ikinci bir anahtar ekliyorlar ve ayırt edilebilmeleri için ilk anahtarlarını yeniden adlandırmak istiyorlar.

account.html içinde, boş bir renameEl işlevini arayın ve ona aşağıdaki kodu ekleyin:

// Rename a credential via HTML element
async function renameEl(el) {
  // Define the ID of the credential to update
  const credentialId = el.srcElement.dataset.credentialId;
  // Rename the credential
  await rename(credentialId);
  // Refresh the credential list to display the new name
  await updateCredentialList();
}

Şimdi templates.js&#3getCredentialHtml içinde class="flex-end" div bölümüne şu kodu ekleyin: Bu kod, kimlik bilgisi kartı şablonuna bir Yeniden adlandır düğmesi ekler. Bu düğme tıklandığında, az önce oluşturduğumuz renameEl işlevi çağrılır:

const getCredentialHtml = (credential, removeEl, renameEl) => {
// ...
 <div class="flex-end">
  <button
    data-credential-id="${credId}"
    @click="${renameEl}"
    class="secondary right"
  >
   Rename
  </button>
 </div>
 // ...
  `;
};

Bu özelliği kullanmayı deneyin. 👩🏻 💻

  • Yeniden adlandır'ı tıklayın.
  • İstendiğinde yeni bir ad girin.
  • Tamam'ı tıklayın.
  • Kimlik bilgisi başarıyla yeniden adlandırılmalıdır ve liste otomatik olarak güncellenmelidir.
  • Sayfa yeniden yüklendiğinde yeni ad yine gösterilir (yeni adın sunucu tarafında kalıcı olduğu gösterilir).

Kimlik bilgisi oluşturma tarihini gösterme

navigator.credential.create() ile oluşturulan kimlik bilgilerinde oluşturma tarihi yer almıyor.

Ancak bu bilgi, kimlik bilgilerinin ayırt edilmesinde kullanıcı açısından faydalı olacağından, başlangıç kodundaki sunucu tarafı kitaplığında küçük değişiklikler yaptık ve yeni kimlik bilgilerini depoladıktan sonra Date.now() değerine eşit bir creationDate alanı ekledik.

Kullanıcıya templates.js class="creation-date" div içinde, oluşturma tarihi bilgilerini göstermek için aşağıdakileri ekleyin:

<div class="creation-date">
  <label>Created:</label>
  <div class="info">
    ${new Date(creationDate).toLocaleDateString()}
    ${new Date(creationDate).toLocaleTimeString()}
  </div>
</div>

9. Kodunuzu geleceğe uygun hale getirme

Şimdiye kadar kullanıcıdan yalnızca basit bir dolaşım kimlik doğrulayıcısını kaydettirmesini istedik. Daha sonra oturum açma sırasında ikinci bir faktör olarak kullanılır.

Daha gelişmiş bir yaklaşım, daha güçlü bir kimlik doğrulayıcı türüne güvenmektir: Kullanıcı doğrulaması dolaşım kimlik doğrulayıcısı (UVRA). UVRA, tek adımlı oturum açma akışlarında iki kimlik doğrulama faktörü ve kimlik avına karşı koruma sağlayabilir.

İdeal olarak, her iki yaklaşımı da desteklersiniz. Bunu yapmak için kullanıcı deneyimini özelleştirmeniz gerekir:

  • Bir kullanıcının yalnızca basit (kullanıcı doğrulaması olmayan) dolaşım kimlik doğrulayıcısı varsa, kimlik avına dayanıklı hesap önyüklemesi için bu tanımlayıcıyı kullanmasına izin verin ancak bir kullanıcı adı ve şifre de yazması gerekir. Codelab'imiz zaten bunu yapıyor.
  • Başka bir kullanıcı, kullanıcı doğrulaması dolaşımı için daha gelişmiş bir kimlik doğrulayıcı sistemine sahipse hesap başlatma sırasında şifre adımını, hatta kullanıcı adı adımını atlayabilir.

İsteğe Bağlı Şifresiz Oturum Açma ile Kimlik Avına Dayanıklı Hesap Önyüklemesi hakkında daha fazla bilgi edinin.

Bu codelab'de, kullanıcı deneyimini gerçekleştirmeyeceğiz, ancak kod tabanınızı, kullanıcı deneyimini özelleştirmek için ihtiyacınız olan verilere sahip olacak şekilde ayarlayacağız.

Bunun için iki şey gereklidir:

  • Arka uç ayarlarınızda residentKey: preferred belirleyin. Bu işlem sizin için zaten yapılmıştır.
  • Bulunabilir bir kimlik bilgisinin (yerleşik anahtarı olarak da adlandırılır) oluşturulup oluşturulmadığını öğrenmek için bir yöntem oluşturun.

Bulunabilir bir kimlik bilgilerinin oluşturulup oluşturulmadığını öğrenmek için:

  • Kimlik bilgisi oluşturulduktan sonra credProps değerini sorgulayın (credProps: true).
  • Kimlik bilgisi oluşturulduktan sonra transports değerini sorgulayın. Bu sayede, temel platformun UVRA işlevini destekleyip desteklemediğini, örneğin cep telefonu olup olmadığını belirleyebilirsiniz.
  • credProps ve transports değerini arka uçta depolayın. Bu işlem, başlangıç kodu için zaten yapılmıştır. Merak ediyorsanız auth.js ürününe göz atın.

credProps ve transports değerini alıp arka uça gönderelim. auth.client.js bölümünde, registerCredential ayarlarını şu şekilde değiştirin:

  • navigator.credentials.create numaralı telefonu aradığınızda bir extensions alanı ekleyin
  • Kimlik bilgilerini depolama için arka uça göndermeden önce encodedCredential.transports ve encodedCredential.credProps değerlerini ayarlayın.

registerCredential şöyle görünmelidir:

async function registerCredential() {
  // Fetch the credential creation options from the backend
  const credentialCreationOptionsFromServer = await _fetch(
    '/auth/credential-options',
    'POST'
  );
  // Decode the credential creation options
  const credentialCreationOptions = decodeServerOptions(
    credentialCreationOptionsFromServer
  );
  // Create a credential via the browser API; this will prompt the user
  const credential = await navigator.credentials.create({
    publicKey: {
      ...credentialCreationOptions,
      extensions: {
        credProps: true,
      },
    },
  });
  // Encode the newly created credential to send it to the backend
  const encodedCredential = encodeCredential(credential);
  // Set transports and credProps for more advanced user flows
  encodedCredential.transports = credential.response.getTransports();
  encodedCredential.credProps =
    credential.getClientExtensionResults().credProps;
  // Send the encoded credential to the backend for storage
  return await _fetch('/auth/credential', 'POST', encodedCredential);
}

10. Tarayıcılar arası destek sağlama

Chromium dışı tarayıcıları destekleme

public/auth.client.js&#39 registerCredential işlevinde, yeni oluşturulan kimlik bilgisi hakkında credential.response.getTransports() kodunu çağırarak bu bilgiyi arka uçta sunucuya ipucu olarak kaydederiz.

Ancak, getTransports() şu anda tüm tarayıcılarda uygulanmamaktadır (tarayıcılar arasında desteklenen getClientExtensionResults politikasından farklı olarak): getTransports() çağrısı, Firefox ve Safari'de hataya neden olur. Bu, kimlik bilgilerinin bu tarayıcılarda oluşturulmasını engeller.

Kodunuzun tüm önemli tarayıcılarda çalışması için encodedCredential.transports çağrısını bir koşul içinde sarmalayın:

if (credential.response.getTransports) {
  encodedCredential.transports = credential.response.getTransports();
}

Sunucuda transports değerinin transports || [] olarak ayarlandığını unutmayın. Firefox ve Safari'de transports listesi undefined olmaz ancak boş bir liste [] olur. Bu da hataları engeller.

WebAuthn'u desteklemeyen tarayıcılar kullanan kullanıcıları uyar

1e9c1be837d66ce8.png

WebAuthn tüm önemli tarayıcılarda desteklense de, WebAuthn'u desteklemeyen tarayıcılarda bir uyarı görüntülemek iyi bir fikirdir.

index.html öğesinde bu div'in varlığını inceleyin:

<div id="warningbanner" class="invisible">
⚠️ Your browser doesn't support WebAuthn. Open this demo in Chrome, Edge, Firefox or Safari.
</div>

index.html's satır içi komut dosyasında, banner'ı WebAuthn'u desteklemeyen tarayıcılarda görüntülemek için aşağıdaki kodu ekleyin:

// Display a banner in browsers that don't support WebAuthn
if (!window.PublicKeyCredential) {
  document.querySelector('#warningbanner').classList.remove('invisible');
}

Gerçek bir web uygulamasında daha ayrıntılı bir işlem yapar ve bu tarayıcılar için uygun bir yedek mekanizmaya sahip olursunuz. Ancak bu, WebAuthn desteğini nasıl kontrol edeceğinizi gösterir.

11. Tebrikler!

✨İşlemi tamamladınız!

Güvenlik anahtarıyla iki faktörlü kimlik doğrulamayı uyguladınız.

Bu codelab'de temel bilgileri ele aldık. 2FA için WebAuthn'u daha fazla keşfetmek istiyorsanız aşağıda bazı denemeler yapabilirsiniz:

  • Kimlik bilgisi kartına "Son kullanılan" bilgi ekleyin. Bu, kullanıcıların belirli bir güvenlik anahtarının etkin bir şekilde kullanılıp kullanılmadığını (özellikle de birden fazla anahtar kaydettilerse) belirlemeleri için yararlı bir bilgidir.
  • Daha sağlam hata giderme ve daha hassas hata mesajları uygulama.
  • auth.js bölümünü inceleyin ve özellikle kullanıcı doğrulamasını destekleyen bir anahtar kullandığınızda authSettings ayarlarından bazılarını değiştirdiğinizde neler olacağını keşfedin.