PostgreSQL SECURITY DEFINER vs INVOKER: Ne Zaman Hangisini Kullanmalı?
Supabase'de yazdığınız fonksiyon RLS'yi bypass mı ediyor? SECURITY DEFINER ve INVOKER arasındaki farkı, hangi durumda hangisini kullanacağınızı basitçe anlattık.
Sorun: Fonksiyon Tüm Verileri Gösteriyor
Supabase’de Row Level Security (RLS) kurdunuz. Her kullanıcı sadece kendi şubesinin verilerini görüyor. Her şey güzel — ta ki bir fonksiyon yazana kadar:
CREATE FUNCTION toplam_stok_sayisi()
RETURNS integer AS $$
SELECT COUNT(*) FROM v3_stock;
$$ LANGUAGE sql;
Bu fonksiyonu çağırdığınızda tüm şubelerin stok sayısını döndürüyor. RLS çalışmıyor!
Neden? Çünkü fonksiyonun güvenlik modu yanlış.
İki Mod Var
SECURITY INVOKER (Varsayılan — Güvenli)
Fonksiyon, onu çağıran kullanıcının yetkileriyle çalışır. Yani RLS kuralları geçerlidir:
CREATE FUNCTION benim_stoklarim()
RETURNS SETOF v3_stock
LANGUAGE sql
SECURITY INVOKER -- Çağıran kişinin yetkileri geçerli
AS $$
SELECT * FROM v3_stock;
$$;
Ahmet bu fonksiyonu çağırırsa → sadece Ahmet’in görebildiği satırlar döner.
SECURITY DEFINER (Dikkatli Kullanın!)
Fonksiyon, fonksiyonu oluşturan kişinin (genelde süper yetkili postgres) yetkileriyle çalışır. RLS devre dışı kalır:
CREATE FUNCTION tum_stoklari_getir()
RETURNS SETOF v3_stock
LANGUAGE sql
SECURITY DEFINER -- ⚠️ postgres yetkisiyle çalışır, RLS yok!
AS $$
SELECT * FROM v3_stock;
$$;
Kim çağırırsa çağırsın → tüm satırlar döner.
Basit Kural
%90 fonksiyonunuz → INVOKER olsun (RLS korunur)
%10 fonksiyonunuz → DEFINER gerekebilir (aşağıdaki durumlarda)
Ne Zaman DEFINER Kullanılır?
| Senaryo | Neden DEFINER? |
|---|---|
| Audit log yazma | Kullanıcı audit tablosuna yazamaz ama fonksiyon yazabilmeli |
| Tüm şubelerin toplamını gösterme | Yönetici raporu — toplam rakam gerekli |
| Sistem bakım işlemleri | Kullanıcı tetikleyebilmeli ama tüm veriye erişmemeli |
DEFINER Kullanırken Güvenlik
DEFINER fonksiyonlarda güvenliği siz sağlamalısınız. RLS size yardımcı olmaz:
CREATE FUNCTION sube_ozet_raporu(hedef_sube text)
RETURNS TABLE(toplam_stok bigint, toplam_deger numeric)
LANGUAGE plpgsql
SECURITY DEFINER
SET search_path = public -- ← Güvenlik için zorunlu
AS $$
BEGIN
-- 1. Yetki kontrolü: Bu kullanıcı bu şubeyi görebilir mi?
IF NOT EXISTS (
SELECT 1 FROM kullanici_subeleri
WHERE kullanici_id = auth.uid()
AND sube_kodu = hedef_sube
) THEN
RAISE EXCEPTION 'Bu şubeye erişim yetkiniz yok';
END IF;
-- 2. Yetkili — veriyi getir
RETURN QUERY
SELECT COUNT(*)::bigint, SUM(miktar * birim_fiyat)
FROM v3_stock
WHERE depo = hedef_sube;
END;
$$;
3 kritik kural:
SET search_path = public— SQL injection koruması- Fonksiyon içinde yetki kontrolü yapın
- Kullanıcı girdisini doğrudan SQL’e koymayın
Mevcut Fonksiyonlarınızı Kontrol Edin
Veritabanınızdaki fonksiyonların hangisi DEFINER, hangisi INVOKER — bir bakın:
SELECT
proname AS fonksiyon_adi,
CASE prosecdef
WHEN true THEN '⚠️ DEFINER'
ELSE '✅ INVOKER'
END AS guvenlik_modu
FROM pg_proc
JOIN pg_namespace ON pronamespace = pg_namespace.oid
WHERE nspname = 'public'
ORDER BY prosecdef DESC;
DEFINER olan fonksiyonlarınız varsa, gerçekten DEFINER olmaları gerektiğinden emin olun.
Özet
- INVOKER = güvenli varsayılan. RLS çalışır, her kullanıcı kendi verisini görür.
- DEFINER = güçlü araç ama dikkatli kullanın. RLS bypass eder.
- DEFINER kullanıyorsanız, fonksiyon içinde kendiniz yetki kontrolü yapın.
Çoğu projede INVOKER yeterlidir. DEFINER sadece özel durumlarda gerekir.
Bu yazı AstaFlow Case Study serisinin bir parçasıdır.