İçeriğe geç
academia.sh

Ders 23 / 23

Nesne Düzeyi Yetki Denetimi

Rol, kapsam ve kiracı denetimlerinden sonra kalan sahiplik sınırı, denetimin sorgu içine konmasıyla dışında bırakılmasının farkı, yanıt kodundan sızan varlık bilgisi ve sahiplik ölçütünün taranabilir hâle getirilmesi.

İçindekiler

Kiracı sınırı, isteğin hangi kümeye dokunabileceğini belirledi. Küme içinde ise hâlâ tek tek nesneler vardır. Aynı kütüphanenin iki üyesi aynı rolü taşır, aynı kapsamlı belirteci kullanır ve aynı kiracıya aittir; buna rağmen biri diğerinin ödünç kaydını görmemelidir.

Bu son sınır, adres çubuğunda bir kimlik değiştirildiğinde sınanır. Rol denetimi geçilir, kapsam denetimi geçilir, kiracı denetimi geçilir — ve istek yine de reddedilmelidir, çünkü istenen nesne isteği yapana ait değildir.

Üç Gerçekleştirim, Aynı Uç Nokta

Sahiplik denetimi üç yerde yapılabilir: hiç yapılmaz, kayıt çekildikten sonra yapılır ya da sorgunun kendisine konur. Aşağıdaki hesap üçünü aynı istek kümesiyle çalıştırır.

cat > nesne.mjs <<'EOF'
// nesne.mjs — nesne duzeyi yetki denetimi: sahiplik sorgunun icinde mi disinda mi
import { DatabaseSync } from "node:sqlite";
import { randomUUID } from "node:crypto";

const db = new DatabaseSync(":memory:");
db.exec(`
  CREATE TABLE odunc (odunc_id TEXT PRIMARY KEY, uye_id TEXT NOT NULL, isbn TEXT NOT NULL,
                      durum TEXT NOT NULL, not_metni TEXT);
  INSERT INTO odunc VALUES
    ('O-1','U-1001','978-0201896831','ACIK','Kapak zedeli teslim alindi'),
    ('O-2','U-1001','978-0262033848','KAPALI',NULL),
    ('O-3','U-1002','978-0131103627','ACIK','Ikinci uyari gonderildi');
`);

// Uc gerceklestirim. Ucu de ayni istegi karsilar: GET /oduncler/{id}
const uclar = {
  denetimsiz: (ctx, id) => db.prepare("SELECT * FROM odunc WHERE odunc_id = ?").all(id),
  cekSonraDenetle: (ctx, id) => {
    const satir = db.prepare("SELECT * FROM odunc WHERE odunc_id = ?").all(id)[0];
    if (!satir) return { durum: 404, govde: null };
    if (satir.uye_id !== ctx.uye) return { durum: 403, govde: { hata: "bu kayit size ait degil" } };
    return { durum: 200, govde: satir };
  },
  sorguDaDenetle: (ctx, id) => {
    const satir = db.prepare("SELECT * FROM odunc WHERE odunc_id = ? AND uye_id = ?").all(id, ctx.uye)[0];
    return satir ? { durum: 200, govde: satir } : { durum: 404, govde: null };
  },
};

const ctx = { uye: "U-1001" };          // istegi yapan uye
const dolgu = (s, n) => String(s).padEnd(n);

console.log("gerceklestirim    istenen  sonuc                       baskasinin kaydi gorundu mu");
for (const id of ["O-1", "O-3", "O-9"]) {
  const ham = uclar.denetimsiz(ctx, id);
  const sahip = ham[0] && ham[0].uye_id !== ctx.uye;
  console.log(`${dolgu("denetimsiz", 17)} ${dolgu(id, 8)} ${dolgu(ham.length ? "200 kayit dondu" : "bos", 27)} ${sahip ? "EVET" : "hayir"}`);
}
for (const [ad, f] of [["cek-sonra-denetle", uclar.cekSonraDenetle], ["sorguda-denetle", uclar.sorguDaDenetle]]) {
  for (const id of ["O-1", "O-3", "O-9"]) {
    const y = f(ctx, id);
    console.log(`${dolgu(ad, 17)} ${dolgu(id, 8)} ${dolgu(y.durum + (y.govde ? " kayit dondu" : " govdesiz"), 27)} hayir`);
  }
}

// Varlik sizintisi: iki gerceklestirimin yanit kodlari baskasinin kaydini ele veriyor mu?
console.log("\nyanit kodlarindan cikarilabilen bilgi");
for (const [ad, f] of [["cek-sonra-denetle", uclar.cekSonraDenetle], ["sorguda-denetle", uclar.sorguDaDenetle]]) {
  const varOlanBaskasinin = f(ctx, "O-3").durum;
  const hicYok = f(ctx, "O-9").durum;
  console.log(`  ${dolgu(ad, 18)} baskasinin kaydi: ${varOlanBaskasinin}   olmayan kayit: ${hicYok}` +
    (varOlanBaskasinin !== hicYok ? "   -> kaydin varligi sizdi" : "   -> ikisi ayirt edilemiyor"));
}

// Tahmin edilemez kimlik yetkilendirme degildir.
const rastgele = randomUUID();
console.log("\ntahmin edilemez kimlik denemesi");
console.log(`  uretilen kimlik: ${rastgele.length} karakter, sozlukten tahmin edilemez`);
console.log("  ama kimlik ele gecerse (kayit, gunluk, paylasilan baglanti) denetimsiz uc nokta yine kaydi doner.");
console.log(`  denetimsiz uc nokta O-3 icin: ${uclar.denetimsiz(ctx, "O-3").length} kayit`);

// Uc nokta taramasi: hangi uc noktalar sahiplik olcutunu bildiriyor?
const UC_NOKTALAR = [
  { yol: "GET /oduncler/{id}",            olcut: "uye_id = :ozne" },
  { yol: "DELETE /oduncler/{id}",         olcut: "uye_id = :ozne" },
  { yol: "GET /oduncler/{id}/not",        olcut: null },
  { yol: "GET /uyeler/{id}/cezalar",      olcut: "uye_id = :ozne" },
  { yol: "POST /oduncler/{id}/uzat",      olcut: null },
  { yol: "GET /raporlar/gecikme",         olcut: "rol = gorevli" },
];
console.log("\nuc nokta                       sahiplik olcutu     bulgu");
let eksik = 0;
for (const u of UC_NOKTALAR) {
  const sorunlu = u.olcut === null;
  if (sorunlu) eksik++;
  console.log(`${dolgu(u.yol, 30)} ${dolgu(u.olcut ?? "-", 19)} ${sorunlu ? "OLCUT BILDIRILMEMIS" : "-"}`);
}
console.log(`bildirilmemis uc nokta: ${eksik} / ${UC_NOKTALAR.length}`);
EOF
node nesne.mjs
gerceklestirim    istenen  sonuc                       baskasinin kaydi gorundu mu
denetimsiz        O-1      200 kayit dondu             hayir
denetimsiz        O-3      200 kayit dondu             EVET
denetimsiz        O-9      bos                         hayir
cek-sonra-denetle O-1      200 kayit dondu             hayir
cek-sonra-denetle O-3      403 kayit dondu             hayir
cek-sonra-denetle O-9      404 govdesiz                hayir
sorguda-denetle   O-1      200 kayit dondu             hayir
sorguda-denetle   O-3      404 govdesiz                hayir
sorguda-denetle   O-9      404 govdesiz                hayir

yanit kodlarindan cikarilabilen bilgi
  cek-sonra-denetle  baskasinin kaydi: 403   olmayan kayit: 404   -> kaydin varligi sizdi
  sorguda-denetle    baskasinin kaydi: 404   olmayan kayit: 404   -> ikisi ayirt edilemiyor

tahmin edilemez kimlik denemesi
  uretilen kimlik: 36 karakter, sozlukten tahmin edilemez
  ama kimlik ele gecerse (kayit, gunluk, paylasilan baglanti) denetimsiz uc nokta yine kaydi doner.
  denetimsiz uc nokta O-3 icin: 1 kayit

uc nokta                       sahiplik olcutu     bulgu
GET /oduncler/{id}             uye_id = :ozne      -
DELETE /oduncler/{id}          uye_id = :ozne      -
GET /oduncler/{id}/not         -                   OLCUT BILDIRILMEMIS
GET /uyeler/{id}/cezalar       uye_id = :ozne      -
POST /oduncler/{id}/uzat       -                   OLCUT BILDIRILMEMIS
GET /raporlar/gecikme          rol = gorevli       -
bildirilmemis uc nokta: 2 / 6

Denetimsiz Uç Noktanın Gözlenebilir Sonucu

İlk bölümdeki tabloda denetimsiz gerçekleştirim, başkasına ait O-3 kaydını olduğu gibi döndürüyor. İstek geçerli bir belirteçle geldi, üye kendi hesabındaydı, yalnız yol parametresi değişti. Sızan şey bir ödünç kaydı; içinde başka bir üyenin okuma geçmişi ve kaydın not alanı var.

Bu kusur sınıfı, uygulamanın hiçbir katmanında hata üretmediği için sessizdir: günlükte başarılı bir istek görünür, izleme panosunda hata oranı değişmez. Ancak yakalanması da kolaydır — her uç nokta için “bu kaydın isteği yapanla ilişkisi nedir” sorusunun yazılı bir yanıtı varsa, yanıtı olmayan uç noktalar listelenebilir.

Denetimin Yeri Yanıtı Değiştirir

İkinci bölüm iki doğru gerçekleştirimi ayırıyor. Kayıt çekildikten sonra denetleyen sürüm, başkasının kaydı için 403, olmayan kayıt için 404 döndürüyor. İkisi farklı olduğu için bir gözlemci, denemediği bir kimliğin var olduğunu öğrenebilir: 403 “bu kayıt var ama senin değil” demektir.

Sorgunun içinde denetleyen sürümde iki durum aynı yanıtı üretir. Kayıt ya isteği yapanın kümesindedir ya da yoktur; ikisi arasında ayrım yapılmaz. Bu, varlık bilgisinin sızmamasını sağlar ve ek bir yararı vardır: denetim unutulamaz, çünkü ölçüt sorgunun parçasıdır. Çek-sonra-denetle kalıbında if satırını silmek yeterlidir; sorguda denetleme kalıbında aynı hatayı yapmak için sorguyu yeniden yazmak gerekir.

Seçim her zaman 404 yönünde değildir. Kullanıcının erişimi olmadığını bilmesi gereken durumlarda — bir ekip üyesinin başka bir ekibin panosuna gitmesi gibi — 403 daha yardımcıdır ve varlık bilgisi zaten paylaşılan bir bilgidir. Ölçüt şudur: kaydın varlığı isteği yapan için gizli bir bilgi mi?

Tahmin Edilemez Kimlik Bir Yetki Denetimi Değildir

Üçüncü bölüm sık rastlanan bir yanılgıyı ölçüyor. Sıralı sayı yerine tahmin edilemez bir kimlik kullanmak, kimlikleri keşfetmeyi zorlaştırır; kaydı korumaz. Kimlik bir kayıt bağlantısında, tarayıcı geçmişinde, sunucu günlüğünde ya da paylaşılan bir bağlantıda ele geçtiğinde denetimsiz uç nokta kaydı yine döndürür — çıktının son satırı bunu gösteriyor.

Tahmin edilemez kimliğin yeri savunmanın derinliğidir, yerine geçmesi değil. Kimlik tasarımı toplu keşfi engeller; yetki denetimi tekil erişimi engeller. İkisi farklı işler yapar ve biri diğerini gereksizleştirmez.

Ölçütü Yazılı Hâle Getirmek

Son bölüm uç noktaları tarıyor: her uç nokta için sahiplik ölçütü bildirilmiş mi? Altı uç noktanın ikisi ölçüt bildirmiyor ve ikisi de alt kaynak: bir ödünç kaydının notu ve uzatma eylemi. Alt kaynaklar bu kusurun en sık göründüğü yerdir, çünkü üst kaynak için yazılmış denetimin alt yola da uygulandığı varsayılır.

Ölçütün yazılı olması iki şey sağlar. Yeni bir uç nokta eklendiğinde ölçüt alanı boş kalırsa tarama bunu bulur — kod incelemesinde gözden kaçan şey listede görünür. İkincisi, ölçüt bir veri olduğu için sınanabilir: her uç nokta için “sahibi olmayan bir özneyle çağır ve boş sonuç bekle” testi kendiliğinden üretilebilir.

Bu kursta kurulan bütün modeller — rol, öznitelik, ilişki, politika, kapsam, kiracı — kararı nesneden bağımsız verir. Nesne düzeyi denetim, o kararın son adımıdır ve atlandığında diğerlerinin hepsi geçilmiş olur. Bu yüzden yetki denetimi tek bir yerde değil, isteğin yolu boyunca katman katman yapılır.

Özet

  • Nesne düzeyi denetim, rol, kapsam ve kiracı denetimlerinin hepsi geçildikten sonra kalan son sınırdır: istenen kaydın isteği yapanla ilişkisi.
  • Denetimsiz uç nokta hata üretmeden başkasının kaydını döndürür; modelde tek bir yol parametresi değişikliği yeterli oldu.
  • Sahiplik ölçütü sorgunun içine konduğunda hem unutulması zorlaşır hem de varlık bilgisi sızmaz; çek-sonra-denetle kalıbında 403 ile 404 ayrımı kaydın varlığını ele verir.
  • Tahmin edilemez kimlik keşfi zorlaştırır, erişimi yetkilendirmez; kimlik ele geçtiğinde denetimsiz uç nokta kaydı yine döndürür.
  • Her uç nokta için sahiplik ölçütünün yazılı olması taranabilir ve sınanabilir kılar; modelde ölçütü bildirilmemiş iki uç noktanın ikisi de alt kaynaktı.

Kurs Kapanışı

Bu kurs iki soruyu birbirinden ayırarak başladı: kim olduğun ve ne yapabildiğin. Kimlik Doğrulama Akışları konusu birinci soruyu ele aldı; temel şemadan oturum ve belirteç tabanlı akışlara, imza doğrulamanın ayrıntısına, yenileme belirteçlerinin dönüşümlü kullanımına, yetki devri protokollerine ve çok etmenli doğrulamaya kadar her akışın hangi sorunu çözdüğünü ve nerede kırıldığını gösterdi. Kimlik Bilgisi Güvenliği konusu kimlik bilgisinin saklanmasını ölçülebilir kurallara bağladı: yavaş karma parametreleri, tuzun ve biberin ayrı işleri, sıfırlama akışının yüzeyi ve oturumun ömrü. Yetkilendirme Modelleri konusu ikinci soruyu dört modelde kurdu, kapsamla istemci yetkisini daralttı, kiracı sınırını sorguya taşıdı ve bu dersle nesne düzeyine indi.

Kurs boyunca değişmeyen üç ölçüt vardı. Karar bir yerde verilir, başka bir yerde uygulanır; ikisinin ayrılması denetimi mümkün kılar. Reddin nedeni ayırt edilebilir olmalıdır, ama ayrıntısı isteği yapana sızmamalıdır. Ve her denetim, unutulduğunda gözlenebilir bir sonuç üretmelidir — sızan kayıt sayısı, açılan uç nokta sayısı, ayırt edilebilen yanıt kodu.

Sıradaki kurs, Veri Erişim Katmanı ve İş Mantığı, bu kararların altındaki katmana iner. Yetki denetiminin sorguya taşınması bu kursta bir güvenlik gereği olarak çıktı; orada aynı sorgu bir başarım ve tasarım sorusu hâline gelir: veri erişimi iş mantığından nasıl ayrılır, işlem sınırı nereye çizilir ve aynı veriye giden yolların maliyeti nasıl ölçülür?

İ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