---
title: 'Çok Kiracılı Yetkilendirme'
source: 'https://academia.sh/tr/kurslar/kimlik-ve-yetki/cok-kiracili-yetkilendirme'
course: 'Kimlik Doğrulama ve Yetkilendirme'
language: tr
updated: '2026-08-17T18:06:48+00:00'
license: 'CC BY-SA 4.0'
---

# Çok Kiracılı Yetkilendirme

Kiracı sınırının sorguya taşınması, süzgeç unutulduğunda oluşan sızıntının ölçülmesi, kiracı kimliğinin kimliği doğrulanmış bağlamdan alınması, üç katmanlı savunma ve fiziksel yerleşim seçenekleri.

Önceki ders kapsamı bir istemcinin yetkisini daraltmak için kullandı. Bütün örneklerde
arka planda tek bir kütüphane vardı: bir üye kümesi, bir görevli kadrosu, bir kayıt havuzu.
Aynı sunucu birden çok kuruma hizmet verdiğinde araya yeni bir sınır girer.

Bu sınırın önceki modellerden farkı, **veriyle** çizilmesidir. Kuzey Kütüphanesi'nin
görevlisi kendi kurumunun bütün kayıtlarını görebilir; Güney Kütüphanesi'nin aynı roldeki
görevlisi de kendi kurumunun kayıtlarını görebilir. İkisinin rolü, izinleri ve belirteç
kapsamları aynıdır. Ayıran tek şey, sorgunun hangi satırlara dokunduğudur.

## Yalıtım Rolle Değil Sorguyla Kurulur

Çok kiracılı bir sistemde her kayıt bir **kiracıya (tenant)** aittir. Kiracı, verinin
sahibi olan kurumdur ve şemada bir sütun olarak durur. Yetki denetimi bu sütuna
bakmadığında rol denetimi geçen bir istek, komşu kurumun kaydını döndürür.

Aşağıdaki hesap iki depo gerçekleştirimini aynı veri üzerinde çalıştırır: biri kiracı
süzgecini unutmuş, diğeri her sorguda zorunlu tutuyor.

```bash
cat > kiraci.mjs <<'EOF'
// kiraci.mjs — kiraci yalitiminin sorguya tasinmasi ve denetlenmesi
import { DatabaseSync } from "node:sqlite";

const db = new DatabaseSync(":memory:");
db.exec(`
  CREATE TABLE uye (uye_id TEXT PRIMARY KEY, kiraci TEXT NOT NULL, ad TEXT NOT NULL);
  CREATE TABLE odunc (odunc_id TEXT PRIMARY KEY, kiraci TEXT NOT NULL, uye_id TEXT NOT NULL,
                      isbn TEXT NOT NULL, durum TEXT NOT NULL);
  INSERT INTO uye VALUES ('U-1','kuzey','Ayse Yilmaz'), ('U-2','kuzey','Deniz Kaya'),
                         ('U-9','guney','Selin Aydin');
  INSERT INTO odunc VALUES ('O-1','kuzey','U-1','978-0201896831','ACIK'),
                           ('O-2','kuzey','U-2','978-0262033848','KAPALI'),
                           ('O-7','guney','U-9','978-0131103627','ACIK'),
                           ('O-8','guney','U-9','978-0201896831','ACIK');
`);

// 1. Kiraci suzgeci unutulmus depo: yalnizca durum kosulu var.
const gevsek = {
  ad: "kiraci suzgeci yok",
  acikOduncler: () => db.prepare("SELECT odunc_id, kiraci FROM odunc WHERE durum = 'ACIK'").all(),
  uyeBul: (id) => db.prepare("SELECT uye_id, kiraci, ad FROM uye WHERE uye_id = ?").all(id),
};

// 2. Kiraci suzgeci zorunlu depo: her sorgu baglamdaki kiraciyi alir.
const siki = {
  ad: "kiraci suzgeci zorunlu",
  acikOduncler: (k) => db.prepare("SELECT odunc_id, kiraci FROM odunc WHERE kiraci = ? AND durum = 'ACIK'").all(k),
  uyeBul: (k, id) => db.prepare("SELECT uye_id, kiraci, ad FROM uye WHERE kiraci = ? AND uye_id = ?").all(k, id),
};

const KIRACI = "kuzey";                       // istegi yapan gorevlinin kiracisi
const sizan = (satirlar) => satirlar.filter((s) => s.kiraci !== KIRACI).length;

console.log("depo                    sorgu             donen  sizan");
for (const [depo, satirlar] of [
  [gevsek, gevsek.acikOduncler()],
  [siki, siki.acikOduncler(KIRACI)],
]) {
  console.log(`${depo.ad.padEnd(23)} ${"acikOduncler".padEnd(17)} ${String(satirlar.length).padStart(5)} ${String(sizan(satirlar)).padStart(6)}`);
}
for (const [depo, satirlar] of [
  [gevsek, gevsek.uyeBul("U-9")],
  [siki, siki.uyeBul(KIRACI, "U-9")],
]) {
  console.log(`${depo.ad.padEnd(23)} ${"uyeBul('U-9')".padEnd(17)} ${String(satirlar.length).padStart(5)} ${String(sizan(satirlar)).padStart(6)}`);
}

// 3. Kiracinin kaynagi: istekten mi, belirtecten mi?
function baglamKur(kaynak, belirtec, istek) {
  return kaynak === "istek" ? istek.kiraci : belirtec.kiraci;
}
const belirtec = { ozne: "P-2001", kiraci: "kuzey" };
console.log("\nkiraci kaynagi   istekteki deger  kullanilan  donen kayit  sizan");
for (const kaynak of ["istek", "belirtec"]) {
  const istek = { kiraci: "guney", uye_id: "U-9" };       // istemci baska kiraci yaziyor
  const k = baglamKur(kaynak, belirtec, istek);
  const satirlar = siki.uyeBul(k, istek.uye_id);
  console.log(`${kaynak.padEnd(16)} ${istek.kiraci.padEnd(16)} ${k.padEnd(11)} ${String(satirlar.length).padStart(11)} ${String(sizan(satirlar)).padStart(6)}`);
}

// 4. Statik denetim: kiraci sutunu tasiyan tablolara giden sorgularda suzgec var mi?
const KIRACILI_TABLOLAR = ["uye", "odunc"];
const SORGULAR = [
  { ad: "acikOduncler/gevsek", sql: "SELECT odunc_id FROM odunc WHERE durum = 'ACIK'" },
  { ad: "acikOduncler/siki",   sql: "SELECT odunc_id FROM odunc WHERE kiraci = ? AND durum = 'ACIK'" },
  { ad: "uyeBul/siki",         sql: "SELECT ad FROM uye WHERE kiraci = ? AND uye_id = ?" },
  { ad: "sayim",               sql: "SELECT count(*) FROM odunc" },
  { ad: "kitap listesi",       sql: "SELECT isbn FROM kitap" },
];
console.log("\nsorgu                  tablo   kiracili  suzgec  bulgu");
let bulgu = 0;
for (const s of SORGULAR) {
  const tablo = /FROM\s+(\w+)/i.exec(s.sql)?.[1] ?? "-";
  const kiracili = KIRACILI_TABLOLAR.includes(tablo);
  const suzgec = /\bkiraci\s*=\s*\?/i.test(s.sql);
  const sorunlu = kiracili && !suzgec;
  if (sorunlu) bulgu++;
  console.log(`${s.ad.padEnd(22)} ${tablo.padEnd(7)} ${(kiracili ? "evet" : "hayir").padEnd(9)} ${(suzgec ? "var" : "yok").padEnd(7)} ${sorunlu ? "EKSIK SUZGEC" : "-"}`);
}
console.log(`denetim bulgusu: ${bulgu} / ${SORGULAR.length}`);
EOF
node kiraci.mjs
```

```text
depo                    sorgu             donen  sizan
kiraci suzgeci yok      acikOduncler          3      2
kiraci suzgeci zorunlu  acikOduncler          1      0
kiraci suzgeci yok      uyeBul('U-9')         1      1
kiraci suzgeci zorunlu  uyeBul('U-9')         0      0

kiraci kaynagi   istekteki deger  kullanilan  donen kayit  sizan
istek            guney            guney                 1      1
belirtec         guney            kuzey                 0      0

sorgu                  tablo   kiracili  suzgec  bulgu
acikOduncler/gevsek    odunc   evet      yok     EKSIK SUZGEC
acikOduncler/siki      odunc   evet      var     -
uyeBul/siki            uye     evet      var     -
sayim                  odunc   evet      yok     EKSIK SUZGEC
kitap listesi          kitap   hayir     yok     -
denetim bulgusu: 2 / 5
```

## Unutulan Süzgecin Gözlenebilir Sonucu

Çıktının ilk bölümü sızıntıyı sayıyor. Süzgeçsiz depo, açık ödünçler sorgusunda üç kayıt
döndürüyor ve bunların ikisi başka kiracıya ait. Üye arama sorgusu daha çarpıcıdır:
istenen kimlik komşu kurumun üyesine aittir ve süzgeçsiz sorgu o kaydı döndürür — rol
denetimi geçilmiştir, çünkü görevlinin "üye okuma" izni vardır. İzin doğrudur; yanlış olan,
iznin **hangi satırlar üzerinde** geçerli olduğunun sorulmamasıdır.

Süzgeçli depoda aynı iki sorgu sıfır sızıntı veriyor. İkinci sorgunun sonucu boştur:
kayıt vardır ama bu kiracı için yoktur. Boş sonuç ile yetkisizlik yanıtı arasındaki seçim
bir tasarım kararıdır ve genellikle **boş sonuç** tercih edilir; "bu kimlik başka bir
kurumda var" bilgisi bile kiracılar arası bir sızıntıdır.

## Kiracı Bilgisi Nereden Gelir

İkinci bölüm daha ince bir hatayı gösteriyor. Kiracı kimliği isteğin gövdesinden ya da yol
parametresinden okunursa, istemci onu değiştirebilir: istek `guney` yazar, sorgu o kiracıyı
kullanır ve komşu kurumun kaydı döner. Aynı istek, kiracı kimliği **belirteçten** alındığında
sıfır kayıt döndürür.

Kural tek cümledir: kiracı kimliği, kimliği doğrulanmış bağlamdan gelir; istekten gelen
kiracı değeri en fazla bir **filtre** olabilir, hiçbir zaman yetkinin kaynağı olamaz.
İstemcinin gönderdiği kiracı ile bağlamdaki kiracı çelişiyorsa istek reddedilir; bu çelişki
aynı zamanda kaydedilmeye değer bir olaydır.

## Süzgeci Unutmayı Zorlaştırmak

Üçüncü bölüm bir denetim yazıyor: kiracı sütunu taşıyan tablolara giden sorgular süzgeci
içeriyor mu? Modeldeki beş sorgunun ikisi bulgu üretti — biri bilinçli olarak gevşek
bırakılan depo, diğeri masum görünen bir sayım sorgusu. Sayım sorgusu tek bir sayı döndürür
ama o sayı bütün kiracıların toplamıdır; gösterge panosunda bu, komşu kurumun iş hacmini
sızdırır.

Denetimin değeri, unutmayı **yapısal olarak** yakalamasıdır. Aynı işi üç katman birlikte
yapar ve her biri diğerinin kaçırdığını tutar:

1. **Depo katmanı**: kiracı parametresi olmayan bir sorgu yazılamaz — sorguyu kuran işlev
   kiracıyı zorunlu argüman olarak alır.
2. **Denetim betiği**: yeni yazılan sorgular sürekli tümleştirmede taranır, süzgeçsiz
   sorgu derlemeyi düşürür.
3. **Veritabanı katmanı**: satır düzeyi güvenlik politikası, uygulama katmanı hata yapsa
   bile satırları süzer. Bu, İlişkisel Veritabanı Yönetimi kursundaki Satır Düzeyi Güvenlik
   dersinin uygulama tarafındaki karşılığıdır.

Üçü birden gerekir. Yalnız depo katmanına güvenmek, ham sorgu yazan bir raporlama
betiğinde kırılır; yalnız veritabanı katmanına güvenmek, kiracı bağlamını oturuma
bağlamayan bir toplu işte kırılır.

## Kiracı Verisinin Fiziksel Yerleşimi

Yalıtımın üç yerleşim biçimi vardır ve seçim ölçütü aynı üç soruyla verilir: komşu
kiracının verisine yanlışlıkla erişme olasılığı, kiracı başına maliyet ve göç ile yedeklemenin
tanecikliği.

**Paylaşılan tablo** (bu dersteki model) en ucuzudur ve en çok özen ister: bütün kayıtlar
aynı tablodadır, yalıtımı sorgu sağlar. **Kiracı başına şema**, süzgeci bağlantı düzeyine
taşır; yanlış şemaya bağlanmak hâlâ olanaklıdır ama sorgu içindeki unutkanlık zararsızlaşır.
**Kiracı başına veritabanı** en güçlü yalıtımı verir; bedeli, göç ve yedekleme işlerinin
kiracı sayısıyla çarpılmasıdır.

Karar tek seferlik değildir: küçük kiracılar paylaşılan tabloda, sözleşmesi yalıtım
gerektiren büyük kiracılar ayrı veritabanında tutulabilir. Bu durumda uygulama katmanının
her iki yerleşimi de aynı arayüzün arkasında görmesi gerekir.

## Özet

- Çok kiracılı sistemde sınır rol ya da kapsamla değil veriyle çizilir; aynı role sahip iki
  görevli farklı satır kümelerini görür.
- Kiracı süzgeci unutulduğunda rol denetimi geçen istek komşu kurumun kaydını döndürür;
  modeldeki iki sorgu üç kayıttan ikisini sızdırdı, süzgeçli sürüm sıfır sızdırdı.
- Kiracı kimliği kimliği doğrulanmış bağlamdan gelir; istekten gelen değer yetkinin kaynağı
  olamaz — modelde istekten okunan kiracı komşu kurumun kaydını açtı.
- Süzgeci unutmak üç katmanla zorlaştırılır: kiracıyı zorunlu argüman yapan depo, sorguları
  tarayan denetim ve satır düzeyi güvenlik politikası.
- Fiziksel yerleşim (paylaşılan tablo, kiracı başına şema, kiracı başına veritabanı) yalıtım
  gücü ile kiracı başına maliyet arasındaki ödünleşimdir.

## Sonraki Adım

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, birbirinin ödünç kaydını görmemelidir.
Kursun son dersi bu son sınırı ele alır — adres çubuğuna başka bir kimlik yazıldığında ne
olduğunu ve nesne sahipliğinin nerede denetlenmesi gerektiğini.
