Uygulama sunucun bir makinede, veritabanın başka bir makinede. Aradaki bağlantı şifresizse, o ağ trafiğini görebilen herkes hem sorgularını hem de dönen veriyi olduğu gibi okuyabilir — müşteri adresleri, sipariş kayıtları, hatta bazı durumlarda kimlik doğrulama bilgileri. MySQL'de SSL bağlantıyı zorunlu kılmak bu riski ortadan kaldırır ve yapılandırması sanılandan çok daha kısadır; asıl zorluk, "şifreli görünen" bir bağlantıyla gerçekten doğrulanmış bir bağlantı arasındaki farkı bilmektir.
Bu rehberde önce TLS'in MySQL'de neyi koruyup neyi korumadığını netleştireceğim; sonra mevcut durumu nasıl kontrol edeceğini, sertifikaları nasıl hazırlayacağını, sunucuda şifresiz bağlantıları nasıl tamamen reddedeceğini ve hesap bazında REQUIRE SSL ile REQUIRE X509 arasındaki farkı göstereceğim. Son bölümlerde istemci tarafındaki ssl-mode seçeneklerini ve en sık yapılan hataları ele alacağım — çünkü sunucuyu doğru ayarlayıp istemcide PREFERRED bırakmak, koruma sağladığını sanmakla eş değerdir.
TLS MySQL'de Neyi Korur, Neyi Korumaz#
TLS iki ayrı iş yapar ve bunlar birbirinden bağımsızdır. Birincisi şifrelemedir: trafiği okuyan biri veriyi anlamlandıramaz. İkincisi kimlik doğrulamadır: bağlandığın sunucunun gerçekten o sunucu olduğunu, araya girmiş bir makine olmadığını kanıtlar. MySQL'de bu ikisi ayrı ayarlarla kontrol edilir ve çoğu kurulumda yalnızca birincisi açıktır.
MySQL 8 istemcisi varsayılan olarak ssl-mode=PREFERRED ile çalışır. Bu, "sunucu TLS destekliyorsa şifreli bağlan, desteklemiyorsa şifresiz devam et" anlamına gelir ve sertifikayı hiç doğrulamaz. Yani trafiğin şifrelidir ama araya giren bir saldırgan kendi sertifikasını sunarak bağlantıyı kabul ettirebilir; üstelik TLS'i tamamen düşürüp şifresiz bağlanmaya zorlayabilir. Aşağıdaki tablo seviyeleri netleştiriyor:
| ssl-mode | Şifreleme | Sunucu doğrulama | Ne zaman kullanılır |
|---|---|---|---|
| DISABLED | Yok | Yok | Yalnızca soket üzerinden yerel bağlantı |
| PREFERRED | Varsa | Yok | Varsayılan, gerçek koruma sağlamaz |
| REQUIRED | Zorunlu | Yok | Şifreleme şart, sertifika kontrolü yok |
| VERIFY_CA | Zorunlu | Sertifika CA ile doğrulanır | Kendi CA'n varsa doğru seçim |
| VERIFY_IDENTITY | Zorunlu | CA artı sunucu adı doğrulanır | En güvenli, hostname eşleşmesi ister |
Buradaki kural şudur: gerçek koruma VERIFY_CA ile başlar. REQUIRED yalnızca şifreleme sağlar ve pasif dinlemeyi engeller, aktif araya girme saldırısını engellemez. Yerel Unix soketi üzerinden yapılan bağlantılarda ise TLS'e gerek yoktur; trafik zaten makineyi terk etmez.
Mevcut TLS Durumunu Kontrol Etmek#
İşe sunucunun TLS'i destekleyip desteklemediğini ve o anki bağlantının şifreli olup olmadığını ayırt etmekle başla. Bu ikisi farklı sorulardır.
-- Sunucu TLS için hangi dosyaları kullanıyor
SHOW VARIABLES LIKE '%ssl%';
-- ssl_ca /var/lib/mysql/ca.pem
-- ssl_cert /var/lib/mysql/server-cert.pem
-- ssl_key /var/lib/mysql/server-key.pem
-- Bu oturum şifreli mi
SHOW STATUS LIKE 'Ssl_cipher';
-- Boş dönerse bağlantın ŞİFRESİZ
-- TLS_AES_256_GCM_SHA384 gibi bir değer dönerse şifreli
Ssl_cipher boş dönüyorsa panik yapma; -h localhost ile bağlandığında MySQL soket kullanır ve TLS devreye girmez, bu normaldir. TCP üzerinden test etmek için adresi açıkça ver:
mysql -h 127.0.0.1 -P 3306 -u uygulama -p -e "SHOW STATUS LIKE 'Ssl_cipher';"
MySQL istemcisinde \s komutu (status) da özet bilgi verir ve SSL: satırında kullanılan şifreleme takımını gösterir. Hangi kullanıcıların şu anda şifresiz bağlandığını topluca görmek istersen performance_schema işini görür:
SELECT t.processlist_user AS kullanici, t.processlist_host AS host,
COALESCE(NULLIF(sbt.variable_value, ''), 'SIFRESIZ') AS sifreleme
FROM performance_schema.threads t
LEFT JOIN performance_schema.status_by_thread sbt
ON sbt.thread_id = t.thread_id AND sbt.variable_name = 'Ssl_cipher'
WHERE t.processlist_id IS NOT NULL AND t.processlist_user IS NOT NULL;
Bu sorgu, TLS'i zorunlu kılmadan önce hangi bağlantıların kırılacağını görmenin en pratik yoludur. Zorunluluğu açmadan önce mutlaka bu listeyi al.
Sertifikaları Hazırlamak#
MySQL 5.7 ve sonrası, ilk başlatmada veri dizininde kendi imzalı sertifikalarını otomatik oluşturur. Dosyalar genellikle /var/lib/mysql altındadır:
ls -l /var/lib/mysql/*.pem
# ca-key.pem ca.pem client-cert.pem client-key.pem
# private_key.pem public_key.pem server-cert.pem server-key.pem
Bu otomatik sertifikalar şifreleme için yeterlidir ve REQUIRED seviyesinde kullanılabilir. Ancak VERIFY_IDENTITY kullanmak istersen sorun çıkar: otomatik üretilen sunucu sertifikasının ortak adı (CN) genellikle MySQL_Server_..._Auto_Generated_Server_Certificate şeklindedir ve gerçek sunucu adıyla eşleşmez. Bu yüzden ciddi bir kurulumda kendi sertifikanı üretmelisin.
Kendi CA'nı ve sunucu sertifikanı oluşturmak birkaç komuttan ibarettir:
sudo mkdir -p /etc/mysql/ssl && cd /etc/mysql/ssl
# 1) Kendi sertifika otoritenizi oluşturun (10 yıl)
sudo openssl genrsa 4096 > ca-key.pem
sudo openssl req -new -x509 -nodes -days 3650 -key ca-key.pem -out ca.pem \
-subj "/C=TR/O=Firmaniz/CN=Firmaniz MySQL CA"
# 2) Sunucu anahtarı ve istek — CN, istemcinin bağlanacağı ADLA aynı olmalı
sudo openssl req -newkey rsa:4096 -nodes -days 825 \
-keyout server-key.pem -out server-req.pem \
-subj "/C=TR/O=Firmaniz/CN=db01.firmaniz.com"
# 3) CA ile imzala
sudo openssl x509 -req -in server-req.pem -days 825 \
-CA ca.pem -CAkey ca-key.pem -set_serial 01 -out server-cert.pem
# 4) İzinler — özel anahtarı yalnızca mysql okuyabilsin
sudo chown mysql:mysql /etc/mysql/ssl/*.pem
sudo chmod 600 /etc/mysql/ssl/*-key.pem
sudo chmod 644 /etc/mysql/ssl/ca.pem /etc/mysql/ssl/server-cert.pem
Üçüncü adımdaki CN değeri kritiktir: VERIFY_IDENTITY kullanacaksan istemcinin bağlanmak için yazdığı adla birebir aynı olmalıdır. IP ile bağlanıyorsan CN yerine SAN alanına IP eklemen gerekir, aksi hâlde doğrulama başarısız olur. Sertifikaların süresini takvimine not et; süresi dolan bir sertifika, REQUIRE SSL tanımlı hesaplarda tüm uygulamayı bir anda durdurur.
Sunucuda TLS'i Etkinleştirmek ve Şifresiz Bağlantıyı Reddetmek#
Sertifikalar hazır olduğunda sunucu yapılandırmasına üç satır eklersin. Dördüncü satır ise asıl zorunluluğu getirir.
# /etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]
ssl_ca = /etc/mysql/ssl/ca.pem
ssl_cert = /etc/mysql/ssl/server-cert.pem
ssl_key = /etc/mysql/ssl/server-key.pem
# Yalnızca TLS 1.2 ve 1.3 kabul et
tls_version = TLSv1.2,TLSv1.3
# Şifresiz TCP bağlantılarını TAMAMEN reddet
require_secure_transport = ON
require_secure_transport = ON ayarı, sunucu düzeyinde şifresiz her TCP bağlantısını reddeder ve hata mesajı nettir:
ERROR 3159 (HY000): Connections using insecure transport are prohibited
while --require_secure_transport=ON.
Bu ayar Unix soketi ve named pipe bağlantılarını etkilemez; yerel yönetim erişimin kesilmez. Yine de değişikliği uygulamadan önce, önceki bölümdeki sorguyla hangi bağlantıların şifresiz olduğunu listelemeyi unutma — aksi hâlde servisi yeniden başlattığında uygulama bir anda bağlanamaz hâle gelir.
sudo systemctl restart mysql
# Şifresiz bağlanmayı deneyerek reddedildiğini doğrula
mysql -h 127.0.0.1 -u uygulama -p --ssl-mode=DISABLED -e "SELECT 1;"
# Yukarıdaki 3159 hatasını vermeli
# Şifreli bağlantı çalışmalı
mysql -h 127.0.0.1 -u uygulama -p --ssl-mode=REQUIRED -e "SHOW STATUS LIKE 'Ssl_cipher';"
tls_version satırını atlamak yaygın bir eksikliktir; eski TLS sürümleri hâlâ kabul ediliyorsa şifreleme zayıflar. TLS 1.2 ve 1.3 ile sınırlamak bugün güvenli bir taban oluşturur ve modern istemcilerin hepsi bunları destekler.
Hesap Bazında REQUIRE SSL ve X509#
Sunucu düzeyindeki zorunluluk hepsini kapsayan bir anahtardır; bazen ise yalnızca belirli hesaplarda şifreleme istersin — örneğin uzaktan bağlanan raporlama kullanıcısında şart koşup yerel bakım hesabını serbest bırakmak gibi. Bu, hesap bazında tanımlanır.
-- Bu hesap yalnızca şifreli bağlanabilsin
ALTER USER 'uygulama'@'185.12.34.60' REQUIRE SSL;
-- Daha katısı: geçerli, CA'mız tarafından imzalanmış bir istemci sertifikası da istensin
ALTER USER 'yonetici'@'185.12.34.61' REQUIRE X509;
-- En katısı: sertifikanın sahibi ve imzalayanı da eşleşsin
ALTER USER 'entegrasyon'@'%' REQUIRE
SUBJECT '/C=TR/O=Firmaniz/CN=entegrasyon-istemci'
ISSUER '/C=TR/O=Firmaniz/CN=Firmaniz MySQL CA';
-- Zorunluluğu kaldırmak için
ALTER USER 'eski'@'%' REQUIRE NONE;
REQUIRE SSL yalnızca bağlantının şifreli olmasını ister, istemcinin kim olduğunu sorgulamaz. REQUIRE X509 ise istemcinin de senin CA'n tarafından imzalanmış bir sertifika sunmasını şart koşar — yani parolaya ek olarak ikinci bir kimlik kanıtı ister. Sunucular arası entegrasyonlarda bu ciddi bir güvenlik kazancıdır çünkü parola çalınsa bile sertifika olmadan bağlanılamaz.
İstemci sertifikası üretmek, sunucu sertifikasıyla aynı akışı izler:
sudo openssl req -newkey rsa:4096 -nodes -days 825 \
-keyout client-key.pem -out client-req.pem \
-subj "/C=TR/O=Firmaniz/CN=entegrasyon-istemci"
sudo openssl x509 -req -in client-req.pem -days 825 \
-CA ca.pem -CAkey ca-key.pem -set_serial 02 -out client-cert.pem
Hangi hesapta hangi şartın tanımlı olduğunu görmek için SHOW GRANTS çıktısına bakarsın; şart varsa satırın sonunda REQUIRE SSL ya da REQUIRE X509 görünür. Yetki modelinin geri kalanını MySQL kullanıcı ve yetki yönetimi yazısında bulabilirsin.
İstemci Tarafı ve Uygulama Bağlantıları#
Sunucu tarafı hazır olduğunda iş istemciye kalır ve asıl atlanan kısım burasıdır. Komut satırı istemcisinde CA dosyasını göstermen ve modu yükseltmen gerekir:
mysql -h db01.firmaniz.com -u uygulama -p \
--ssl-mode=VERIFY_IDENTITY \
--ssl-ca=/etc/mysql/ssl/ca.pem
Bunu her seferinde yazmamak için istemci yapılandırma dosyasına eklersin:
# ~/.my.cnf ya da /etc/mysql/conf.d/ssl-client.cnf
[client]
ssl-mode = VERIFY_CA
ssl-ca = /etc/mysql/ssl/ca.pem
Uygulama tarafında ayar, kullandığın sürücüye göre değişir ama mantık aynıdır: CA dosyasının yolunu vermek ve doğrulamayı açmak. PHP'nin PDO sürücüsünde bu, bağlantı seçenekleri dizisiyle yapılır ve PDO::MYSQL_ATTR_SSL_CA ile PDO::MYSQL_ATTR_SSL_VERIFY_SERVER_CERT sabitlerini kullanır. WordPress gibi hazır uygulamalarda ise genellikle wp-config.php içine MYSQL_CLIENT_FLAGS sabiti eklenir. Ne kullanırsan kullan, işi bitirdikten sonra doğrulamayı atlama:
-- Uygulamanın açtığı oturumda çalıştır
SELECT CURRENT_USER(), (SELECT variable_value FROM performance_schema.session_status
WHERE variable_name='Ssl_cipher') AS sifreleme;
Araya ProxySQL gibi bir katman koyduysan iki ayrı bacağı ayrı ayrı düşünmelisin: uygulama ile ProxySQL arasındaki bağlantı ve ProxySQL ile MySQL arasındaki bağlantı. İkisini de şifrelemen gerekir; birini açık bırakmak zinciri kırar. Bu kurgunun ayrıntıları için ProxySQL ile veritabanı yük dengeleme yazısına bakabilirsin. Aynı şey replikasyon için de geçerlidir: master ile replika arasındaki trafik varsayılan olarak şifresizdir ve SOURCE_SSL=1 ile açılır; MySQL replikasyon kurulumu yazısında bu bacağı da unutma.
Sık Yapılan Hatalar#
Birinci hata, PREFERRED modunu koruma sanmaktır. Bağlantı şifreli görünür, Ssl_cipher doluysa "tamam" dersin; oysa sertifika doğrulanmadığı için araya giren bir makine kendi sertifikasını sunup trafiği okuyabilir. En az VERIFY_CA kullan.
İkinci hata, require_secure_transport = ON ayarını hangi bağlantıların etkileneceğini kontrol etmeden açmaktır. Cron betikleri, eski entegrasyonlar ve izleme araçları genellikle şifresiz bağlanır ve hepsi aynı anda kırılır. Önce mevcut oturumları listele, sonra açık.
Üçüncü hata, sertifika süresini takip etmemektir. Süresi dolan bir sunucu sertifikası, doğrulama yapan tüm istemcileri anında keser ve hata mesajı ilk bakışta bunu söylemez. Sertifika bitiş tarihini bir hatırlatıcıya yaz ve düzenli kontrol et:
openssl x509 -in /etc/mysql/ssl/server-cert.pem -noout -enddate
# notAfter=Aug 25 10:12:33 2028 GMT
Dördüncü hata, sertifika dosyalarının izinlerini yanlış vermektir. server-key.pem dosyası mysql kullanıcısına ait ve 600 izinli olmalıdır; başkası okuyabiliyorsa MySQL bazı sürümlerde dosyayı kullanmayı reddeder ve TLS sessizce devre dışı kalır. Yeniden başlatma sonrası SHOW VARIABLES LIKE 'ssl%' çıktısının dolu olduğunu daima doğrula.
Beşinci hata, VERIFY_IDENTITY kullanırken sunucu adı ile sertifika CN'sinin uyuşmadığını fark etmemektir. IP ile bağlanıp CN'de alan adı yazıyorsa doğrulama başarısız olur ve hata mesajı yeterince açık değildir. Ya sertifikaya SAN alanı ekle ya da istemcinin bağlandığı adı sertifikadakiyle eşitle.
Son bir not: TLS'in CPU maliyeti modern donanımda ihmal edilebilir seviyededir, çünkü el sıkışma yalnızca bağlantı kurulurken bir kez yapılır. Ancak her sorguda yeni bağlantı açan bir uygulamada bu maliyet çarpar; kalıcı bağlantı ya da bağlantı havuzu kullanmak hem TLS maliyetini hem de genel gecikmeyi düşürür.
Sıkça Sorulan Sorular#
MySQL bağlantımın şifreli olup olmadığını nasıl kontrol ederim#
Bağlandığın oturumda SHOW STATUS LIKE 'Ssl_cipher'; komutunu çalıştır. Değer boş dönüyorsa bağlantı şifresizdir; TLS_AES_256_GCM_SHA384 gibi bir şifreleme takımı görüyorsan şifrelidir. MySQL istemcisinde \s komutu da aynı bilgiyi özet ekranda gösterir. Yalnızca localhost ile bağlandığında boş dönmesi normaldir, çünkü o durumda Unix soketi kullanılır ve trafik makineyi terk etmez.
require_secure_transport açmak siteyi keser mi#
Şifresiz bağlanan her uygulamayı keser, bu yüzden açmadan önce mevcut bağlantıları kontrol etmelisin. Uygulaman zaten TLS ile bağlanıyorsa hiçbir etkisi olmaz. Yerel soket üzerinden gelen bağlantılar bu ayardan etkilenmez, dolayısıyla sunucudaki yönetim erişimin kaybolmaz. En güvenli yöntem, önce hesap bazında REQUIRE SSL uygulayıp bir süre gözlemlemek, sonra sunucu genelinde zorunluluğa geçmektir.
Let's Encrypt sertifikasını MySQL için kullanabilir miyim#
Teknik olarak kullanabilirsin ama çoğu senaryoda gereksizdir. Let's Encrypt sertifikaları tarayıcıların güvendiği bir zincirden gelir; MySQL istemcileri ise tarayıcı güven deposunu kullanmaz, sen hangi CA'yı gösterirsen ona güvenir. Bu yüzden kendi CA'nı oluşturmak hem daha basit hem de daha uzun ömürlüdür. Ayrıca Let's Encrypt sertifikaları kısa ömürlü olduğu için yenileme sonrası MySQL'i yeniden yükleme ihtiyacı doğar.
İstemci sertifikası zorunlu olmalı mı#
Çoğu web uygulaması için gerekli değildir; REQUIRE SSL ile şifreleme sağlamak ve güçlü parola kullanmak yeterli koruma verir. İstemci sertifikası, yani REQUIRE X509, parolaya ek ikinci bir kimlik kanıtı ister ve sunucular arası entegrasyonlar, ödeme sistemleri ya da veritabanına dışarıdan erişen iş ortakları gibi senaryolarda anlamlıdır. Sertifika yönetimi ek yük getirdiği için ihtiyaç duymadığın yerde uygulama.
TLS kullanmak veritabanını yavaşlatır mı#
Modern işlemcilerde şifreleme maliyeti ihmal edilebilir düzeydedir; asıl maliyet, bağlantı kurulurken bir kez yapılan TLS el sıkışmasındadır. Kalıcı bağlantı veya bağlantı havuzu kullanan uygulamalarda bu maliyet neredeyse görünmez olur. Her istekte yeni bağlantı açan mimarilerde ise fark hissedilebilir; bu durumda çözüm TLS'i kapatmak değil, araya bir bağlantı havuzu koymaktır.
Replikasyon trafiği de şifrelenmeli mi#
Evet, özellikle master ve replika farklı makinelerde veya farklı ağlardaysa. Replikasyon trafiği binary log içeriğini taşır, yani veritabanındaki tüm değişiklikleri açık hâlde ağa verir. Şifrelemek için replika tarafında bağlantı tanımına SOURCE_SSL=1 eklemen ve CA dosyasını göstermen yeterlidir. Aynı veri merkezinde, izole bir özel ağdaki kurulumlarda risk düşüktür ama iki nokta arasında internet varsa bu ayar zorunlu sayılmalıdır.
Kapanış#
MySQL'de TLS, açılması on beş dakika süren ama etkisi kalıcı olan güvenlik önlemlerinden biridir. Aklında tutman gereken dört alışkanlık şu: istemcide PREFERRED modunu koruma sayma ve en az VERIFY_CA kullan, require_secure_transport = ON ayarını açmadan önce mevcut şifresiz bağlantıları listele, sertifika bitiş tarihlerini takvime yaz ve server-key.pem dosyasının izinlerini 600 olarak tut. Bir de zincirin tamamını düşün — uygulama, proxy, replikasyon; birini şifresiz bırakmak diğerlerinin kazancını götürür.
Bu ayarları yapabilmek için MySQL yapılandırma dosyasına ve sertifika dizinine erişimin olması gerekir; VDS ve bulut sunucu paketlerimiz tam root erişimiyle bunu mümkün kılar. Sertifika üretimi, yenileme takibi ve sertleştirme adımlarını kendin üstlenmek istemiyorsan sunucu yönetimi hizmetimiz bu işi devralır; web tarafındaki sertifikalar için SSL sayfamıza da göz atabilirsin.