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,SameSiteveDomainnitelikleri 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.