Çok Kiracılı SaaS’ta RLS Tek Başına Yetmez (UI Gate’i de Yetmez)
Çok kiracılı bir SaaS’ta veri izolasyonu tek katmanla sağlanmaz. RLS, sorgu filtresi, UI gate ve SECURITY DEFINER katmanlarının ayrı başarısızlık modları.
Çok kiracılı bir SaaS’ta veri izolasyonu tek katmanla sağlanmaz. RLS, sorgu filtresi, UI gate ve SECURITY DEFINER katmanlarının ayrı başarısızlık modları.
PAFTA çok kiracılı bir ERP: her firma yalnız kendi verisini görür. “Bunu nasıl sağlıyorsunuz?” sorusuna çoğu geliştirici “RLS var” diye cevap verir. Doğru, ama eksik. Gerçek izolasyon katmanlıdır ve her katmanın kendine özgü bir başarısızlık modu vardır.
Row Level Security, her sorguya veritabanı seviyesinde otomatik bir filtre uygular. Yaygın bir kiracı izolasyonu politikası şuna benzer:
CREATE POLICY tenant_isolation ON campaigns
USING (company_id = current_company_id());Buradaki kritik ayrıntı, politikanın okuduğu kaynağın kendisidir. Kiracı kimliğini üreten kolonu kullanıcı serbestçe değiştirebiliyorsa, RLS teknik olarak “çalışır” ama izolasyon çöker: kullanıcı kendi satırını başka bir firmaya çevirip o firmanın verisine meşru şekilde erişir. Kiracı izolasyonu, onu üreten kolonu kimin yazabildiği kadar güçlüdür — o kolonun değişmezliği ayrıca zorlanmalıdır.
RLS varken sorguya ayrıca filtre yazmak gereksiz görünür; değildir.
// Yalnız RLS'e yaslanır — çalışır, ama niyet kodda görünmez
const { data } = await supabase
.from('campaigns')
.select('*')
.eq('status', 'active');
// Defense-in-depth: hem sorgu hem RLS filtreler
const { data } = await supabase
.from('campaigns')
.select('id, name, status')
.eq('company_id', companyId)
.eq('status', 'active');İkinci biçim yalnız güvenlik için değil, okunabilirlik ve sorgu planı için de daha iyidir. Ayrıca yıldızlı seçim yerine kolonları açıkça yazmak, şema değiştiğinde sessizce fazla veri taşımanızı önler.
Bir butonu yetkiye göre gizlemek bir arayüz kararıdır. Butonu görmeyen biri, isteği doğrudan API’ye göndermekten alıkonmuş olmaz:
// Buton gizli olsa da bu istek yapılabilir
fetch('/rest/v1/campaigns?id=eq.123', {
method: 'DELETE',
headers: { Authorization: 'Bearer <kullanıcının kendi tokenı>' },
});Sonuç: UI gate kaldırılabilir bir kolaylıktır, veritabanı kuralı kaldırılamaz bir zorunluluktur. Bu ikisini aynı düzeyde görmek, en sık rastladığımız yanlış güvenlik hissi kaynağı. Pratikte şu ayrımı kullanıyoruz: kiracı izolasyonu ile yetki kontrolü ayrı sorulardır — bir tablonun politikası yalnız “aynı firma mı” diye soruyorsa, o tablo kiracı-güvenlidir ama yetki-güvenli değildir.
SECURITY DEFINER işaretli bir fonksiyon, onu çağıranın değil, tanımlayanın haklarıyla çalışır. Bu yüzden fonksiyonun içinde oturum kullanıcısını sorgulamak yanıltıcıdır — pratikte hep sahip rolünü görürsünüz ve “kim çağırdı” sorusu sessizce yanlış cevaplanır. Gerçek çağıran kimliği için kimlik doğrulama katmanının sağladığı kullanıcı kimliğini kullanın.
İkinci tuzak, uygulama tarafından yazılabilen ayar bayraklarına güvenmek. “Bu isteği denetleme” veya “bu kontrolü atla” kararını çalışma zamanı ayarına bağlarsanız, o ayarı yazabilen herkes kararı da yazabilir hale gelir. Sistemin kendisinden gelen sinyalleri tercih edin — örneğin bir tetikleyicinin başka bir tetikleyici tarafından mı çağrıldığını, uygulamanın söylediğine değil, veritabanının kendi derinlik bilgisine bakarak anlayın.
Üçüncüsü ve en sinsisi: bir fonksiyonu CREATE OR REPLACE ile güncellediğinizde tanım tamamen yenisiyle değişir. Yeni tanımda SECURITY DEFINER ifadesini yazmayı unutursanız fonksiyon sessizce varsayılana, yani çağıranın haklarıyla çalışmaya döner. Hata vermez; yalnız davranış değişir. Aynı şekilde, fonksiyonu yeniden oluşturmak üzerindeki yetki kısıtlamalarını da sıfırlayabilir. Bu yüzden her güncellemeden sonra fonksiyonun güvenlik bayrağını ve yetkilerini doğrulamak kontrol listemizde:
SELECT proname, prosecdef, proconfig
FROM pg_proc
WHERE proname = 'benim_fonksiyonum';İstek
↓ UI gate ......... görünürlük kararı, bypass edilebilir
↓ Uç nokta ........ token doğrulama
↓ Sorgu ........... açık kiracı filtresi + açık kolon listesi
↓ RLS ............. son savunma, her koşulda çalışır
↓ DEFINER fn ...... doğru kimlik sinyali + doğrulanmış yetkilerDers: güvenlik katmanlıdır ve katmanlar birbirinin yerine geçmez. Her katmanın nasıl sessizce bozulabileceğini bilmeden “güvendeyiz” demek, aslında yalnız en görünür katmanın çalıştığını söylemektir.
PAFTA’da veri izolasyonu ve yetkilendirme yaklaşımımızı merak ediyorsanız güvenlik sayfamıza göz atabilirsiniz.
Güvenlik ve KVKKPAFTA’nın kurucusu. KOBİ’ler için ERP, e-fatura ve ön muhasebe süreçlerini sadeleştirme üzerine yazıyor.
LinkedInBulut ERP ile yerinde (on-premise) ERP arasındaki maliyet, güvenlik, kurulum süresi ve esneklik farklarını karşılaştırıyoruz. KOBİ’ler için hangi model daha uygun?
KOBİ’lerin ön muhasebe/ERP seçerken bakması gereken 7 objektif kriter: modül kapsamı, e-Fatura entegrasyonu, fiyatlandırma modeli, kurulum süresi.