İçeriğe geç
academia.sh

Ders 15 / 15

Kimlik Doğrulama Akışları

İsteğin kimliğini oturum tabanlı ve taşıyıcı belirteçli akışta ölçmek, imzanın neyi koruyup neyi korumadığını görmek ve kursun on beş dersini tek tabloda toplamak.

Yayın Güncelleme

Bu derste

  • Oturum tabanlı ve imzalı taşıyıcı belirteçli akışı yan yana kurar ve isteğin kimliğini taraf ölçümüyle sınıflar
  • hash_hmac() ve hash_equals() ile kullanici.imza biçiminde bir belirteç üretir ve doğrular
  • Oturumun sunucuda iptal edilebildiğini, imzalı belirtecin ise anahtar değişmeden geçerli kaldığını ölçer
  • Kursun on beş dersini taraf sınıfıyla tek tabloda okur
İçindekiler

Parola Saklama dersi saklanan değeri ve onu üreten ayarı ölçtü. Parola doğrulandıktan sonra da bir soru kalır: sonraki istekler hangi kullanıcı adına işleniyor ve bunu kim belirliyor? Kimlik doğrulama (authentication) kullanıcının kim olduğunu saptar; ne yapabileceğini saptayan yetkilendirme ayrı bir iştir ve Kimlik Doğrulama ve Yetkilendirme dersinde çerçeve içinde ele alındı. Orada kapının isteği koşturup koşturmadığı ölçüldü. Burada her parça koşar; sorulan, isteğin kimliğini kimin değiştirebildiğidir.

GÜ6. Bu derste parola yalnız doğrulanır. Parola tahmin etmek, oturum çalmak, belirteç taklit etmek ve yetki aşmak modellenmez. İstemcinin sapması her zaman nötr bir değerdir: başka bir kullanıcının kendi girişinden aldığı oturum kimliği ya da belirteç.

İki Akış

HTTP durumsuzdur; sunucu her isteği ayrı görür. Kimliği isteğe iki yoldan bağlarız. Oturum tabanlı akışta giriş sonrası sunucu rastgele bir oturum kimliği üretir, kimlikten kullanıcıya giden kaydı kendi belleğinde tutar ve kimliği istemciye verir; istemci onu her istekte geri gönderir. Taşıyıcı belirteçli akışta sunucu kayıt tutmaz: kullanıcı adını hash_hmac() ile imzalar ve kullanici.imza biçiminde bir taşıyıcı belirteç (bearer token) verir. İstemci belirteci her istekte, örneğin Authorization başlığında taşır; sunucu imzayı yeniden hesaplayıp hash_equals() ile karşılaştırır.

Ölçüm çekirdeği önceki derslerdekiyle aynıdır: sina() sonucu bir kez sapmasız, sonra her dış tarafın sapmasıyla hesaplar; taraf() sırayla istemciye, üçüncü tarafa, ortama bakar ve hiçbiri değiştiremediyse sunucuyu söyler. Dosya her derste olduğu gibi değişmeden verilir.

<?php
// taraf.php — bu sonucu kim değiştirebiliyor?
declare(strict_types=1);

// Kodun dışındaki taraflar, karar sırasıyla: her istek, her dış çağrı, her dağıtım.
const DIS_TARAFLAR = ['istemci', 'üçüncü taraf', 'ortam'];

/** Sonucu bir kez sapmasız, sonra her dış tarafın sapmasıyla ayrı ayrı hesaplar. */
function sina(callable $sonuc, array $sapmalar): array {
    $taban = $sonuc([]);
    $degisti = [];
    foreach (DIS_TARAFLAR as $taraf) {
        if (array_key_exists($taraf, $sapmalar)) {
            $degisti[$taraf] = $sonuc([$taraf => $sapmalar[$taraf]]) !== $taban;
        }
    }
    return $degisti;
}

/** Sonucu değiştirebilen ilk dış taraf; hiçbiri değiştiremiyorsa sunucu. */
function taraf(array $degisti): string {
    foreach (DIS_TARAFLAR as $t) {
        if ($degisti[$t] ?? false) return $t;
    }
    return 'sunucu';
}

function tarafRaporu(string $ad, callable $sonuc, array $sapmalar): string {
    $degisti = sina($sonuc, $sapmalar);
    $satirlar = [];
    foreach ($degisti as $t => $d) $satirlar[] = $t . ($d ? ' değiştirdi' : ' değiştiremedi');
    $sonTaraf = taraf($degisti);
    echo "$ad → $sonTaraf\n    ", implode(' · ', $satirlar), "\n";
    return $sonTaraf;
}

Aşağıdaki dosya iki akışı kurar. Parolalar yalnız password_verify() ile sınanır, oturum kimliği random_bytes() ile üretilir ve belirteç imzası hash_hmac('sha256', ...) ile atılır. İmza anahtarı burada bir eğitim dizgesidir; gerçek bir uygulamada anahtar kodda durmaz, ortam değişkeninden okunur.

<?php
// kimlik.php — oturum ve belirteç akışı
declare(strict_types=1);

const IMZA_ANAHTARI = 'ders-anahtari';

function karmalar(): array {
    $karmalar = [];
    foreach (['ayse' => 'parola-bir', 'mehmet' => 'parola-iki'] as $kullanici => $parola) {
        $karmalar[$kullanici] = password_hash($parola, PASSWORD_BCRYPT, ['cost' => 10]);
    }
    return $karmalar;
}

function girisDogrula(array $karmalar, string $kullanici, string $parola): bool {
    return isset($karmalar[$kullanici]) && password_verify($parola, $karmalar[$kullanici]);
}

final class OturumDeposu {
    private array $kayitlar = [];

    public function ac(string $kullanici): string {
        $kimlik = bin2hex(random_bytes(16));
        $this->kayitlar[$kimlik] = $kullanici;
        return $kimlik;
    }

    public function kullanici(string $kimlik): string {
        return $this->kayitlar[$kimlik] ?? 'anonim';
    }

    public function kapat(string $kimlik): void {
        unset($this->kayitlar[$kimlik]);
    }
}

function belirtecUret(string $kullanici, string $anahtar = IMZA_ANAHTARI): string {
    return $kullanici . '.' . hash_hmac('sha256', $kullanici, $anahtar);
}

function belirtecKullanici(string $belirtec, string $anahtar = IMZA_ANAHTARI): string {
    $parcalar = explode('.', $belirtec, 2);
    if (count($parcalar) !== 2) return 'anonim';
    $beklenen = hash_hmac('sha256', $parcalar[0], $anahtar);
    return hash_equals($beklenen, $parcalar[1]) ? $parcalar[0] : 'anonim';
}

İsteğin Kimliği

Sonuç, isteğin işlendiği kullanıcı adıdır. İstemcinin sapması, başka bir kullanıcının kendi girişinden aldığı kimlik bilgisini sunmaktır: oturum akışında mehmet’in oturum kimliği, belirteç akışında mehmet’in belirteci. Rastgele değerler basılmaz; yalnız uzunlukları ve sonuçları basılır.

<?php
// akis.php — isteğin kimliği, iki akış
declare(strict_types=1);
require 'taraf.php';
require 'kimlik.php';

$karmalar = karmalar();
foreach (['ayse' => 'parola-bir', 'mehmet' => 'parola-iki'] as $kullanici => $parola) {
    echo "giriş $kullanici ", var_export(girisDogrula($karmalar, $kullanici, $parola), true), "\n";
}
echo 'yanlış parola ', var_export(girisDogrula($karmalar, 'ayse', 'parola-iki'), true), "\n";

$depo = new OturumDeposu();
$oturumAyse = $depo->ac('ayse');
$oturumMehmet = $depo->ac('mehmet');
$belirtecAyse = belirtecUret('ayse');
$belirtecMehmet = belirtecUret('mehmet');
echo 'oturum kimliği ', strlen($oturumAyse), ' karakter · imza ', strlen(explode('.', $belirtecAyse)[1]), " karakter\n";

tarafRaporu('kimlik (oturum)', fn(array $s) => $depo->kullanici($s['istemci'] ?? $oturumAyse), ['istemci' => $oturumMehmet]);
tarafRaporu('kimlik (belirteç)', fn(array $s) => belirtecKullanici($s['istemci'] ?? $belirtecAyse), ['istemci' => $belirtecMehmet]);

echo 'uyuşmayan imza ', belirtecKullanici('ayse.' . str_repeat('0', 64)), "\n";
echo 'tanınmayan oturum kimliği ', $depo->kullanici(str_repeat('0', 32)), "\n";
giriş ayse true
giriş mehmet true
yanlış parola false
oturum kimliği 32 karakter · imza 64 karakter
kimlik (oturum) → istemci
    istemci değiştirdi
kimlik (belirteç) → istemci
    istemci değiştirdi
uyuşmayan imza anonim
tanınmayan oturum kimliği anonim

İki akışta da sonuç istemcidir. İstemci sunduğu oturum kimliğini ya da belirteci değiştirince isteğin kimliği ayse’den mehmet’e geçti. Çünkü kimliği isteğe taşıyan, her istekte onu gönderen istemcidir; sunucu yalnız sunulan bilgiyi doğrular. Doğrulama, aynı akışın iki ayrı noktasında farklı çalışır: oturum akışında sunucu kimliği kendi kaydında arar, belirteç akışında imzayı hesaplayıp karşılaştırır.

GÜ7. Son iki satır savunmanın sınırını gösterir. İmzası uyuşmayan belirteç bir kullanıcı adı üretmedi, tanınmayan oturum kimliği de üretmedi: ikisi de anonim verdi. İstemci bir kullanıcı adı seçemez; elinde sunucunun kendisine verdiği kimlik bilgilerinden birini sunabilir ya da hiçbirini sunamaz. Yani savunma sonucu istemciden almaz, istemcinin seçebileceği kümeyi sunucunun verdiği değerlere daraltır. Bu, kursun başından beri tekrarlanan cümledir: savunma her zaman sahibi değiştirmez, kimi zaman yalnız neyin değiştirilebileceğini daraltır.

Çerezler dersi imzalı çerezde aynı şeyi göstermişti. Oturumlar dersinde sepeti sunucuya taşıyan düzenek burada kimliği taşımaz, çünkü kimliğin kendisi istemcinin elindeki bir değerdir; Oturumlar dersinin sınırı da aynıydı: hangi oturumun açılacağı istemcide kalır.

Sınırlayıcı Ölçüm

İki akışın sonucu aynı tarafa düştü, ama sunucunun elindeki çare aynı değil. Oturum akışında sunucu kaydı sildiğinde kimlik artık hiçbir kullanıcıya gitmez. Belirteç akışında sunucu kayıt tutmadığı için silecek bir şey yoktur; imza anahtarı değişmedikçe belirteç geçerli kalır. Anahtar ortamdan geliyorsa onu değiştiren dağıtımdır ve değişiklik bütün belirteçleri birden düşürür.

<?php
// cikis.php — çıkış ve anahtar değişimi
declare(strict_types=1);
require 'taraf.php';
require 'kimlik.php';

$depo = new OturumDeposu();
$oturum = $depo->ac('ayse');
$belirtec = belirtecUret('ayse');

echo 'çıkıştan önce oturum ', $depo->kullanici($oturum), ' · belirteç ', belirtecKullanici($belirtec), "\n";
$depo->kapat($oturum);
echo 'çıkıştan sonra oturum ', $depo->kullanici($oturum), ' · belirteç ', belirtecKullanici($belirtec), "\n";

tarafRaporu(
    'kimlik (anahtar ortamdan)',
    fn(array $s) => belirtecKullanici($belirtec, $s['ortam'] ?? IMZA_ANAHTARI),
    ['ortam' => 'yeni-anahtar']
);
çıkıştan önce oturum ayse · belirteç ayse
çıkıştan sonra oturum anonim · belirteç ayse
kimlik (anahtar ortamdan) → ortam
    ortam değiştirdi

Çıkıştan sonra oturum anonim verdi, belirteç ayse vermeyi sürdürdü. Belirtecin çıkışta ortadan kalkmasını isteyen bir uygulama sunucuya ayrıca bir iptal kaydı eklemek zorundadır; bu da belirtecin kayıtsız olma avantajını geri alır. Anahtar değişince belirteç düştü ve sonucu değiştiren ortamdır: anahtarı dağıtım belirler. GÜ8. Bu derste süre ölçülmez; bir belirtecin bitiş zamanı taşıması ya da oturumun boşta kalınca kapanması saat gerektirir ve ölçüm kapsamında değildir. Burada yalnız sunucunun hangi çareye sahip olduğuna bakıldı.

Özet

  • Bu dersin ölçü sınıfı istemci: oturum kimliğini ya da belirteci değiştiren istemci isteğin kimliğini değiştirdi, iki akışta da.
  • Oturum akışı kimliği sunucu belleğindeki bir kayıttan, belirteç akışı hash_hmac() imzasını hash_equals() ile doğrulayarak çözer; ikisi de tanınmayan değeri anonim’e düşürür.
  • İmza istemcinin seçebileceği kümeyi sunucunun verdiği belirteçlerle sınırlar, sahibi değiştirmez.
  • Oturum sunucuda iptal edilir; imzalı belirteç anahtar değişmedikçe geçerli kalır ve anahtar ortamdan geliyorsa onu değiştiren ortamdır.

Kurs Kapanışı

Bu kursta on beş derste bir sonucu kimin değiştirebildiğini ölçtük: oturum ve durum, veri erişimi, güvenlik. Her ders kendi sonucunu ve bir karşıt ya da sınırlayıcı ölçümünü taşıdı. Aşağıdaki tablo hepsini tek yerde toplar; taraf hücreleri kaynak dersin özetinden alınmıştır.

Ders Ölçülen sonuç Taraf Sapma ya da karşıt ölçüm
Çerezler Çerezdeki sepet, imzalı çerez istemci İmza değiştirilen değeri fark etti, sahibi değiştirmedi
Oturumlar $_SESSION’daki sepet sunucu Aynı sepet=5 çerezde sonucu değiştirdi
Yapılandırma ve Ortam Değişkenleri UYG_VERITABANI ile bağlantı adı ortam Koda yazılan değer sunucuda kalır, taşınmaz
Veritabanı Erişim Katmanı Dönen değerin tipi ortam Açık int dönüşümüyle sunucu
İşlemler İki hesabın bakiye toplamı sunucu İşlemsiz aktarımda ortam toplamı değiştirdi
Göç Yönetimi Uygulanan göç sayısı ortam Şema sürümü her ortamda aynı: sunucu
Nesne–Tablo Bağlamasının Sorguları Gönderilen sorgu sayısı sunucu Tembel yüklemede satırla büyüdü: istemci
Veri Biçimleri Ayrıştırılan fiyat üçüncü taraf Kendi JSON’umuzdaki sayı gösterimi: sunucu
HTTP İstemcisi Servisin bildirdiği fiyat üçüncü taraf Varsayılan değer sonucu sınırlar, sahibi değiştirmez
Girdi Doğrulama ve Arındırma Doğrulanan adet istemci Arındırma bozuk girdiyi başka değere çevirir
SQL Enjeksiyonu Önleme prepare()’e giden metin sunucu Birleştirmede istemci
Siteler Arası Betik Çalıştırma Önleme Basılan HTML istemci Çift kaçışlama metni bozar
İstek Sahteciliği Koruması E-posta güncellemesi sunucu Belirteçsiz güncellemede üçüncü taraf
Parola Saklama Karma öneki ortam Açık maliyette sunucu
Kimlik Doğrulama Akışları İsteğin kimliği istemci Anahtar ortamdan gelince ortam

Tabloda iki ayrı dağılım var ve ikisi ayrı şeyleri sayar. Derslerin dağılımı her dersin ölçü sınıfını sayar: 5 sunucu · 4 ortam · 4 istemci · 2 üçüncü taraf. Çekirdeğin dağılımı Çerezler dersinde koşturulan on sonucu sayar: 4 sunucu · 2 ortam · 2 istemci · 2 üçüncü taraf. Biri on beş dersin, öteki on ölçümün dağılımıdır; aynı sayılar değildir ve biri ötekini doğrulamaz.

Kursun söylediği tek cümle şudur: bir web uygulamasında sonucun sahibi kodu yazan değildir. İstemci isteği, ortam ayarı, üçüncü taraf veriyi getirir; savunma düzenekleri sahibi kimi zaman sunucuya taşır, kimi zaman yalnız daraltır. Sıradaki kurs bu uygulamayı üretim ortamına taşır: dağıtım, çalıştırma ve başarım konusu.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ ↵ Esc gezin · aç · kapat