Veritabanı sunucularımızda bulunan kullanıcıların hangi yetkilere sahip oldukları, hangi tablolara ulaştığı, hangi fonksiyonları kullanma yetkisi olduğu, hangi şemaları görüntüleyebildiği gibi bir çok yetkinin gerekli şekillerde dağıtılması veritabanı güvenliğimiz açısından çok önemli bir durum. Bu yazıda bu yetkilerin nasıl kontrol edilebileceğini anlatacağım ve düzeltilebilecek bazı genel noktalardan bahsedeceğim.
Öncelikle kullanım yetkileri(Access Privileges) hakkında bilgi sahibi değilseniz Mina'nın bu konu ile ilgili blog yazısını okumanızı tavsiye ederim: Mina Yılık | Access Privilege (Kullanım Yetkileri) Nedir?

Öncelikle PostgreSQL'in veritabanına erişim ayarlarının belirlendiği "pg_hba.conf" dosyasını inceleyelim. Bu dosyanın bulunduğu yeri bilmiyorsanız iki şekilde öğrenebilirsiniz:

1) Veritabanına bağlandıktan sonra "SHOW hba_file;" komutu ile,
2) "postgresql.conf" içerisindeki "hba_file" parametresinde yazılan konumda.

Bu dosyada bulunan bağlantı kuralları sırasıyla takip edilir. Eğer ilk baştaki kural bağlantı isteği ile uyuşmazsa bir sonraki kural uygulanır. Eğer kurallardan hiçbiri uymuyorsa bağlantı reddedilir(reject).

Bağlantı kurallarını genel olarak aşağıdaki gibi kullanabilirsiniz:

local        <veritabanı> <kullanıcı> <metod> [seçenek]
host         <veritabanı> <kullanıcı> <adres/ağmaskesi> <metod> [seçenek]
hostssl     <veritabanı> <kullanıcı> <adres/ağmaskesi> <metod> [seçenek]
hostnossl <veritabanı> <kullanıcı> <adres/ağmaskesi> <metod> [seçenek]
 

Bağlantı Tipleri:

local: Bu bağlantı tipi unix-domain socketini kullanacak bağlantılar için kullandığımız bir bağlantı tipidir. Varsayılan CentOS 7 kurulumunda socket yolu şu şekildedir: /var/run/pgsql/s.PGSQL.5432

Eğer "psql" ile bağlanırken bu socketi kullanmak istiyorsak, sunucuya bağlıyken "-h" parametresi ile belirtebiliriz:
psql -h /var/run/pgsql/

host: SSL ile şifrelenmiş veya normal IPv4/IPv6 bağlantılar için kullanacağımız bir bağlantı tipidir. Aşağıdaki gibi bir IPv4 ile kullanımda geçerli olabilecek bir bağlantı tipidir:
psql -h <IP Adresi/Hostname>

hostssl: Sadece SSL ile şifrelenmiş bağlantılar için kullanılabilecek bir bağlantı tipidir.
hostnossl: Sadece SSL ile şifrelenmemiş bağlantılar için kullanılabilecek bir bağlantı tipidir.
 

Veritabanı:

all: Bağlantı yapacak kullanıcının tüm veritabanlarına bağlantısı için belirlenen metod uygulanır.

(Bu tanımlamaya "replication" dahil değildir.)

sameuser: Bağlantı yapılan veritabanın adı ile bağlantı yapacak kullanıcının adı aynı olduğunda bu metod uygulanır.

samerole: Bağlantı yapılan veritabanın adı ile bağlantı yapacak kullanıcının dahil olduğu herhangi bir grup adı ile aynı olduğunda bu metod uygulanır.

replication: PostgreSQL replikasyon protokolünde bulunan, sadece replikasyon baglantıları için kullanılan ve gerçekte olmayan bir veritabanıdır.

<veritabanı>,<veritabanı>: Birden fazla veritabanı için veritabanları isimlerinin arasına virgül konularak birden fazla veritabanı için tek kura tanımlanabilir.

@<dosya-adı>: Bu şekilde uygulandığında içerisinde veritabanlarının isimleri bulunan başka bir dosya kullanılabilir.
 

Kullanıcı

all: Belirlenen veritabanına bağlanmak isteyen tüm kullanıcılar için bu metod uygulanır.

<kullanıcı>,<kullanıcı>: Birden fazla kullanıcı için kullanıcı isimlerinin arasına virgül konularak birden fazla kullanıcı için tek kural tanımlanabilir.

+<grup>: Belirlenen grubun üyeleri için belirlenen metod geçerli olur


@<dosya>: Bu şekilde uygulandığında içerisinde kullanıcıların isimleri bulunan başka bir dosya kullanılabilir.

(Eğer bir dosya yolu belirtilmeden sadece dosya adı belirtilirse, bu dosya $PGDATA (varsayılan: /var/lib/pgsql/11/data) dizininde aranır.)
 

Adres/Ağ Maskesi

<adres>/<ağmaskesi>: Bu alanda alan adı veya IPv4/IPv6 adresi tercih edilebilir. Ağ maskesini belirlediğiniz IP aralığından gelen bağlantılar için belirleyebilirsiniz.

Bu alan için iki adet kullanım mevcuttur:
1) <adres>/<CIDR-mask>: IP-Adresi/32
2) <adres> <mask>: IP-Adresi 255.255.255.255
İkisinden birini tercih edebilirsiniz. Önerim birinci örnekteki gibi kullanmanızdır.
 Alan adının çözülmesi bir miktar zaman alacağı için; alan sahibi siz değilseniz hedef IP değişebileceği için IPv4/IPv6 tercih etmenizi öneriyorum.

all: Herhangi bir IP adresinden gelen bağlantılar için belirlenen metod uygulanır.

[.]<hostname>: "hostname" den gelen bağlantı istekleri için belirlenen metod uygulanır. 

samehost: Sunucunun kendi IP adreslerin gelen bağlantı istekleri için belirlenen metod uygulanır


samenet: Sunucunun bağlı olduğu bir ağ aralığında bulunan IP'den gelen bağlantı için belirlenen kural geçerli olur

 

Doğrulama Metodları:

trust: Belirlenen adresten gelen tüm bağlantıları parola gibi doğrulama metodlarını kullanmadan geçirir.

scram-sha-256: Kullanıcının parolasının "scram-sha-256" ile şifrelendiğini ve bu metod ile kontrol edilmesi gerektiğini söyler.

md5: Kullanıcının parolasının "md5" ile şifrelendiğini ve bu metod ile kontrol edilmesi gerektiğini söyler.

password: Bu metod ile şifrelenmeyen parolaları(plain-text) doğrulanabilir.

cert: Bağlantı için SSL sertifikası kullanılıyorsa bu şekilde doğrulama yapılmasını sağlar.

peer: Sistem üzerinde "psql" ile bağlanmak isteyen kullanıcının, Linux kullanıcı adı ile bağlanmak istediği kullanıcı adının aynı olup olmadığını kontrol eder.

pam: Parola doğrulaması için işletim sistemi tarafından sağlanan PAM(Pluggable Authentication Modules) kullanmasını söyler.

gss: Parola doğrulaması için GSSAPI kullanmasını söyler.

sspi:  Parola doğrulaması için SSPI kullanmasını söyler.

ldap: Parola doğrulaması için LDAP kullanmasını söyler.

radius: Parola doğrulaması için RADIUS kullanmasını söyler.


bsd: Parola doğrulaması için işletim sistemi tarafından sağlanan "BSD Authentication" servisini kullanmasını söyler

reject: Belirlenen kurallardan gelen istekler reddedilir.

ident: "pg_ident.conf" içersinde bağlantı için gelen isteği sağlayan kurallar aranır. Sunucudaki "pg_ident.conf" şı şekilde girilen verileri kontrol eder:
<map-ismi> <sistem-kullanıcıs> <veritabanı-kullanıcısı>
 

UYARI: "ident" bağlantı yöntemi bir kimlik doğrulama metodu değildir. 

 

Seçenekler:

map: "pg_ident.conf" dosyasında oluşturduğunuz "map" ile belirtilen ismi arar. Bu ismi bulduktan sonra belirlenen kullanıcı ile eşleşen bir kural varsa bu kuralı uygular.
 
map=<map-ismi>

(Bu seçenek sadece ident, peer, gssapi, sspi, cert metodları tarafından kullanılabilir.)

clientcert: Sadece "0" ve "1" değerlerini alabilir. Bağlanmak isteyen kullanıcının sertifikasını kontrol eder.

(Bu seçenek sertifika içerisindeki Common Name(CN) ile bağlanmak istenilen PostgreSQL kullanıcısının adının eşleşip eşleşmediğine bakar. Eğer istenilirse "map" seçeneği kullanılarak başka bir kullanıcıya bağlantı izni verilmesi sağlanılabilir.)
 

Öneriler:

1) Hiç bir zaman "password" veya "trust" metodlarını kullanmayın.

"password" metodu parolayı şifrelemeden olduğu gibi taşır. Araya girebilen bir saldırgan rahatlıkla parolayı ele geçirebilir.

"trust" metodu, belirlenen IP'den gelen bağlantıları parola sormadan kabul eder. Varlığını bile unutmamız gerekli. :-)

Ayrıca STIG(Security Technical Implemention Guide) kuralları bu iki metodun kullanılmasını tamamen yasaklıyor.

2) Eğer PostgreSQL 10 ve üzeri bir kurulumunuz varsa mutlaka "scram-sha-256" kullanın.

"md5" yeterince güvenli bir yöntem fakat daha kırılabilme ihtimali, "scram-sha-256" ya göre daha yüksek olan bir şifreleme yöntemi.

STIG tarafından kullanılması yasaklanmıyor fakat The Federal Information Processing Standard(FIPS) 140-2 tarafından yasaklanıyor.

3) Bağlantılarda mutlaka "hostssl" çeşidini, "cert" metodunu ve "clientcert=1" seçeneğini kullanın.

Bu metodu kullanmak için gerekli bilgi bir önceki blog yazımda mevcut: PostgreSQL'de Bağlantı için SSL Kullanımı

4) Her kullanıcının "LOGIN" yetkisi özel olmalı. Yani başka kullanıcılar tarafından paylaşılmamalı.

5) Veritabanına yapılan tüm bağlantıları mümkün olduğunda SSL ile şifreleyin. Bu işlem sırasında mutlaka "sslmode=verify-full" olarak kullanmalısınız. Man in The Middle(MITM) saldırılarını engelleyen iki seçenekten birisidir. (Diğeri "verify-ca" parametresidir fakat MITM için CA sertifikanızın güvenilirliğine bağlıdır.)

Diğer "sslmode" ları için şuraya bakabilirsiniz: PostgreSQL | SSL Support

Replikasyon için kullanılan "recovery.conf" dosyasında bulunan bir bağlantı örneği:
primary_conninfo = 'user=repuser passfile=''/var/lib/pgsql/.pgpass'' host=IP-Adresi port=5432 sslmode=verify-full sslcompression=0 krbsrvname=postgres target_session_attrs=any'

6) Replikasyon gibi bağlantı gerektiren durumlarda mutlaka ".pgpass" kullanın. (Bu dosyanın yetkisi "600" olmalıdır.)
 
touch ~/.pgpass

<Sunucu-IP>:<port>:<veritabanı>:<kullanıcı-adı>:<parola>

("Wildcard" için * kullanabilirsiniz.)


7 ) Sertifikalar için mutlaka Certificate Revocation List(CRL) kullanın.

Mevcut sertifikaların yönetimi için mutlaka bir CRL kullanın. Bu sayede bir sertifikayı iptal etmek istediğinizde bu listeye ekleme yapabilirsiniz.

Bu listeyi "postgresql.conf" dosyasında "ssl_crl_file" parametresini değiştirerek belirtebilirsiniz.


8-) Varsayılan olarak tüm oluşturulan veritabanlarında herkesin "CREATE" yetkisinin bulunması durumunu engellemeliyiz.

Bunun sebebi "CVE-2018-1058" e göre farklı şemalarda aynı isimli objelerin yaratılması durumunda, yaratılan objeyi daha yetkili bir kullanıcının çalıştırması sağlanarak istenilen yerlere veya istenilen komutu yetkisiz bir şekilde çalıştırmaktır.

Bu yetkiyi istemediğimiz veritabanına bağlandıktan sonra aşağıdaki sorgu ile kaldırabiliriz:
REVOKE CREATE ON SCHEMA public FROM PUBLIC;

Detaylı bir şekilde açıklamak gerekirse,

PostgreSQL bir sorgu çalıştığında önce sistem şeması olan "pg_catalog" şemasına, daha sonra "search_path" parametresine bakar. Bu parametre varsayılan olarak search_path = "$user, public" olarak tanımlıdır.
(Buradaki "$user", CURRENT_USER anlamına gelir.)

Yani bir sorgu çalıştığında önce "$user" şeması var ise bu şema içerisinde arama yapar. Eğer yoksa "public" şemasına bakar. Eğer ikisinde de yoksa hata verir.

Farklı şemalarda aynı isimlerde nesneler oluşturulabildiği için bazı veri tipleri şema belirtilmediği taktirde kullanılmak istenen nesnenin önüne geçebilir.  Örneğin "public" şemasında bulunan lower(varchar) tipinde bir fonksiyon,  sistem şemalarında bulunan bir lower(text) tipindeki fonksiyonunun önüne geçebilir.

Bu yüzden "search_path" parametremizde yazan şemalarda herkesin "CREATE" yetkisine sahip olmadığından emin olmalıyız.
 

Varsayılan ayarlarda kaldırılması düşünülmesi gereken soruları şu şekilde sorabiliriz ve kendi sistemimize göre cevaplayabiliriz:

1) Belirlenen veritabanında herkesin gerçekten "public" şemasını kullanmaya ihtiyacı var mı?
REVOKE USAGE ON SCHEMA public FROM PUBLIC;

Bu işlem uygulandıktan sonra istenmeyen kişinin "public" şemasının içerisindekileri görmesi engellenir. Fakat uğraşılırsa sistem tabloları sayesinde istenilirse bu şemanın içindekiler yine de görüntülenebilir.

2) Belirlenen veritabanında varsayılan olan "public" şeması kullanılmak zorunda mı?
DROP SCHEMA public CASCADE;

Eğer bu şekilde bir işlem yaptıysanız "postgresql.conf" daki "search_path" parametresinde değişiklik yaparak istediğiniz şemayı/şemaları tanımlayabilirsiniz. Bu adımdaki önceliğimiz herkes tarafından bilinen "public" şemasını ortadan kaldırmak.

3) Belirlenen veritabanında herkes geçici bir tablo oluşturabilmeli mi?
REVOKE TEMPORARY ON DATABASE gbd FROM PUBLIC;

4) Belirlenen veritabanında herkes bazı dilleri kullanarak fonksiyon oluşturmalı mı?
REVOKE USAGE ON LANGUAGE sql, plpgsql FROM PUBLIC;

5) Oluşturulan her "ROUTINE" için verilen çalıştırma yetkisi herkeste olmalı mı?
REVOKE EXECUTE ON ALL ROUTINES IN SCHEMA public FROM PUBLIC;

6) Oluşturulacak olan her "ROUTINE" için varsayılan verilen çalıştırma yetkisi herkeste olmalı mı?
ALTER DEFAULT PRIVILEGES IN SCHEMA public REVOKE EXECUTE ON ROUTINES FROM PUBLIC;

Bu tarz yetkileri kontrol etmek için Joe Conway tarafından yazılan projeye şuradan ulaşabilirsiniz: Joe Conway | Crunchy Check Accesss

Bu projenin kullanımı ile ilgili ileriki günlerde bir blog yazısı daha yazacağım. 

No comments

The author does not allow comments to this entry