Modern SaaS ortamında çok kiracılılık, sadece bir dağıtım stratejisi değil; temel bir mimari gerekliliktir. Paylaşılan veritabanları önemli maliyet verimliliği ve basitleştirilmiş yedekleme sunar ancak kritik bir güvenlik zorluğu olan veri izolasyonunu beraberinde getirir. Kiracı A'nın, bir uygulama hatası veya geliştirici hatası olsa bile Kiracı B'nin verilerini asla görmemesini nasıl sağlarsınız? Cevap Satır Seviyesinde Güvenlikte (RLS) yatar.
Satır Seviyesinde Güvenlik (RLS), PostgreSQL, Azure SQL ve Oracle gibi gelişmiş ilişkisel veritabanlarında bulunan güçlü bir özelliktir. Veritabanı motorunun erişim kontrol politikalarını uygulamasına olanak tanır ve güvenliği uygulama katmanından veri katmanına taşır. Bu yazıda, RLS'yi etkili bir şekilde nasıl uygulayacağımızı, performans veya geliştirici deneyiminden ödün vermeden sağlam izolasyon nasıl sağlanacağını keşfedeceğiz.
Neden RLS, Uygulama Seviyesinde Filtrelemeden Daha İyi?
Çok kiracılılığa yönelik geleneksel yaklaşım, her tabloya bir tenant_id (kiracı kimliği) sütunu eklemek ve her sorgunun WHERE tenant_id = current_user_tenant_id koşulunu içerdiğinden emin olmaktır. Bu yöntem çalışsa da, tamamen uygulama kodunun doğruluğuna dayanır. Bir geliştirici filtreyi unutursa veya karmaşık bir dinamik sorgu oluşturucu koşulu enjekte etmekte başarısız olursa, büyük bir veri ihlali meydana gelir.
RLS, bu riski SQL yürütme düzeyinde kısıtlamaları uygulayarak ortadan kaldırır. Bir yöneticiye kullanıcıya geniş izinler verilse bile, RLS politikası görünmez bir filtre gibi davranarak sonuçları yalnızca erişim izni olan satırlarla sınırlar. Bu derinlemesine savunma stratejisi, GDPR, HIPAA ve SOC2 gibi uyumluluk çerçeveleri için hayati önem taşır.
PostgreSQL'de RLS Uygulama
Gelişmiş RLS özellikleri için endüstri standardı olan PostgreSQL kullanarak pratik bir uygulamaya bakalım. Temel kavram, geçerli kiracı bağlamını belirleyen bir işlev oluşturma ve bu bağlamı uygulayan politikalar tanımlamayı içerir.
Adım 1: Kiracı Bağlamını Oluşturma
Öncelikle, geçerli kiracıyı tanımlamanın bir yoluna ihtiyacımız var. Bu genellikle bir oturum değişkeni ayarlayarak veya bir kimlik doğrulama belirtecindeki (JWT gibi) bir iddia kullanılarak yapılır. Bu örnek için özel bir oturum parametresi kullanacağız.
-- Güvenlik bağlamını etkinleştir
-- Gerçek bir uygulamada bu, kimlik doğrulamasından sonra ayarlanır
SET app.current_tenant_id = 'tenant_001';
-- Kiracı kimliğini almak için yardımcı işlev
CREATE OR REPLACE FUNCTION get_current_tenant_id()
RETURNS UUID AS $$
BEGIN
RETURN current_setting('app.current_tenant_id')::UUID;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
Adım 2: Güvenlik Politikalarını Tanımlama
Şimdi, politikaları belirli tablolara uygulayalım. Standart bir orders (siparişler) tablosu için bir örnek aşağıdadır.
-- Tabloda RLS'yi etkinleştir
ALTER TABLE orders ENABLE ROW LEVEL SECURITY;
-- Politika: Kullanıcılar yalnızca kendi verilerini görebilir
CREATE POLICY tenant_isolation_policy ON orders
USING (tenant_id = get_current_tenant_id());
-- İsteğe bağlı: Yönetici kullanıcılar için politika (süper kullanıcılar)
-- Yöneticilerin tüm verileri görmesi gerekiyorsa, ikinci bir politika oluşturabilirsiniz
CREATE POLICY admin_override_policy ON orders
FOR ALL
TO admin_role
USING (true);
USING ifadesi ile WITH CHECK ifadesi arasındaki ayrımı not edin. USING ifadesi, bir SELECT işlemi sırasında hangi satırların döndürüleceğini kontrol eder. WITH CHECK ifadesi, hangi satırların eklenebileceğini veya güncellenebileceğini kontrol eder; bu da kullanıcıların yanlışlıkla (veya kötü niyetle) başka kiracılara ait kayıtları değiştirmesini engeller.
Adım 3: Kimlik Yönetimi
Birçok uygulamada, bir kaydın kim tarafından oluşturulduğunu da izlemeniz gerekir. Kullanıcı tablonuzda bir user_id (kullanıcı kimliği) içeriyorsa, politikayı, kullanıcıların yalnızca kendi kiracı verilerine değil, ayrıca kişisel olarak oluşturdukları verilere de erişmesini sağlayacak şekilde genişletebilirsiniz.
Performans Düşünceleri
RLS güvenlidir ancak doğru şekilde indekslenmezse performansı etkileyebilir. Veritabanı motoru, erişilen her satır için politikayı değerlendirmek zorundadır. Bu nedenle, tenant_id sütununun indekslenmesi hayati önem taşır. Uygun bir indeks olmadan veritabanı, her sorgu için sıralı tarama (sequential scan) yapabilir ve veri hacmi büyüdükçe önemli gecikme artışı yaşanabilir.
Ayrıca, RLS politikalarının sorgu planlama aşamasına ek yük getirdiğini unutmayın. Güvenlik yükünün performans bütçenizi aşmadığından emin olmak için sorgularınızı yük altında test edin. Okuma ağırlıklı iş yükleri için, politika değerlendirmesinin yazma işlemleri üzerindeki etkisini azaltmak üzere okuma yansılarını veya önbellekleme stratejilerini düşünün.
Sonuç
Satır Seviyesinde Güvenlik uygulamak, çok kiracılı bir mimariyi güvence altına almanın en etkili yollarından biridir. Güvenliği, "güven ama doğrula" uygulama mantığı modelinden "asla güvenme, her zaman doğrula" veritabanı modeline taşır. RLS'den yararlanarak veritabanı mühendisleri, işle birlikte ölçeklenen, hassas verileri koruyan ve minimum uygulama düzeyi yüküyle uyumluluğu sağlayan sağlam bir güvenlik temeli sağlayabilir.
RLS'yi yığınına entegre ederken, uç durumları test ettiğinizden, uygun indekslemeyi sağladığınızdan ve güvenlik politikalarınızı net bir şekilde belgelediğinizden emin olun. Çok kiracılılık dünyasında güvenlik bir özellik değil; temelidir.