İçeriğe geç
academia.sh

Ders 20 / 22

Tarayıcıda Kimlik Doğrulama

Kimlik doğrulama ile yetkilendirmenin ayrımı, oturum çerezi ile taşıyıcı belirtecin karşılaştırılması, belirteç gövdesinin okunabilirliği, üç saklama seçeneğinin risk–fayda çözümlemesi ve çerez niteliklerinin işlevi.

İçindekiler

Ölçüm giriş formu artık değerini tutuyor, kurallarını iki tarafta aynı biçimde uyguluyor ve hatasını herkesin algılayabileceği biçimde sunuyor. Geriye formun sorduğu bir soru kaldı: bu ölçümü kim giriyor? Kuzey Yamaç kayıtlarına herkes yazamaz; gözlemcinin önce tanınması gerekir.

Bu ders kimlik akışının istemci tarafını ele alır: sunucu gözlemciyi tanıdıktan sonra verdiği kimlik kanıtı tarayıcıda nerede durur, hangi istekle birlikte gider ve bu kararın hangi riskleri açıp hangilerini kapattığı. Kapsam bilinçli olarak dardır — istemci güvenliğinin bütünü, içerik güvenlik ilkesi ve kaynaklar arası paylaşım dahil, Frontend Kalitesi kursunun güvenlik konusunda ele alınır. Burada yalnız kimlik akışının kendisi vardır.

Kimlik Doğrulama ile Yetkilendirme Ayrı Katmanlardır

Kimlik doğrulama (authentication), “sen kimsin” sorusunun yanıtıdır. Gözlemci kullanıcı adı ve parolasını verir, sunucu bunu doğrular ve bir kimlik kanıtı üretir.

Yetkilendirme (authorization), “bunu yapabilir misin” sorusunun yanıtıdır. Tanınmış bir gözlemcinin KY-01 istasyonuna ölçüm yazma hakkı olabilir, KY-02’ye olmayabilir.

İkisinin ayrı tutulması bir söz oyunu değildir. Yetkilendirme kararı her zaman sunucuda verilir; istemcinin yaptığı, kullanıcının erişemeyeceği bir düğmeyi göstermemektir. Bu görsel eleme bir güvenlik denetimi değil, bir kullanışlılık iyileştirmesidir: düğmeyi gizlemek, isteğin başka yollarla kurulmasını engellemez.

İki Taşıma Biçimi

Kimlik kanıtının istekle birlikte gitmesinin iki yolu vardır.

Oturum çerezi, sunucunun ürettiği rastgele bir tanımlayıcıyı taşır. Tanımlayıcının kendisi bir anlam taşımaz; sunucu onu kendi oturum kayıtlarında arar ve karşılığındaki kullanıcıyı bulur. Çerez, tarayıcı tarafından kendiliğinden gönderilir; uygulama kodu her isteğe bir şey eklemez.

Taşıyıcı belirteç (bearer token), kullanıcı bilgisini kendi içinde taşıyan imzalı bir dizgidir. JWT bu biçimin yaygın bir örneğidir. Sunucu her istekte kayıt aramak zorunda kalmaz, imzayı doğrulayıp gövdedeki iddialara bakar. Belirteç bir başlıkla elle gönderilir; tarayıcı bunu kendiliğinden yapmaz.

Ayrımın iki pratik sonucu vardır. Kendiliğinden gönderilme, oturum çerezini kaynaklar arası istek sahteciliğine (CSRF) açık kılar: başka bir sayfadan tetiklenen bir istek de çerezi yanında götürür. Elle gönderilme, taşıyıcı belirteci bu riskten uzak tutar ama belirtecin bir yerde saklanmasını zorunlu kılar — ve saklandığı yer yeni bir risk açar. Bu ödünleşim dersin çekirdeğidir.

Kendi içinde bilgi taşımanın ikinci sonucu geri alınamazlıktır. Oturum kaydı sunucuda silinince oturum o an biter. İmzalı bir belirteç ise süresi dolana kadar geçerli kalır; iptal edilmesi için sunucunun ayrıca bir geçersizler listesi tutması gerekir — ki bu, kayıt tutmama kazancını büyük ölçüde geri verir. Uygulamada bu, belirteçlerin kısa ömürlü tutulmasıyla dengelenir.

Belirteç Gövdesi Gizli Değildir

Belirteç imzalıdır; bu, içeriğinin değiştirilemeyeceği anlamına gelir, gizli olduğu anlamına gelmez. Aradaki fark, belirtecin içine ne konacağı ve istemcinin onunla ne yapabileceği sorularını doğrudan belirler.

// belirtec-okuma.mjs — belirtecin istemcide ne kadar okunabilir oldugu
import { createHmac, timingSafeEqual } from "node:crypto";

const b64 = (v) => Buffer.from(typeof v === "string" ? v : JSON.stringify(v))
  .toString("base64url");

// --- Sunucu tarafi: belirteci uretir ve imzalar -----------------------------
const GIZLI = "yalnız-sunucuda-duran-anahtar";
const SIMDI = 1768348800;                     // 2026-01-14T12:00:00Z, sinama icin sabit

function belirtecUret(iddialar, gizli) {
  const govde = `${b64({ alg: "HS256", typ: "JWT" })}.${b64(iddialar)}`;
  const imza = createHmac("sha256", gizli).update(govde).digest("base64url");
  return `${govde}.${imza}`;
}

const belirtec = belirtecUret({
  sub: "gozlemci-3", rol: "yazar", istasyon: "KY-01",
  iat: SIMDI, exp: SIMDI + 900,               // 15 dakika
}, GIZLI);

console.log("belirteç:", belirtec);

// --- Istemci tarafi: govdeyi imzayi dogrulamadan okur -----------------------
function govdeyiOku(belirtec) {
  const parcalar = belirtec.split(".");
  if (parcalar.length !== 3) return null;
  try { return JSON.parse(Buffer.from(parcalar[1], "base64url").toString("utf8")); }
  catch { return null; }
}

const iddialar = govdeyiOku(belirtec);
console.log("istemcinin okuduğu gövde:", JSON.stringify(iddialar));

// Sure denetimi bir kullanislilik hesabidir, bir yetki karari degildir.
const PAY = 30;                               // saat kaymasi payi (saniye)
const suresiDoldu = (i, simdi) => simdi >= i.exp - PAY;
for (const simdi of [SIMDI, SIMDI + 600, SIMDI + 880, SIMDI + 1000]) {
  console.log(
    `t=${String(simdi - SIMDI).padStart(4)}s  kalan=${String(iddialar.exp - simdi).padStart(4)}s`,
    ` yenilensin mi: ${suresiDoldu(iddialar, simdi) ? "evet" : "hayır"}`);
}

// --- Sunucu tarafi: imza dogrulamasi ---------------------------------------
function imzaGecerliMi(belirtec, gizli) {
  const [b, g, imza] = belirtec.split(".");
  const beklenen = createHmac("sha256", gizli).update(`${b}.${g}`).digest("base64url");
  const a = Buffer.from(imza), c = Buffer.from(beklenen);
  return a.length === c.length && timingSafeEqual(a, c);
}

// Govdesi degistirilmis bir belirtec: rol "yazar" yerine "yonetici".
const [bas, , eskiImza] = belirtec.split(".");
const degisik = `${bas}.${b64({ ...iddialar, rol: "yonetici" })}.${eskiImza}`;

console.log("\nözgün belirteç, sunucu doğrulaması :", imzaGecerliMi(belirtec, GIZLI));
console.log("gövdesi değiştirilmiş, sunucu       :", imzaGecerliMi(degisik, GIZLI));
console.log("gövdesi değiştirilmiş, istemci okur :",
  JSON.stringify(govdeyiOku(degisik).rol));
belirteç: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJnb3psZW1jaS0zIiwicm9sIjoieWF6YXIiLCJpc3Rhc3lvbiI6IktZLTAxIiwiaWF0IjoxNzY4MzQ4ODAwLCJleHAiOjE3NjgzNDk3MDB9.gwTdVAyLlHGq0DNaEXcRIsRnVA24TMTg5Q4ylPoN0aY
istemcinin okuduğu gövde: {"sub":"gozlemci-3","rol":"yazar","istasyon":"KY-01","iat":1768348800,"exp":1768349700}
t=   0s  kalan= 900s  yenilensin mi: hayır
t= 600s  kalan= 300s  yenilensin mi: hayır
t= 880s  kalan=  20s  yenilensin mi: evet
t=1000s  kalan=-100s  yenilensin mi: evet

özgün belirteç, sunucu doğrulaması : true
gövdesi değiştirilmiş, sunucu       : false
gövdesi değiştirilmiş, istemci okur : "yonetici"

Çıktı üç kural veriyor.

Gövde herkesin okuyabileceği bir metindir. Kodlama bir şifreleme değildir; belirteci eline geçiren herkes içindekini okur. Bu yüzden gövdeye kullanıcının kimliği ve yetki adları dışında bir şey — kişisel bilgi, iç sistem kimlikleri, gizli değer — konmaz.

İmza değişikliği yakalar ama yalnız sunucuda. Değiştirilmiş belirteç sunucu doğrulamasından geçmedi; imza gövdeyle uyuşmuyor. Aynı belirtecin gövdesini istemci sorunsuz okuyor ve rolü “yonetici” görüyor. Yani istemcinin okuduğu rol bir iddia değildir, yalnızca bir gösterimdir. Arayüz bu bilgiyle menü çizebilir; erişim kararı veremez.

Süre denetimi bir kullanışlılık hesabıdır. İstemci exp alanına bakıp belirteci süresi dolmadan yenileyebilir; böylece kullanıcı 401 yanıtıyla karşılaşmaz. Ama bu denetim bir yetki kararı değildir: istemcinin saati yanlış olabilir, gövde değiştirilmiş olabilir. Örnekteki otuz saniyelik pay, saat kaymasına karşı konur ve kararı erkene çeker. Kararı veren yine sunucudur.

Belirteç Deposu: Üç Seçenek

Belirtecin nerede saklanacağı üç seçeneğe indirgenir ve üçü farklı riskleri açar. Tabloda “betik çalıştırılırsa” ifadesi, sayfada uygulamaya ait olmayan bir betiğin çalıştığı durumu — siteler arası betik çalıştırma, XSS — anlatır.

Ölçüt Bellekte değişken Web Storage HttpOnly çerez
Betik çalıştırılırsa okunur mu Evet (aynı çalışma bağlamında) Evet, doğrudan Hayır, betikten erişilemez
Sahtecilik (CSRF) riski Yok, elle gönderilir Yok, elle gönderilir Var; nitelikler ve belirteçle daraltılır
Sayfa yenilenince Kaybolur Kalır Kalır
Sekmeler arasında Paylaşılmaz Paylaşılır Paylaşılır
Alt alan adlarına sızma Yok Kaynak başına ayrıdır Domain yazılırsa olur
Sunucu tarafında iptal Ömür kısaysa kolay Ömür kısaysa kolay Oturum kaydıyla anında

Tablodan çıkan tek bir “doğru seçenek” yoktur; iki yaygın ve savunulabilir düzen vardır.

Erişim belirteci bellekte, yenileme HttpOnly çerezde. Kısa ömürlü erişim belirteci hiçbir kalıcı depoya yazılmaz; sayfa yenilendiğinde kaybolur ve çerezle yapılan bir yenileme isteğiyle yeniden alınır. Betik çalıştırılırsa erişim belirteci okunabilir ama ömrü dakikalarla sınırlıdır; yenileme belirtecine hiç erişilemez.

Her şey HttpOnly çerezde, oturum kaydı sunucuda. Betikten hiçbir kimlik değeri okunamaz ve iptal anında yapılabilir. Karşılığında sahtecilik riski üstlenilir ve niteliklerle daraltılır.

Kaçınılması gereken düzen, yenileme belirtecinin Web Storage’da tutulmasıdır: uzun ömürlü bir kimlik kanıtı, betikten okunabilen bir yerde durur. Kısa ömürlü erişim belirtecinin Web Storage’da tutulması ise bir ödünleşimdir — sayfa yenilenmesine dayanma kazancı karşılığında okunabilirlik riski alınır.

Depolama seçiminin sınırı şudur: betik çalıştırılabiliyorsa depo neredeyse önemsizdir. Belirteç HttpOnly çerezde olsa bile, sayfada çalışan bir betik kullanıcının adına istek gönderebilir. Depolama kararı riski daraltır, ortadan kaldırmaz; asıl savunma, betiğin hiç çalıştırılamamasıdır ve o savunma Frontend Kalitesi kursunun konusudur.

Çerez Niteliklerinin İşlevi

Çerez kullanan düzenlerde dört nitelik doğrudan güvenlik işlevi taşır ve dördü de sunucunun yazdığı başlıkta bildirilir.

HttpOnly, çerezin betikten okunmasını engeller. Bu nitelik yazılmamışsa çerez, Web Storage’dan farksız biçimde okunabilir hâle gelir.

Secure, çerezin yalnız şifreli bağlantıda gönderilmesini sağlar. Yerel geliştirme adresleri dışında istisnası yoktur.

SameSite, çerezin başka bir siteden tetiklenen isteklerde gönderilip gönderilmeyeceğini belirler. Strict hiçbir dış bağlamda göndermez ve dış bağlantıyla gelen kullanıcının oturumsuz görünmesine yol açar; Lax üst düzey gezinmelerde gönderir, gömülü isteklerde göndermez; None her durumda gönderir ve yalnız Secure ile birlikte kullanılabilir. Sahtecilik riskini asıl daraltan nitelik budur, ama tek başına yeterli sayılmaz: durum değiştiren istekler ayrıca bir sahtecilik belirteciyle korunur.

Path ve Domain, çerezin gönderileceği yolları ve alan adlarını belirler. Domain yazmak çerezi bütün alt alan adlarına açar; bir alt alan adı ele geçtiğinde oturumun da etkilenmesi demektir. Yazılmadığında çerez yalnız kendi ana bilgisayar adına gider ki çoğu durumda istenen budur. Çerez adına konan __Host- öneki bu daraltmayı kural hâline getirir: bu önekli bir çerez ancak Secure ile, Domain yazılmadan ve kök yolla kabul edilir.

İstemcinin Kimlik Durumu

Uygulamanın “oturum açık mı” sorusuna verdiği yanıt da bir durum parçasıdır ve Durum Yönetimi konusundaki ayrımlara uyar. Bu durum, belirtecin kendisinden değil, belirtecin varlığı ve sunucunun yanıtlarından türetilir.

Uygulama açılışında kimlik durumu bilinmez. Bellekte belirteç tutan bir düzende sayfa yenilendiğinde elde hiçbir şey yoktur; oturumun sürüp sürmediğini öğrenmenin tek yolu sunucuya sormaktır. Bu, kimlik durumunun dördüncü bir değeri olduğu anlamına gelir: bilinmiyor. Bu değer olmadan yazılan arayüzler, açılışta bir an için oturum kapalı görünür ve kullanıcıyı giriş ekranına atar.

Rota koruma da bu duruma bağlanır. Yönlendirme konusundaki rota koruma kuralı istemci tarafında görünürlüğü yönetir; korunan bir rotanın verisi yine sunucudan 401 alarak korunur. İstemci koruması atlanabilir; sunucu koruması atlanamaz.

Özet

  • Kimlik doğrulama “kimsin”, yetkilendirme “bunu yapabilir misin” sorusudur; yetki kararı her zaman sunucuda verilir, istemci yalnız görünürlüğü yönetir.
  • Oturum çerezi tarayıcı tarafından kendiliğinden gönderilir ve sahteciliğe açıktır; taşıyıcı belirteç elle gönderilir ve saklanmak zorundadır.
  • Belirtecin imzalı olması içeriğinin değiştirilemez olduğunu söyler, gizli olduğunu değil; gövde herkesçe okunabilir ve istemcinin okuduğu rol bir yetki kanıtı değildir.
  • Süre denetimi istemcide bir kullanışlılık hesabıdır; saat kayması payıyla erkene çekilir ama kararı sunucu verir.
  • Üç saklama seçeneği farklı riskler açar; savunulabilir iki düzen, erişim belirtecini bellekte yenilemeyi HttpOnly çerezde tutmak ya da her şeyi çerezde tutup sahteciliğe karşı ayrıca korunmaktır.
  • HttpOnly, Secure, SameSite ve Domain nitelikleri riski daraltır; sayfada betik çalıştırılabiliyorsa hiçbir depolama seçeneği tek başına korumaz.

Sonraki Adım

Bu ders belirtecin kısa ömürlü olmasını bir savunma olarak sundu ama kısa ömrün bir bedeli var: on beş dakikada bir oturumun düşmesi. Gözlemci ölçüm formunu doldururken belirtecin süresi dolarsa gönderim 401 ile döner ve yazdığı her şey tehlikeye girer. Sonraki ders bu bedeli ödemeden kısa ömrü korumanın yolunu kurar: kullanıcı fark etmeden yapılan yenileme, aynı anda düşen birden çok isteğin tek bir yenilemede toplanması ve oturumun her yerde aynı anda kapanması.

İ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