Ders 15 / 25
Satır Düzeyi Güvenlik
Aynı tabloda hesaba göre farklı satır kümesi göstermek: politika ifadesi, oturum değişkeni, politikanın sızdırdığı satırların sayılması, yazma tarafındaki denetim ve politikanın erişim yoluyla etkileşimi.
İçindekiler
Önceki ders izinleri nesne düzeyinde kurdu: bir hesap ödünç tablosunu ya okuyabiliyordu ya okuyamıyordu. Kütüphanenin gerçek gereksinimi bu ikilikle karşılanmaz. Sahil şubesinin görevlisi ödünç kayıtlarını okuyabilmelidir, ama yalnız kendi şubesinin satırlarını. Merkez şubesinin satırlarını görmesi bir gereksinim değil, bir sızıntıdır.
Bu, nesne düzeyi izinle ifade edilemeyen bir kısıttır. Çözüm, tabloya erişimi tümden açmak ya da kapatmak yerine, tabloya bir politika (policy) iliştirmektir: satırın görünür olması için sağlaması gereken koşul. Motor, o tabloya giden her sorguya bu koşulu kendiliğinden ekler. Buna satır düzeyi güvenlik (row-level security) denir.
Politika İfadesi ve Oturum Değişkeni
Politika bir yüklemdir — satırın sütunlarını ve oturuma ait bir bilgiyi kullanan bir koşul. Oturuma ait bilgi, bağlantı açılırken belirlenen ve sorgu içinden okunabilen bir değerdir: hangi hesap bağlandı, o hesap hangi şubeye ait. Buna oturum değişkeni (session variable) denir.
Kütüphane örneğindeki politika şudur: sube_id, oturumun şube numarasına eşit olmalı.
Sorgu WHERE yan tümcesine hiçbir şey yazmasa bile motor bu koşulu ekler; sorgu kendi
koşulunu yazmışsa iki koşul birlikte uygulanır.
Aşağıdaki örnek politikayı bir görünüm üzerinden modelliyor. Gerçek satır düzeyi güvenlik politikayı taban tabloya iliştirir ve görünüm gerektirmez; burada modellenen şey politika ifadesinin satır kümesini nasıl daralttığıdır, bu daraltma her iki durumda da aynıdır.
rm -f kutuphane.db sqlite3 -box -header kutuphane.db <<'SQL' CREATE TABLE odunc( id INTEGER PRIMARY KEY, kitap_id INT NOT NULL, uye_id INT NOT NULL, sube_id INT NOT NULL, alis TEXT NOT NULL, iade TEXT); CREATE TABLE oturum(anahtar TEXT PRIMARY KEY, deger TEXT NOT NULL); INSERT INTO odunc(kitap_id,uye_id,sube_id,alis,iade) VALUES (11,101,1,'2024-03-01','2024-03-14'),(12,102,1,'2024-03-03',NULL), (13,103,1,'2024-03-05',NULL),(14,104,2,'2024-03-02','2024-03-16'), (15,105,2,'2024-03-06',NULL),(16,106,2,'2024-03-08',NULL), (17,107,2,'2024-03-09','2024-03-20'),(18,108,3,'2024-03-04','2024-03-18'), (19,109,3,'2024-03-11',NULL); INSERT INTO oturum VALUES ('sube_id','2'); CREATE VIEW odunc_gorunur AS SELECT * FROM odunc WHERE sube_id = (SELECT CAST(deger AS INTEGER) FROM oturum WHERE anahtar='sube_id'); SELECT 'taban tablo' AS kaynak, COUNT(*) AS satir FROM odunc UNION ALL SELECT 'politika sonrasi (sube 2)', COUNT(*) FROM odunc_gorunur; UPDATE oturum SET deger='1' WHERE anahtar='sube_id'; SELECT 'politika sonrasi (sube 1)' AS kaynak, COUNT(*) AS satir FROM odunc_gorunur; SQL rm -f kutuphane.db
┌───────────────────────────┬───────┐ │ kaynak │ satir │ ├───────────────────────────┼───────┤ │ taban tablo │ 9 │ │ politika sonrasi (sube 2) │ 4 │ └───────────────────────────┴───────┘ ┌───────────────────────────┬───────┐ │ kaynak │ satir │ ├───────────────────────────┼───────┤ │ politika sonrasi (sube 1) │ 3 │ └───────────────────────────┴───────┘
Sorgu metni değişmedi; yalnız oturum değeri değişti ve sonuç kümesi 4 satırdan 3 satıra
düştü. Politikanın anlamı budur: aynı sorgu, kim çalıştırdığına göre farklı satır kümesi
döndürür. Uygulama kodunda WHERE sube_id = ? yazmayı unutmak artık bir sızıntı
üretmez, çünkü koşulu uygulama değil motor koyar.
Politikanın Sızdırdığı Satırların Sayılması
Politika bir kez yazılır ve yıllarca durur. Yazıldığı gün doğru olan bir koşul, sonradan
eklenen bir “kolaylık” ile genişleyebilir. Sık görülen genişleme şudur: iade edilmiş
kayıtların “artık gizli sayılmaması” gerektiği kararı verilir ve politikaya bir OR
eklenir.
Bu tür bir değişikliğin etkisi tahmin edilmez, sayılır. Amaçlanan satır kümesi ile yürürlükteki politikanın döndürdüğü küme arasındaki fark, politikanın sızdırdığı satırlardır.
rm -f politika.db sqlite3 -box -header politika.db <<'SQL' CREATE TABLE odunc( id INTEGER PRIMARY KEY, kitap_id INT NOT NULL, uye_id INT NOT NULL, sube_id INT NOT NULL, alis TEXT NOT NULL, iade TEXT); INSERT INTO odunc(kitap_id,uye_id,sube_id,alis,iade) VALUES (11,101,1,'2024-03-01','2024-03-14'),(12,102,1,'2024-03-03',NULL), (13,103,1,'2024-03-05',NULL),(14,104,2,'2024-03-02','2024-03-16'), (15,105,2,'2024-03-06',NULL),(16,106,2,'2024-03-08',NULL), (17,107,2,'2024-03-09','2024-03-20'),(18,108,3,'2024-03-04','2024-03-18'), (19,109,3,'2024-03-11',NULL); WITH amaclanan AS (SELECT id FROM odunc WHERE sube_id = 2), genis AS (SELECT id FROM odunc WHERE sube_id = 2 OR iade IS NOT NULL) SELECT (SELECT COUNT(*) FROM odunc) AS taban, (SELECT COUNT(*) FROM amaclanan) AS amaclanan, (SELECT COUNT(*) FROM genis) AS genis_politika, (SELECT COUNT(*) FROM genis WHERE id NOT IN (SELECT id FROM amaclanan)) AS sizan; SELECT id, uye_id, sube_id, iade FROM odunc WHERE (sube_id = 2 OR iade IS NOT NULL) AND sube_id <> 2 ORDER BY id; SQL rm -f politika.db
┌───────┬───────────┬────────────────┬───────┐ │ taban │ amaclanan │ genis_politika │ sizan │ ├───────┼───────────┼────────────────┼───────┤ │ 9 │ 4 │ 6 │ 2 │ └───────┴───────────┴────────────────┴───────┘ ┌────┬────────┬─────────┬────────────┐ │ id │ uye_id │ sube_id │ iade │ ├────┼────────┼─────────┼────────────┤ │ 1 │ 101 │ 1 │ 2024-03-14 │ │ 8 │ 108 │ 3 │ 2024-03-18 │ └────┴────────┴─────────┴────────────┘
Sızan satır sayısı iki, ve hangileri olduğu listelendi: Merkez ve Tepe şubelerinin iade edilmiş kayıtları, üye numaralarıyla birlikte Sahil görevlisine açılmış oldu. Dokuz satırlık bir tabloda bu fark gözle görülür; on milyon satırlık bir tabloda yalnız bu sorguyla görülür.
Bu sorgu bir denetim aracıdır ve politikanın her değişikliğinde çalıştırılır. Yazılışı şablon hâline getirilebilir: amaçlanan koşulu bir ortak tablo ifadesine, yürürlükteki politikayı bir başkasına koy, farkı say. Sıfırdan büyük her sonuç, politikanın metnine bakmayı gerektirir.
Yazma Tarafındaki Denetim
Politika okumayı daraltır. Yazmayı kendiliğinden daraltmaz. Bu ayrım, satır düzeyi güvenliğin en sık atlanan yanıdır: Sahil görevlisi, Merkez şubesine ait bir ödünç kaydı yazabilir ve yazdığı satırı sonradan göremez.
Aşağıdaki örnek bu durumu üretiyor. Görünüm üzerinden yapılan ekleme taban tabloya gidiyor, ama eklenen satır politikanın dışında kaldığı için görünümde yok:
rm -f yazma.db sqlite3 -box -header yazma.db <<'SQL' CREATE TABLE odunc(id INTEGER PRIMARY KEY, kitap_id INT NOT NULL, uye_id INT NOT NULL, sube_id INT NOT NULL, alis TEXT NOT NULL, iade TEXT); CREATE TABLE oturum(anahtar TEXT PRIMARY KEY, deger TEXT NOT NULL); INSERT INTO oturum VALUES ('sube_id','2'); INSERT INTO odunc(kitap_id,uye_id,sube_id,alis,iade) VALUES (15,105,2,'2024-03-06',NULL),(16,106,2,'2024-03-08',NULL); CREATE VIEW odunc_gorunur AS SELECT * FROM odunc WHERE sube_id = (SELECT CAST(deger AS INTEGER) FROM oturum WHERE anahtar='sube_id'); CREATE TRIGGER odunc_gorunur_ekle INSTEAD OF INSERT ON odunc_gorunur BEGIN INSERT INTO odunc(kitap_id,uye_id,sube_id,alis,iade) VALUES (NEW.kitap_id, NEW.uye_id, NEW.sube_id, NEW.alis, NEW.iade); END; INSERT INTO odunc_gorunur(kitap_id,uye_id,sube_id,alis,iade) VALUES (20,110,3,'2024-03-12',NULL); SELECT 'gorunumden' AS kaynak, COUNT(*) AS satir FROM odunc_gorunur UNION ALL SELECT 'taban tablo', COUNT(*) FROM odunc; SQL rm -f yazma.db
┌─────────────┬───────┐ │ kaynak │ satir │ ├─────────────┼───────┤ │ gorunumden │ 2 │ │ taban tablo │ 3 │ └─────────────┴───────┘
Taban tabloda üç satır var, görünümde iki. Üçüncü satır yazıldı ve kayboldu. Bu, veri bütünlüğü açısından okumadan sızıntı yapmaktan daha kötüdür: kimsenin göremediği ama var olan kayıtlar birikir, sayımlar tutmaz ve neden bulunamaz.
Çözüm, aynı koşulun yazma yolunda da denetlenmesidir. Aşağıdaki tetikleyici, politikaya uymayan satırı yazmadan reddediyor:
rm -f yazma2.db sqlite3 yazma2.db <<'SQL' CREATE TABLE odunc(id INTEGER PRIMARY KEY, kitap_id INT NOT NULL, uye_id INT NOT NULL, sube_id INT NOT NULL, alis TEXT NOT NULL, iade TEXT); CREATE TABLE oturum(anahtar TEXT PRIMARY KEY, deger TEXT NOT NULL); INSERT INTO oturum VALUES ('sube_id','2'); CREATE VIEW odunc_gorunur AS SELECT * FROM odunc WHERE sube_id = (SELECT CAST(deger AS INTEGER) FROM oturum WHERE anahtar='sube_id'); CREATE TRIGGER odunc_gorunur_ekle INSTEAD OF INSERT ON odunc_gorunur BEGIN SELECT CASE WHEN NEW.sube_id <> (SELECT CAST(deger AS INTEGER) FROM oturum WHERE anahtar='sube_id') THEN RAISE(ABORT, 'politika disi satir yazilamaz') END; INSERT INTO odunc(kitap_id,uye_id,sube_id,alis,iade) VALUES (NEW.kitap_id, NEW.uye_id, NEW.sube_id, NEW.alis, NEW.iade); END; INSERT INTO odunc_gorunur(kitap_id,uye_id,sube_id,alis,iade) VALUES (20,110,3,'2024-03-12',NULL); SQL echo "cikis kodu: $?" rm -f yazma2.db
Runtime error near line 19: politika disi satir yazilamaz (19) cikis kodu: 1
Tetikleyici sözdizimi ve hata iletisinin biçimi motora göre değişir; satır düzeyi güvenliği yerleşik olarak destekleyen motorlarda aynı denetim politikanın ayrı bir yanında tanımlanır. Değişmeyen kural şudur: her politikanın okuma tarafı ile yazma tarafı ayrı ayrı yazılır ve ikisinin uyumu ayrıca sınanır.
Politika ve Erişim Yolu
Politika yüklemi, sorgunun kendi koşuluna eklenen bir koşuldur; planlayıcı ikisini birlikte görür. Bunun iki sonucu vardır.
Birincisi olumludur: politika sütunu üzerinde uygun bir dizin varsa, politika sorguyu yavaşlatmaz — aksine seçiciliği artırdığı için hızlandırabilir. Önceki konuda kurulan dizin ve bölümleme kararları burada işe yarar; şube numarasına göre bölümlenmiş bir tablo, şube politikasıyla doğal biçimde uyuşur.
İkincisi dikkat gerektirir: politika yüklemi ile sorgunun kendi yüklemi arasındaki değerlendirme sırası her zaman tanımlı değildir. Politika dışındaki bir satırın varlığı, o satır üzerinde çalışan bir işlevin hata vermesi ya da yavaşlaması yoluyla dolaylı olarak sezilebilir. Bu tür çıkarımlara karşı korunan motorlar politika yüklemini önce değerlendirmeyi güvence altına alır. İşletim tarafındaki sorumluluk, politika taşıyan tablolarda kullanıcı tanımlı işlevlerin yüklem olarak kullanılmasına izin vermemektir.
Politikanın Kapsamadığı Yollar
Satır düzeyi güvenlik yalnız uygulandığı tablo üzerinden geçen erişimi denetler. Denetim listesi şu soruları içerir.
Tablo sahibi ve geniş yetkili hesaplar. Motorlar genellikle tablo sahibini ve yönetici hesabını politikadan muaf tutar. Uygulama hesabının tablo sahibi olmaması bu yüzden önemlidir.
Politikasız kopyalar. Aynı verinin bir rapor tablosuna, bir maddeleştirilmiş görünüme ya da bir dışa aktarım dosyasına kopyalanması, politikayı arkada bırakır. Kopyanın kendisine de politika gerekir; gerekmiyorsa kopya politikanın kapsadığı alanı daraltacak biçimde üretilir.
Toplama işlevleriyle çıkarım. Politika satırları gizler, sayıları gizlemeyebilir. Bir hesabın göremediği satırlar üzerinde toplam ya da sayım alabildiği bir yol varsa, satır içeriği olmasa da bilgi sızar. Toplama sorgularının politikadan sonra çalıştığını doğrulamak, denetimin bir parçasıdır.
Yedekler. Bu ders boyunca kurulan her koruma, çalışan sisteme aittir. Yedek dosyası politikasız bir kopyadır ve ayrı korunur — bu, sonraki dersin konusudur.
Özet
- Satır düzeyi güvenlik, tabloya iliştirilen bir politika yüklemiyle aynı sorgunun kim çalıştırdığına göre farklı satır kümesi döndürmesini sağlar.
- Politika, oturum değişkenini kullanır; koşulu uygulama kodu değil motor ekler.
- Politika değişikliğinin etkisi tahmin edilmez, sayılır: amaçlanan küme ile yürürlükteki kümenin farkı sızan satırları verir.
- Okuma politikası yazmayı daraltmaz; yazma tarafı ayrıca denetlenmezse görünmeyen satırlar birikir.
- Politika yüklemi sorgu planına girer; politika sütununa uygun dizin ve bölümleme seçimi başarımı korur.
- Tablo sahibi, politikasız kopyalar, toplama üzerinden çıkarım ve yedek dosyaları politikanın kapsamadığı yollardır.
Sonraki Adım
Erişim denetimi verinin yanlış kişiye gitmesini engeller; verinin yok olmasını
engellemez. Bir disk arızası, yanlış yazılmış bir DELETE ya da hatalı bir şema
değişikliği, en özenli yetki çizelgesine sahip veritabanını da kullanılamaz hâle getirir.
Sonraki ders yedeklemeye geçiyor: mantıksal, fiziksel ve artımlı yedeklerin ne olduğunu,
hangi maliyetle alındıklarını ve bir yedeğin geri yüklendiğinde kaynakla eşit olduğunun
nasıl kanıtlanacağını ele alıyor.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.