Veritabanı Yönetimi

    PostgreSQL Rol ve Yetki Yönetimi

    PostgreSQL rolleri, GRANT/REVOKE mantığı ve en az yetki ilkesiyle güvenli erişim kurulumu.

    11 dk okuma Güncellendi: 25 Ağustos 2026

    Yeni bir PostgreSQL kurulumunda uygulamanızı postgres süper kullanıcısıyla bağlamak beş dakikanızı alır ve aylarca sorun çıkarmaz — ta ki bir SQL enjeksiyonu ya da sızdırılmış bir .env dosyası, saldırgana veritabanının tamamını verene kadar. PostgreSQL rol ve yetki yönetimi, tam olarak bu riski bölmek için var: kim hangi tabloyu okuyabilir, kim yazabilir, kim şema değiştirebilir sorularının cevabını veritabanının kendisine yazarsınız ve uygulama katmanındaki bir hata tüm veritabanını ele geçirmeye yetmez.

    Bu rehberde PostgreSQL'in tek bir "rol" kavramı üzerine kurulu yetki modelini, kullanıcı ile grup arasındaki farkı, GRANT ve REVOKE komutlarının şema ve tablo düzeyinde nasıl çalıştığını, herkesin gözden kaçırdığı PUBLIC varsayılan izinlerini, yeni oluşturulan tablolara otomatik yetki veren ALTER DEFAULT PRIVILEGES komutunu ve çok kiracılı yapılarda satır düzeyi güvenliği (RLS) anlatacağım. Sonunda üretimde kullanabileceğiniz, salt okunur ve okuma-yazma rollerini ayıran tam bir kurulum betiği bulacaksınız.

    Rol Kavramı: Kullanıcı ve Grup Aynı Şeydir#

    PostgreSQL'de CREATE USER ve CREATE GROUP diye iki ayrı nesne yoktur. Her ikisi de roldür; aralarındaki tek fark LOGIN özniteliğidir. LOGIN verilmiş bir rol sunucuya bağlanabilir, verilmemiş bir rol yalnızca yetki taşıyıcısı olarak kullanılır. Aslında CREATE USER, CREATE ROLE ... LOGIN komutunun kısayolundan ibarettir.

    Bu tekilleştirme başta kafa karıştırıcı gelse de çok esnek bir model verir: yetkileri giriş yapmayan gruplara toplarsınız, kişileri ve uygulamaları da bu gruplara üye yaparsınız. Kişi ayrıldığında rolü silersiniz, grup ve yetkileri olduğu gibi kalır.

    -- Giriş yapamayan, yalnızca yetki taşıyan gruplar
    CREATE ROLE app_okuma  NOLOGIN;
    CREATE ROLE app_yazma  NOLOGIN;
    
    -- Gerçek bağlanan roller
    CREATE ROLE uygulama LOGIN PASSWORD 'guclu-parola' CONNECTION LIMIT 40;
    CREATE ROLE rapor    LOGIN PASSWORD 'baska-parola' CONNECTION LIMIT 5;
    
    -- Üyelikler
    GRANT app_yazma  TO uygulama;
    GRANT app_okuma  TO rapor;
    

    Rollerin sahip olabileceği öznitelikleri bilmek, hangisini kime vereceğinize karar vermeyi kolaylaştırır:

    ÖznitelikNe yaparKime verilir
    LOGINSunucuya bağlanabilirİnsan ve uygulama hesapları
    SUPERUSERTüm kontrolleri atlarNeredeyse hiç kimseye
    CREATEDBVeritabanı oluşturabilirGeliştirme ortamı hesapları
    CREATEROLERol oluşturup değiştirebilirSınırlı yönetici hesapları
    REPLICATIONReplikasyon akışı açabilirYedek ve replika hesapları
    BYPASSRLSSatır düzeyi güvenliği atlarBakım ve raporlama hesapları
    CONNECTION LIMITEşzamanlı bağlantı üst sınırıHer uygulama rolü için önerilir

    SUPERUSER konusunda net olun: bu öznitelik satır düzeyi güvenlik, sütun izinleri ve tüm GRANT kontrollerini tamamen devre dışı bırakır. Uygulamanızın veritabanına süper kullanıcıyla bağlanması, bu rehberde anlatacağım her şeyi anlamsız kılar.

    GRANT ve REVOKE Mantığı#

    PostgreSQL izinleri nesne bazlıdır ve bir izin verilmedikçe yoktur — tek istisna birazdan anlatacağım PUBLIC varsayılanlarıdır. Bir uygulamanın veritabanını kullanabilmesi için üç katmanda izne ihtiyacı vardır ve bu katmanlardan birinin eksik olması, klasik "permission denied for schema public" hatasının kaynağıdır.

    Katmanlar sırasıyla şunlardır: veritabanına bağlanma (CONNECT), şemayı görebilme (USAGE), ve tablo üzerinde işlem yapabilme (SELECT, INSERT, UPDATE, DELETE). Çoğu kişi üçüncü katmanı verip ikinciyi unutur.

    -- 1. katman: veritabanına bağlanma
    GRANT CONNECT ON DATABASE uygulama_db TO app_okuma, app_yazma;
    
    -- 2. katman: şemayı görebilme (SIK UNUTULAN ADIM)
    GRANT USAGE ON SCHEMA public TO app_okuma, app_yazma;
    
    -- 3. katman: mevcut tablolar üzerinde işlem
    GRANT SELECT ON ALL TABLES IN SCHEMA public TO app_okuma;
    GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_yazma;
    
    -- Sequence'ler ayrı bir nesne tipidir; INSERT için USAGE gerekir
    GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO app_yazma;
    

    GRANT ... ON ALL TABLES komutunun kritik sınırı şudur: yalnızca o an var olan tablolara uygulanır. Yarın oluşturacağınız tablo bu izinlerin dışında kalır ve uygulamanız aniden "permission denied for table yeni_tablo" hatası verir. Çözümü bir sonraki bölümde.

    Verilen bir izni geri almak için REVOKE kullanılır ve söz dizimi simetriktir. Yetkileri denetlemek için psql içinde \dp komutu en pratik araçtır:

    REVOKE DELETE ON TABLE odemeler FROM app_yazma;
    
    uygulama_db=# \dp odemeler
                                  Access privileges
     Schema |   Name   | Type  |       Access privileges
    --------+----------+-------+---------------------------------
     public | odemeler | table | sahip=arwdDxt/sahip             +
            |          |       | app_yazma=arwx/sahip            +
            |          |       | app_okuma=r/sahip
    

    Kısaltmaları okumak zor görünse de birkaç harfi bilmek yeter: r = SELECT (read), a = INSERT (append), w = UPDATE (write), d = DELETE, x = REFERENCES, t = TRIGGER, D = TRUNCATE. Yukarıdaki çıktıda app_yazma rolünde d harfinin olmaması, REVOKE DELETE komutunun işe yaradığını gösterir.

    PUBLIC Varsayılanları ve Şemayı Kilitlemek#

    PostgreSQL'in en çok gözden kaçan güvenlik detayı PUBLIC sözde rolüdür. Bu, "sunucudaki her rol" anlamına gelir ve bazı izinler varsayılan olarak ona verilmiştir. Tarihsel olarak public şeması üzerinde CREATE ve USAGE izinleri PUBLIC rolündeydi; yani bağlanabilen herhangi bir kullanıcı public şemasında tablo oluşturabiliyordu. PostgreSQL 15 ile bu davranış değişti ve CREATE izni kaldırıldı, ancak daha eski sürümlerden yükseltilmiş veritabanlarında eski izinler yerinde durmaya devam eder.

    Sürümünüz ne olursa olsun, izinleri açıkça kendiniz belirlemek en sağlıklısıdır:

    -- Herkesin şemada nesne oluşturmasını engelle
    REVOKE CREATE ON SCHEMA public FROM PUBLIC;
    
    -- Herkesin veritabanına bağlanmasını engelle, sadece istediğin rollere ver
    REVOKE CONNECT ON DATABASE uygulama_db FROM PUBLIC;
    GRANT  CONNECT ON DATABASE uygulama_db TO app_okuma, app_yazma;
    
    -- Fonksiyonlarda da EXECUTE varsayılan olarak PUBLIC'tedir
    REVOKE EXECUTE ON ALL FUNCTIONS IN SCHEMA public FROM PUBLIC;
    

    Yeni tabloların otomatik olarak doğru izinleri almasını sağlayan mekanizma ise ALTER DEFAULT PRIVILEGES. Bu komutun anlaşılması güç yanı, izinlerin kimin oluşturduğu tablolara uygulanacağını belirtmeniz gerekmesidir. FOR ROLE kısmını yazmazsanız komut yalnızca sizin oluşturduğunuz nesneler için geçerli olur:

    -- Bundan sonra 'sema_sahibi' rolünün oluşturduğu her tablo için otomatik izin
    ALTER DEFAULT PRIVILEGES FOR ROLE sema_sahibi IN SCHEMA public
        GRANT SELECT ON TABLES TO app_okuma;
    
    ALTER DEFAULT PRIVILEGES FOR ROLE sema_sahibi IN SCHEMA public
        GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO app_yazma;
    
    ALTER DEFAULT PRIVILEGES FOR ROLE sema_sahibi IN SCHEMA public
        GRANT USAGE, SELECT ON SEQUENCES TO app_yazma;
    

    Bu üç komutu kurulum sırasında çalıştırırsanız, göç (migration) betikleriniz yeni tablo eklediğinde izin vermeyi bir daha hiç düşünmezsiniz. Mevcut yetki tablosunu \ddp komutuyla görebilirsiniz.

    Şema Sahipliği ve Migration Rolü Ayrımı#

    Sağlam bir kurulumda dört rol ailesi olur ve bunları ayırmak, üretimde yanlışlıkla tablo düşürmeyi neredeyse imkânsız hale getirir. Şema sahibi rolü nesneleri oluşturur ve değiştirir; uygulama rolü yalnızca veri okur ve yazar; raporlama rolü sadece okur; yedekleme rolü replikasyon akışı açar.

    Uygulamanızın çalışma zamanı rolü hiçbir tablonun sahibi olmamalıdır. Sahiplik DROP TABLE, ALTER TABLE ve TRUNCATE haklarını beraberinde getirir; bu haklara sahip bir uygulama rolü, SQL enjeksiyonu durumunda veritabanınızı silebilir. Sahiplik ayrı bir migration rolünde durur ve o rol yalnızca deploy sırasında, ayrı bir bağlantı dizesiyle kullanılır.

    -- Şemayı ve tabloları oluşturacak rol
    CREATE ROLE sema_sahibi LOGIN PASSWORD 'migration-parolasi';
    ALTER SCHEMA public OWNER TO sema_sahibi;
    
    -- Uygulama rolü sahip DEĞİL, sadece kullanıcı
    -- (yukarıdaki GRANT'ler ve default privileges ile yetkilendirildi)
    
    RolSahiplikTipik yetkilerNerede kullanılır
    sema_sahibiŞema ve tablolarTam DDLYalnızca migration adımında
    uygulamaYokSELECT/INSERT/UPDATE/DELETEUygulama bağlantı havuzu
    raporYokYalnızca SELECTBI araçları, analiz sorguları
    yedekciYokREPLICATIONpg_basebackup, replika

    Bu ayrımı kurduktan sonra bağlantı dizelerinizi de ayırmayı unutmayın; ikisi de aynı .env dosyasında duruyorsa kazanç sınırlıdır. Yedekleme rolünün nasıl kullanıldığını PostgreSQL WAL arşivleme ve PITR yazısında ayrıntılı bulabilirsiniz. Rolleri oluşturduktan sonra parolaları üretmek için parola üretici aracımızı kullanabilirsiniz.

    Satır Düzeyi Güvenlik (RLS)#

    Çok kiracılı bir uygulamada her müşterinin verisi aynı tabloda durur ve ayrım WHERE musteri_id = ? koşuluyla yapılır. Bu koşulu tek bir sorguda yazmayı unutmanız, bir müşterinin başka bir müşterinin verisini görmesi demektir. Satır düzeyi güvenlik, bu filtreyi uygulama katmanından alıp veritabanına taşır; artık filtreyi unutmak mümkün değildir çünkü PostgreSQL onu her sorguya kendisi ekler.

    -- 1) Tabloda RLS'i etkinleştir
    ALTER TABLE siparisler ENABLE ROW LEVEL SECURITY;
    
    -- 2) Politikayı tanımla: oturum değişkenindeki kiracıya ait satırlar görünsün
    CREATE POLICY kiraci_izolasyonu ON siparisler
        USING (kiraci_id = current_setting('app.kiraci_id', true)::bigint);
    
    -- 3) Yazma için ayrı kontrol: başka kiracı adına satır eklenemesin
    CREATE POLICY kiraci_yazma ON siparisler
        FOR INSERT
        WITH CHECK (kiraci_id = current_setting('app.kiraci_id', true)::bigint);
    

    Uygulama her isteğin başında oturum değişkenini ayarlar ve bundan sonra tüm sorgular otomatik filtrelenir:

    -- Bağlantı havuzunda her istek başında çalıştırılır
    SELECT set_config('app.kiraci_id', '42', true);   -- true = işlem sonunda sıfırlanır
    
    -- Bundan sonra bu sorgu yalnızca 42 numaralı kiracının satırlarını döner
    SELECT * FROM siparisler;
    

    RLS'in iki büyük tuzağı vardır. Birincisi, tablo sahibi ve süper kullanıcı politikaları varsayılan olarak atlar. Test ederken postgres ile bağlanıp "RLS çalışmıyor" sonucuna varmak çok kolaydır; mutlaka gerçek uygulama rolüyle test edin. Sahibin de politikalara uymasını istiyorsanız ALTER TABLE siparisler FORCE ROW LEVEL SECURITY; komutunu çalıştırın. İkincisi, current_setting çağrısındaki ikinci parametreyi true yapmazsanız değişken ayarlanmamışsa sorgu hata verir; true ile NULL döner ve politika hiçbir satır göstermez — güvenli olan davranış budur.

    Sık Yapılan Hatalar#

    Uygulamayı süper kullanıcıyla bağlamak. En yaygın ve en pahalı hata. Süper kullanıcı tüm izin kontrollerini, RLS dahil, atlar. Kurulumun ilk günü kolaylık olsun diye yapılır ve yıllarca öyle kalır.

    USAGE ON SCHEMA izninin unutulması. Tablo izinlerini vermenize rağmen "permission denied for schema public" hatası alıyorsanız neredeyse kesinlikle bu eksiktir. Tablo izni, o tablonun bulunduğu şemayı görme hakkını içermez.

    ALTER DEFAULT PRIVILEGES komutunu yanlış rolle çalıştırmak. Komutu postgres ile çalıştırıp FOR ROLE sema_sahibi kısmını yazmazsanız, varsayılan izinler yalnızca postgres kullanıcısının oluşturduğu tablolara uygulanır. Migration'larınız sema_sahibi ile çalıştığı için yeni tablolar yine izinsiz kalır ve neden olduğunu bulmak saatler alır.

    Parolayı ALTER ROLE ile değiştirip günlüğe düşürmek. ALTER ROLE ... PASSWORD 'metin' komutu, log_statement = 'ddl' veya 'all' ayarlıysa parolayı düz metin olarak sunucu günlüğüne yazar. Parola değişikliklerini psql içinde \password rol_adi komutuyla yapın; bu komut parolayı istemci tarafında hash'leyip gönderir.

    Rolü silememek. DROP ROLE komutu, rolün sahip olduğu nesneler veya üzerinde izinleri varsa hata verir. Önce REASSIGN OWNED BY eski_rol TO yeni_rol; ve DROP OWNED BY eski_rol; çalıştırın, sonra rolü düşürün. Bu iki komutu her veritabanında ayrı ayrı çalıştırmanız gerektiğini unutmayın.

    pg_hba.conf katmanını atlamak. GRANT izinleri yalnızca bağlantı kurulduktan sonra devreye girer. Kimin hangi IP'den, hangi kimlik doğrulama yöntemiyle bağlanabileceği pg_hba.conf dosyasında belirlenir ve bu dosya GRANT'ten önce çalışır. scram-sha-256 kullanın, trust yöntemini üretimde asla bırakmayın.

    Sıkça Sorulan Sorular#

    CREATE USER ile CREATE ROLE arasındaki fark nedir#

    Pratikte hiçbir fark yoktur; CREATE USER, CREATE ROLE ... LOGIN komutunun kısayoludur. PostgreSQL 8.1'den beri kullanıcı ve grup tek bir "rol" kavramında birleştirildi ve aralarındaki tek ayrım LOGIN özniteliğidir. Alışkanlık olarak giriş yapacak hesaplar için CREATE USER, yetki grupları için CREATE ROLE ... NOLOGIN yazmak okunabilirliği artırır ama teknik bir zorunluluk değildir.

    Yeni oluşturduğum tabloya uygulama neden erişemiyor#

    Çünkü GRANT ... ON ALL TABLES IN SCHEMA komutu yalnızca çalıştırıldığı andaki tablolara uygulanır, gelecekte oluşturulacaklara değil. Çözüm ALTER DEFAULT PRIVILEGES kullanmaktır ve komutu tabloları kimin oluşturduğunu belirten FOR ROLE kısmıyla birlikte yazmanız gerekir. Aksi halde varsayılan izinler yalnızca komutu çalıştıran rolün oluşturduğu nesnelere işler.

    Bir rolün hangi yetkilere sahip olduğunu nasıl görürüm#

    psql içinde \du komutu rollerin özniteliklerini ve üyeliklerini listeler, \dp tablo_adi belirli bir tablonun erişim izinlerini gösterir, \dn+ şema izinlerini verir. Programatik olarak sorgulamak isterseniz information_schema.table_privileges görünümü veya has_table_privilege('uygulama', 'siparisler', 'SELECT') fonksiyonu kullanılabilir. Sonuncusu, bir izni gerçekten alıp almadığını test etmenin en kesin yoludur.

    Salt okunur bir kullanıcı nasıl oluştururum#

    Üç adım yeterlidir: rolü LOGIN ile oluşturun, veritabanına CONNECT ve şemaya USAGE verin, ardından GRANT SELECT ON ALL TABLES IN SCHEMA public çalıştırın. Gelecekteki tabloların da otomatik dahil olması için aynı role ALTER DEFAULT PRIVILEGES ... GRANT SELECT ON TABLES tanımlayın. Rolün yazma yapamayacağından emin olmak için \dp çıktısında yalnızca r harfini görmelisiniz.

    Satır düzeyi güvenlik performansı düşürür mü#

    Politika koşulu her sorguya bir WHERE filtresi olarak eklendiği için maliyeti, o filtrenin maliyeti kadardır. Politikada kullandığınız sütuna indeks kurduysanız ek yük genellikle ihmal edilebilir düzeyde kalır. Sorun, politikanın içinde alt sorgu veya fonksiyon çağrısı kullanmakta çıkar; bunlar her satır için değerlendirilebilir. Politikayı olabildiğince basit tutun ve etkisini EXPLAIN ANALYZE ile ölçün.

    Parolayı düz metin yerine nasıl güvenle değiştiririm#

    psql içinde \password rol_adi komutunu kullanın. Bu komut yeni parolayı sizden gizli olarak alır, istemci tarafında SCRAM ile hash'ler ve sunucuya yalnızca hash'i gönderir. Böylece parola ne ağ trafiğinde ne de sunucu günlüğünde düz metin olarak görünür. ALTER ROLE ... PASSWORD 'metin' yazmak, günlükleme ayarlarınıza bağlı olarak parolayı log dosyasına düşürebilir.

    Kapanış#

    PostgreSQL'de yetki yönetiminin özü birkaç ilkeye iniyor: uygulamanızı asla süper kullanıcıyla bağlamayın, şema sahipliği ile çalışma zamanı rolünü ayırın, USAGE ON SCHEMA iznini unutmayın ve ALTER DEFAULT PRIVILEGES ile gelecekteki tabloları da kapsayın. Çok kiracılı bir yapı işletiyorsanız satır düzeyi güvenliği uygulama filtresine güvenmeden veritabanına taşıyın, ama testi mutlaka gerçek uygulama rolüyle yapın çünkü sahip ve süper kullanıcı politikaları atlar.

    Bu düzeni kurmak için kendi PostgreSQL örneğinizi tam kontrolle yönetmeniz gerekir; root erişimli VDS veya kaynağı esnek bulut sunucu paketlerimiz pg_hba.conf dahil her ayarı özgürce düzenlemenize izin verir. Rol düzeni, erişim politikaları ve düzenli güvenlik denetimlerini devretmek isterseniz sunucu yönetimi hizmetimiz bu işi üstlenir; veritabanınızı dış saldırılara karşı önden korumak için de DDoS koruma ve WAF katmanlarımıza göz atabilirsiniz.

    PostgreSQLGüvenlikYetkilendirme

    Uygulamaya geçmeye hazır mısınız?

    NVMe SSD, ücretsiz SSL ve %99.9 uptime garantisiyle Clou.TR hosting ve sunucu çözümleriyle projenizi hayata geçirin.