SaaS uygulamalarında PostgreSQL satır düzeyi güvenliği veritabanında kiracı izolasyonunu uygular. Ne zaman kullanılacağını, politikaların nasıl yazılacağını ve hangi hatalardan kaçınılacağını öğrenin.

Bir SaaS uygulamasında en tehlikeli güvenlik hatası, ölçeklendikten sonra ortaya çıkan hatadır. Başlangıçta “kullanıcılar sadece kendi kiracılarına ait veriyi görebilir” gibi basit bir kuralla başlarsınız; sonra yeni bir uç nokta hızla yayınlanır, bir raporlama sorgusu eklenir veya kontrolü atlayan bir join ortaya çıkar.
Sadece uygulama tarafında yapılan yetkilendirme baskı altında başarısız olur çünkü kurallar dağınık hale gelir. Bir kontrolör tenant_id denetler, başka bir kontrolör üyeliği doğrular, bir arka plan işi unutur ve bir “admin dışa aktarma” yolu aylarca "geçici" kalır. Çok dikkatli ekipler bile bir noktayı gözden kaçırır.
PostgreSQL satır düzeyi güvenliği (RLS), veritabanının bir isteğe hangi satırların görünür olduğunu zorunlu kılmasını sağlayarak spesifik bir sorunu çözer. Zihinsel model basittir: her SELECT, UPDATE ve DELETE politikalarla otomatik olarak filtrelenir; tıpkı her isteğin kimlik doğrulama ara yazılımı ile filtrelenmesi gibi.
“Satırlar” kısmı önemlidir. RLS her şeyi korumaz:
Somut bir örnek: Dashboard için projeleri listelerken invoices ile join yapan bir uç nokta eklersiniz. Sadece uygulama yetkilendirmesiyle projects için tenant filtresi uygulayıp invoices için filtrelemeyi unutmak veya tenantları çaprazlayan bir anahtarda join yapmak kolaydır. RLS ile her iki tablo da kiracı izolasyonunu zorunlu kılabilir, böylece sorgu veri sızdırmak yerine güvenli bir şekilde başarısız olur.
Takas gerçek. Tekrarlanan yetkilendirme kodunu daha az yazarsınız ve sızıntı yaratabilecek yerlerin sayısını azaltırsınız. Ancak yeni işler de yüklenir: politikaları dikkatle tasarlamalı, erken test etmeli ve bir politikanın beklediğiniz bir sorguyu engelleyebileceğini kabul etmelisiniz.
Uygulamanız bir avuç uç noktanın ötesine geçene kadar RLS ekstra iş gibi gelebilir. Katı kiracı sınırlarınız ve çok sayıda sorgu yolu (liste ekranları, arama, dışa aktarımlar, yönetici araçları) varsa kurali veritabanına koymak, aynı filtreyi her yere eklemeyi hatırlama zorunluluğunu ortadan kaldırır.
RLS, kural sıkıcı ve evrensel olduğunda güçlü bir uyum sağlar: “bir kullanıcı yalnızca kendi kiracısına ait satırları görebilir” veya “bir kullanıcı yalnızca üyesi olduğu projeleri görebilir” gibi. Bu kurulumlarda politikalar hataları azaltır çünkü her SELECT, UPDATE ve DELETE aynı kapıdan geçer, hatta sorgu sonradan eklendiğinde bile.
Ayrıca filtreleme mantığının tutarlı kaldığı okuma-ağırlıklı uygulamalarda yardımcı olur. API'nizde faturaları yüklemenin 15 farklı yolu varsa (duruma, tarihe, müşteriye, aramaya göre), RLS her sorguda kiracı filtresini yeniden uygulamayı bırakmanızı sağlar ve özelliğe odaklanmanıza izin verir.
Kurallar satır bazlı olmadığında RLS acı verir. “Maaşı görebilirsin ama primi göremezsin” veya “bu sütunu İK değilse maskele” gibi alan bazlı kurallar genellikle garip SQL'lere ve zor bakılan istisnalara dönüşür.
Ayrıca gerçekten geniş erişim gerektiren ağır raporlama için uygun bir uyum değildir. Ekipler genellikle “sadece bu iş için” atlatma rolleri oluşturur ve hata birikimi burada başlar.
Karar vermeden önce veritabanının son gardiyan olup olmayacağına karar verin. Eğer evet ise disiplin planlayın: veritabanı davranışını test edin (sadece API yanıtlarını değil), göçleri güvenlik değişikliği olarak değerlendirin, hızlı atlatmalardan kaçının, arka plan işlerinin nasıl kimlik doğrulayacağını belirleyin ve politikaları küçük ve tekrarlanabilir tutun.
Eğer backend üreten araçlar kullanıyorsanız, teslimatı hızlandırabilirler ama açık roller, testler ve basit bir kiracı modeline olan ihtiyacı ortadan kaldırmazlar. (Örneğin, Koder.ai Go ve PostgreSQL ile üretilmiş backendler kullanır ve yine de RLS'i sonradan “serpiştirmek” yerine kasıtlı tasarlamak istersiniz.)
Şema zaten kimin neye sahip olduğunu açıkça söylüyorsa RLS en kolay haldedir. Bulanık bir modelle başlayıp “politikalarda düzeltmeye” çalışırsanız genellikle yavaş sorgular ve kafa karıştıran hatalar elde edersiniz.
Bir tenant anahtarını (ör. org_id) seçin ve tutarlı kullanın. Çoğu tenant-sahibi tablo bunu içermelidir; hatta başka bir tabloya referans verse bile. Bu, politikalar içinde join'leri önler ve USING kontrollerini basit tutar.
Pratik kural: bir satır müşteri iptal ettiğinde kaybolması gerekiyorsa muhtemelen org_id'ye ihtiyaç duyar.
RLS politikaları genellikle bir soruya cevap verir: “Bu kullanıcı bu org'un üyesi mi ve ne yapabilir?” Bunu rastgele sütunlardan çıkarmak zordur.
Çekirdek tabloları küçük ve sıkıcı tutun:
users (kişiye bir satır)orgs (her tenant için bir satır)org_memberships (user_id, org_id, role, status)project_membershipsBunlarla politikalarınız üyeliği tek bir indeksli sorguyla kontrol edebilir.
Her şey org_id gerektirmez. Ülkeler, ürün kategorileri veya plan tipleri gibi referans tablolar genelde tüm kiracılar arasında paylaşılan verilerdir. Bunları çoğu rol için salt okunur yapın ve tek bir org'a bağlamayın.
Tenant-sahibi veriler (projeler, faturalar, ticket'lar) paylaşılan tablolardan kiraca özel detayları çekmemelidir. Paylaşılan tabloları minimal ve stabil tutun.
Foreign key'ler RLS ile hala çalışır, ancak silmeler sizi şaşırtabilir çünkü silme yapan rol bağlı satırları “göremeyebilir”. Cascade'leri dikkatle planlayın ve gerçek silme akışlarını test edin.
Policy'ların filtrelediği sütunları, özellikle org_id ve üyelik anahtarlarını indeksleyin. WHERE org_id = ... gibi görünen bir politika tablo milyonlarca satıra ulaşınca tam tablo taraması olmamalıdır.
RLS tablo başına açılan bir anahtardır. Bir kez etkinleştirildiğinde PostgreSQL uygulama kodunun kiracı filtresini hatırlamasını güvenilmez sayar. Her SELECT, UPDATE ve DELETE politikalar tarafından filtrelenir, her INSERT ve UPDATE ise politikalar tarafından doğrulanır.
En büyük zihinsel değişim: RLS açıkken eskiden veri döndüren sorgular artık hata vermeden sıfır satır döndürebilir. Bu PostgreSQL'in erişim kontrolü uygulaması.
Politikalar tabloya bağlı küçük kurallardır. İki kontrol kullanırlar:
USING okuma filtresidir. Bir satır USING ile eşleşmiyorsa SELECT için görünmez ve UPDATE veya DELETE tarafından hedeflenemez.WITH CHECK yazma kapısıdır. INSERT veya UPDATE sırasında yeni veya değişen satırların hangi şartları sağlaması gerektiğini belirler.Yaygın bir SaaS deseni: USING sadece kendi kiracınızın satırlarını görmenizi sağlar, WITH CHECK ise tahminle gönderilen bir tenant ID ile başkasına satır eklenmesini engeller.
Daha sonra daha fazla politika eklendiğinde bu önem kazanır:
PERMISSIVE (varsayılan): herhangi bir politika satırı izin veriyorsa satır kabul edilir.RESTRICTIVE: yalnızca tüm kısıtlayıcı politikalar izin veriyorsa satır kabul edilir (permissive davranışın üzerine ek olarak).Tenant eşleşmesi + rol kontrolleri + proje üyeliği gibi katmanlı kurallar koymayı planlıyorsanız, restrictive politikalar niyeti daha net yapabilir; ancak bir koşulu unutursanız kendinizi kilitlemek de kolaylaşır.
RLS güvenilir bir “kim çağırıyor” değeri gerektirir. Yaygın seçenekler:
app.user_id ve app.tenant_id).SET ROLE ... her istek için), çalışabilir ama işletme bakım yükü ekler.Bir yaklaşım seçin ve her yerde uygulayın. Hizmetler arasında kimlik kaynaklarını karıştırmak hızlıca kafa karıştıran hatalara yol açar.
Şema dökümleri ve loglar okunaklı kalsın diye öngörülebilir bir konvansiyon kullanın. Örneğin: {table}__{action}__{rule}, mesela projects__select__tenant_match.
RLS'e yeniyseniz bir tabloyla başlayın ve küçük bir kanıt yapın. Amaç mükemmel kapsam değil. Amaç, uygulama hatası olsa bile veritabanının kiracılar arası erişimi reddetmesidir.
Basit bir projects tablosu varsayın. İlk olarak, yazmaları bozmadan tenant_id ekleyin.
ALTER TABLE projects ADD COLUMN tenant_id uuid;
-- Backfill existing rows (example: everyone belongs to a default tenant)
UPDATE projects SET tenant_id = '11111111-1111-1111-1111-111111111111'::uuid
WHERE tenant_id IS NULL;
ALTER TABLE projects ALTER COLUMN tenant_id SET NOT NULL;
Sonra sahipliği erişimden ayırın. Yaygın bir desen: tablolar bir rol tarafından sahiplenilir (app_owner), API ise başka bir rol ile erişir (app_user). API rolü tablo sahibi olmamalıdır, aksi halde politikaları atlayabilir.
ALTER TABLE projects OWNER TO app_owner;
REVOKE ALL ON projects FROM PUBLIC;
GRANT SELECT, INSERT, UPDATE, DELETE ON projects TO app_user;
Şimdi isteğin Postgres'e hangi kiracıyı hizmet ettiğini söyleyeceğine karar verin. Basit bir yaklaşım istek kapsamlı bir ayar kullanmaktır. Uygulamanız bunu bir işlem açtıktan hemen sonra ayarlar.
-- inside the same transaction as the request
SELECT set_config('app.current_tenant', '22222222-2222-2222-2222-222222222222', true);
RLS'i etkinleştirin ve önce okuma erişimi ile başlayın.
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;
CREATE POLICY projects_tenant_select
ON projects
FOR SELECT
TO app_user
USING (tenant_id = current_setting('app.current_tenant')::uuid);
Farklı iki kiracıyla deneyip satır sayısının değiştiğini kontrol ederek çalıştığını kanıtlayın.
WITH CHECK)Okuma politikaları yazmaları korumaz. Ekleyin ki insert ve update işlemleri yanlış kiracıya satır sokamasın.
CREATE POLICY projects_tenant_write
ON projects
FOR INSERT, UPDATE
TO app_user
WITH CHECK (tenant_id = current_setting('app.current_tenant')::uuid);
Davranışı (hatalar dahil) doğrulamanın hızlı bir yolu, her göçten sonra yeniden çalıştırabileceğiniz küçük bir SQL betiği tutmaktır:
BEGIN; SET LOCAL ROLE app_user;SELECT set_config('app.current_tenant', '\u003ctenant A\u003e', true); SELECT count(*) FROM projects;INSERT INTO projects(id, tenant_id, name) VALUES (gen_random_uuid(), '\u003ctenant B\u003e', 'bad'); (başarısız olmalı)UPDATE projects SET tenant_id = '\u003ctenant B\u003e' WHERE ...; (başarısız olmalı)ROLLBACK;Bu betiği her çalıştırdığınızda aynı sonuçları alabiliyorsanız, RLS'i diğer tablolara genişletmeden önce güvenilir bir temeliniz vardır.
Çoğu ekip aynı yetkilendirme kontrollerini her sorguda tekrar etmekten bıkınca RLS kullanmaya başlar. İyi haber, ihtiyaç duyacağınız politika şekilleri genelde tutarlıdır.
Bazı tablolar doğal olarak bir kullanıcıya aittir (notlar, API token'ları). Diğerleri bir kiracıya aittir ve erişim üyeliğe bağlıdır. Bunları farklı desenler olarak ele alın.
Sahip-e yönelik veriler için politikalar genellikle created_by = app_user_id() gibi kontroller yapar. Kiracı verileri için politikalar genellikle kullanıcının org için bir üyelik satırı olup olmadığını kontrol eder.
Politikaları okunabilir tutmanın pratik bir yolu kimliği küçük SQL yardımcılarında merkezileştirmek ve yeniden kullanmaktır:
-- Example helpers
create function app_user_id() returns uuid
language sql stable as $$
select current_setting('app.user_id', true)::uuid
$$;
create function app_is_admin() returns boolean
language sql stable as $$
select current_setting('app.is_admin', true) = 'true'
$$;
Okumalar genelde yazmalardan daha geniştir. Örneğin, herhangi bir org üyesi projeleri SELECT edebilir, ama sadece editörler UPDATE yapabilir ve sadece sahipler DELETE yapabilir.
Açık tutun: bir SELECT politikası (üyelik), bir INSERT/UPDATE politikası WITH CHECK ile (rol) ve bir DELETE politikası (genelde update'ten daha sıkı) şeklinde ayırın.
“Adminler için RLS'i kapat” yerine, app_is_admin() gibi politikalar içinde bir kaçış yolu ekleyin; böylece paylaşılan bir servis rolüne kazara tam erişim vermemiş olursunuz.
deleted_at veya status kullanıyorsanız bunu SELECT politikasına dahil edin (deleted_at is null). Aksi halde uygulamanın final kabul ettiği bayrakları biri değiştirerek satırları "diriltebilir".
WITH CHECK ile uyumlu tutunINSERT ... ON CONFLICT DO UPDATE yazma sonrası satır için WITH CHECK'i sağlamalıdır. Politikanız created_by = app_user_id() gerektiriyorsa, upsert insert sırasında created_by'ı ayarlamalı ve update sırasında onu üzerine yazmamalıdır.
Eğer backend kodu üretiyorsanız, bu desenler yeni tabloların güvenli varsayılanlarla başlamasını sağlamak için dahili şablonlara dönüştürmeye değer.
RLS harika ama küçük bir detay PostgreSQL’in veriyi “rastgele” saklıyormuş gibi görünmesine yol açabilir. Aşağıdaki hatalar en çok zaman kaybettirir.
İlk tuzak, INSERT ve UPDATE için WITH CHECK'i unutmaktır. USING sadece neyi görebileceğinizi kontrol eder, ne oluşturabileceğinizi değil. WITH CHECK olmadan bir uygulama hatası yanlış kiracıya satır yazabilir ve aynı kullanıcı bunu geri okuyamadığı için fark etmeyebilirsiniz.
Bir diğer yaygın sızıntı “sızdıran join”dir. projects'u doğru filtrelersiniz, sonra invoices, notes veya files ile join yaparsınız ancak onlar aynı şekilde korunmamıştır. Çözüm katıdır ama basittir: kiracı verisini açığa çıkarabilecek her tablo kendi politikasına ihtiyaç duyar; view'lar yalnızca bir tablonun güvenli olmasına dayanarak yazılmamalıdır.
Yaygın hata örüntüleri erken çıkar:
WITH CHECK eksik.Aynı tabloya referans veren politikalar (doğrudan veya view aracılığıyla) döngüsel sürprizler yaratabilir. Bir politika üyeliği başka bir view üzerinden kontrol ediyorsa ve o view korunan tabloyu tekrar okursa hatalar, yavaş sorgular veya hiçbir zaman eşleşmeyen bir politika oluşabilir.
Rol ayarları başka bir kafa karışıklığı kaynağıdır. Tablo sahipleri ve yükseltilmiş roller RLS'i atlayabilir, bu yüzden testleriniz geçerken gerçek kullanıcılar başarısız olabilir (veya tam tersi). Her zaman uygulamanın kullandığı düşük ayrıcalıklı rol ile test edin.
SECURITY DEFINER fonksiyonlarına dikkat edin. Bunlar fonksiyon sahibinin ayrıcalıkları ile çalışır, bu yüzden current_tenant_id() gibi bir yardımcı genelde güvenli olabilir, ama veriyi okuyan “kolaylık” fonksiyonları tenantlar arası okumaya izin verebilir; tasarımını RLS'e saygılı yapın.
Ayrıca security definer fonksiyonlar içinde güvenli bir search_path ayarlayın. Ayarlamazsanız fonksiyon aynı ada sahip farklı bir nesneyi yakalayabilir ve politika mantığınız oturum durumuna bağlı olarak yanlış şeye işaret edebilir.
RLS hataları genelde bağlam eksikliğinden kaynaklanır, kötü SQL'den değil. Bir politika kağıt üzerinde doğru olsa bile oturum rolü sandığınızdan farklı olabilir veya istek politikaların beklediği tenant ve kullanıcı değerlerini hiç ayarlamıyor olabilir.
Üretim bir raporu yeniden üretmenin güvenilir yolu, aynı oturum ayarlarını yerelde taklit etmek ve tam sorguyu çalıştırmaktır. Bu genelde şunları içerir:
SET ROLE app_user; (veya gerçek API rolü)SELECT set_config('app.tenant_id', 't_123', true); ve SELECT set_config('app.user_id', 'u_456', true);SELECT current_user, current_setting('app.tenant_id', true), current_setting('app.user_id', true);Hangi politikanın uygulandığından emin değilseniz tahmin etmek yerine katalogu kontrol edin. pg_policies her politikayı, komutu ve USING ile WITH CHECK ifadelerini gösterir. Bunu pg_class ile eşleştirip tabloda RLS'in etkin olup olmadığını ve atlanıp atlanmadığını doğrulayın.
Performans sorunları yetki sorunları gibi görünebilir. Bir politika üyelik tablosuna join yapıyor veya bir fonksiyon çağırıyorsa doğru olabilir ama tablo büyüdüğünde yavaşlayabilir. Yeniden üretilen sorguda EXPLAIN (ANALYZE, BUFFERS) kullanın ve tam tablo taramaları, beklenmeyen nested loop'lar veya filtrelerin geç uygulanması gibi şeylere bakın. (tenant_id, user_id) ve üyelik tablolarındaki eksik indeksler yaygın sebeplerdir.
Ayrıca app katmanında her istekte üç değeri loglamak yardımcı olur: tenant ID, user ID ve istek için kullanılan veritabanı rolü. Bunlar düşündüğünüz gibi ayarlı değilse, RLS “yanlış” davranır çünkü girdiler yanlıştır.
Testler için birkaç seed tenant tutun ve başarısızlıkları açık hale getirin. Küçük bir test süiti genellikle şunları içerir: “Tenant A, Tenant B'yi okuyamaz”, “üyeliği olmayan kullanıcı projeyi göremez”, “sahip güncelleyebilir, viewer güncelleyemez”, “insert bağlam eşleşmiyorsa engellenir” ve “admin override sadece hedeflendiği yerde uygulanır”.
RLS'i bir emniyet kemeri gibi değerlendirin, bir özellik anahtarı gibi değil. Küçük eksiklikler “herkes herkesin verisini görebilir” veya “her şey sıfır satır döner” sonuçlarına yol açabilir.
Tablo tasarımınız ve politika kurallarınız kiracı modelinizle eşleşsin.
tenant_id) sahip olmalı. Eğer yoksa nedenini yazın (ör. global referans tablolar).FORCE ROW LEVEL SECURITY düşünün.USING ile, yazmalar WITH CHECK ile olmalı ki insert ve update başka bir kiracıya satır taşıyamasın.tenant_id ile filtreleme veya üyelik tabloları üzerinden join varsa uygun indeksleri ekleyin.Basit bir sağlık senaryosu: tenant A kullanıcısı kendi faturalarını okuyabilir, sadece tenant A için fatura ekleyebilir ve faturayı tenant_id'yi değiştirerek güncelleyemez.
RLS, uygulamanızın kullandığı roller kadar güçlüdür.
bypassrls hakkına sahip bir rol ile bağlanmadığından emin olun.Şirketlerin (org) projeleri olduğu ve projelerin görevleri olduğu bir B2B uygulamasını düşünün. Kullanıcılar birden fazla org'a üye olabilir ve bir kullanıcı bazı projelerin üyesi olmayabilir. Bu, RLS için iyi bir uyumdur çünkü veritabanı API uç noktası bir filtreyi unuttuğunda bile kiracı izolasyonunu zorunlu kılabilir.
Basit model: orgs, users, org_memberships (org_id, user_id, role), projects (id, org_id), project_memberships (project_id, user_id), tasks (id, project_id, org_id, ...). tasks üzerindeki org_id kasıtlıdır. Politikaları basit tutar ve join'lerde sürprizleri azaltır.
Klasik bir sızıntı, görevlerin sadece project_id'ye sahip olmasıdır ve erişimi projects üzerinden join ile kontrol edersiniz. Bir hata (projects üzerinde izin veren bir politika, join sırasında bir koşulun düşmesi veya bir view'ın bağlamı değiştirmesi) diğer org'ların görevlerini açığa çıkarabilir.
Daha güvenli bir geçiş yolu üretim trafiğini bozmadan şöyle olabilir:
tasks'a org_id ekleyin, üyelik tablolarını ekleyin).tasks.org_id'yi projects.org_id'den backfill yapın, sonra NOT NULL ekleyin.Destek erişimi genellikle RLS'i devre dışı bırakmak yerine dar bir break-glass rolü ile yönetilir. Bunu normal destek hesaplarından ayırın ve ne zaman kullanıldığını açıkça kaydedin.
Kuralları belgeleyin: hangi oturum değişkenlerinin ayarlanması gerektiği (user_id, org_id), hangi tabloların org_id taşıması gerektiği, “üye” tanımının ne olduğu ve yanlış org ile çalıştırıldığında 0 satır döndürmesi gereken birkaç SQL örneği.
RLS, bir ürün değişikliği gibi ele alındığında daha kolay yaşanır. Küçük parçalar halinde dağıtın, testlerle davranışı kanıtlayın ve her politikanın neden var olduğuna dair net bir kayıt tutun.
İşleyen bir rollout planı genelde şöyle olur:
projects) ve onu kilitleyin.İlk tablo stabil olduktan sonra politika değişikliklerini kasıtlı yapın. Göçlere bir politika inceleme adımı ekleyin ve niyeti (kimin, neye ve neden erişmesi gerektiği) kısa bir notla beraber bir eşleyen test değişikliğini ekleyin. Bu, “bir OR daha ekle” şeklinde politikaların yavaşça delik haline gelmesini önler.
Hızlı hareket ediyorsanız, Koder.ai (koder.ai) gibi araçlar Go + PostgreSQL başlangıç noktası üretmenizde yardımcı olabilir; ardından aynı disiplinle RLS politikaları ve testleri ekleyin.
Son olarak, rollout sırasında emniyet rayları tutun. Politika göçlerinden önce snapshot alın, rollback alıştırmasını sık yapın ve tüm sistemi kapatmadan kullanılacak küçük bir break-glass yolu tutun.
RLS, PostgreSQL'in bir isteğe hangi satırların görülebileceğini veya yazılabileceğini zorunlu kılmasını sağlar; böylece kiracı izolasyonu her uç noktanın doğru WHERE tenant_id = ... filtresini hatırlamasına bağlı olmaz. Ana kazanım, uygulamanız büyüdükçe ve sorgular çoğaldıkça “bir eksik kontrol” hatalarını azaltmaktır.
Erişim kuralları tutarlı ve satır tabanlıysa (ör. kiracı izolasyonu veya üyeliğe dayalı erişim) ve çok sayıda sorgu yolu (arama, dışa aktarma, admin ekranları, arka plan işleri) varsa genellikle buna değer. Çoğu kural alan alanına özel, çok istisnaya dayalı veya geniş çaplı raporlama gerektiren durumlarda genelde değmez.
RLS'i satır görünürlüğü ve temel yazma kısıtlamaları için kullanın; diğer ihtiyaçlar için ek araçlar gerekir. Sütun gizliliği genellikle view'lar ve sütun ayrıcalıkları ile ele alınır; karmaşık iş kuralları (fatura sahipliği veya onay akışları gibi) hâlâ uygulama mantığında veya özenle tasarlanmış veritabanı kısıtlarında olmalıdır.
API için düşük ayrıcalıklı bir rol oluşturun (tablo sahibi olmayan), RLS'i etkinleştirin, sonra bir SELECT politikası ve WITH CHECK içeren bir INSERT/UPDATE politikası ekleyin. İstek kapsamlı bir oturum değeri (ör. app.current_tenant) ayarlayın ve bunu değiştirmenin hangi satırları görebildiğinizi ve yazabildiğinizi nasıl etkilediğini doğrulayın.
Yaygın bir varsayılan, her istek için bir oturum değişkenidir; örneğin işleme başladığınızda app.tenant_id ve app.user_id gibi değerler ayarlanır. Anahtar nokta tutarlılıktır: tüm kod yolları (web istekleri, işler, betikler) politikaların beklediği aynı değerleri ayarlamalıdır, aksi halde kafa karıştırıcı “sıfır satır” davranışıyla karşılaşırsınız.
USING mevcut satırların hangi koşullarda görüneceğini ve SELECT, UPDATE, DELETE tarafından hedef alınabileceğini kontrol eder. WITH CHECK ise INSERT ve sırasında yeni veya değişen satırların hangi şartları sağlaması gerektiğini belirler; böylece hatalı bir ile başka bir kiracıya yazmayı engeller.
USING eklediğinizde ama WITH CHECK yokken, hatalı bir uç nokta yine de başka bir kiracıya veri yazabilir ve bunu fark etmeyebilirsiniz çünkü aynı kullanıcı o satırı geri okumaya yetkili olmayabilir. Yazma kurallarını okumaya karşılık gelen WITH CHECK ile eşleştirmeden sakın.
Politikaları karmaşıklaştırmadan okunabilir tutmanın bir yolu, kiracı anahtarını (org_id gibi) doğrudan kiracıya ait tablolarda bulundurmaktır; bu sayede politikalar tek bir dizinli okuma ile üyeliği kontrol edebilir. Ayrıca org_memberships ve gerekirse project_memberships gibi açık üyelik tabloları ekleyin.
Önce aynı oturum bağlamını taklit ederek üretim raporunu yerelde yeniden oluşturun: aynı rolü ayarlayın, aynı oturum değişkenlerini set_config ile verin ve uygulamanın çalıştırdığı tam SQL'i yürütün. Sonra pg_policies'i kontrol ederek hangi USING ve WITH CHECK ifadelerinin uygulandığını görün; çoğu RLS hatası kimlik bağlamının eksik olmasından kaynaklanır, kötü SQL’den değil.
Evet — ama üretilen kodu bir başlangıç noktası olarak görün, güvenlik sistemi olarak değil. Koder.ai ile bir Go + PostgreSQL backend üretseniz bile, kendi kiracı modelinizi tanımlamanız, oturum kimliğini tutarlı ayarlamanız ve yeni tabloların doğru korumalarla gelmesini sağlamak için politikalar ve testler eklemeniz gerekir.
UPDATEtenant_id