MySQL Güvenlik: Yetki Sızıntısı, GRANT Audit ve En Az Yetki Prensibi (Least Privilege)
Aşırı yetkili (over-privileged) MySQL kullanıcılarını, wildcard (%) host erişimlerini ve kontrolsüz SUPER/GRANT ALL izinlerini audit logları ile tespit edip, MySQL 8.0 rolleri ile en az yetki prensibine güvenle indirme.
Operasyonel Güvenlik ve Risk Uyarısı
Bu runbook müdahale adımları içerir; birçok adım yazma etkilidir (KILL, terminate, DROP, RESIZE). Önce test ortamında deneyin, üretimde adımları sırayla ve doğrulayarak uygulayın.
1. Belirti / Symptom
- SOC / Güvenlik denetiminde `GRANT ALL ON *.*` veya `SUPER` yetkisine sahip servis kullanıcıları uyarısı.
- `host = '%'` (tüm dünyadan erişilebilir) tanımlı uygulama kullanıcıları tespit edildi.
- Audit loglarında (`mysql.audit_log` veya Percona Audit) yetkisiz `CREATE USER`, `GRANT` veya DDL anomalileri.
2. Etki / Impact
P2 - Güvenlik & Uyumluluk Riski. Bir web uygulamasının SQL Injection veya kimlik bilgisi sızıntısına uğraması durumunda saldırgan tüm veritabanını silebilir (`DROP DATABASE`), yeni yönetici ekleyebilir veya kritik verileri sızdırabilir.
3. Teşhis / Diagnosis
SELECT user, host, Grant_priv, Super_priv, authentication_string
FROM mysql.user
WHERE host = '%' AND user NOT IN ('app_readonly', 'mysql.sys', 'mysql.session');SELECT user, host, Grant_priv, Super_priv, Create_user_priv, Reload_priv
FROM mysql.user
WHERE Super_priv = 'Y' OR Grant_priv = 'Y';SELECT * FROM information_schema.user_privileges WHERE grantee LIKE "'%app%'";
SELECT * FROM information_schema.schema_privileges WHERE grantee LIKE "'%app%'";-- Enterprise Audit tablosu
SELECT user, host, command_class, sql_text, timestamp
FROM mysql.audit_log
WHERE timestamp > NOW() - INTERVAL 7 DAY
AND command_class IN ('grant', 'create_user', 'drop_user', 'revoke')
ORDER BY timestamp DESC;
-- Percona Audit için log taraması:
-- cat /var/log/mysql/audit.log | grep -i grant4. Müdahale / Response
### ALTIN KURAL: ÖNCE YENİ ROLE OLUŞTURUN VE TEST EDİN, SONRA ESKİ YETKİYİ REVOKE EDİN!
Doğrudan canlıda REVOKE çalıştırmak production uygulamalarını anında çökertebilir.
---
#### Adım 1: Mevcut Yetkileri Yedekleyin (Rollback Planı)
mysqldump -u root -p mysql --tables user db tables_priv columns_priv procs_priv default_roles role_edges > /backups/mysql_privs_$(date +%F).sql
mysqlpump -u root -p --users > /backups/grants_$(date +%F).sql
# veya tekil kullanıcı için:
SHOW GRANTS FOR 'app_user'@'10.0.0.%' \G---
#### Adım 2: MySQL 8.0 Rol Tabanlı Yeni Yapı Oluşturun (En Az Yetki)
-- Yeni standart kurumsal roller
CREATE ROLE IF NOT EXISTS app_readonly, app_readwrite, app_dba_read;
-- Readonly: Sadece SELECT
GRANT SELECT ON mydb.* TO app_readonly;
-- ReadWrite: SELECT, INSERT, UPDATE, DELETE (DDL yok!)
GRANT SELECT, INSERT, UPDATE, DELETE ON mydb.* TO app_readwrite;
-- DBA Read: Teşhis ve izleme (Tüm DB'lerde SELECT + SHOW)
GRANT SELECT ON *.* TO app_dba_read;
GRANT SHOW DATABASES, PROCESS, REPLICATION CLIENT ON *.* TO app_dba_read;
-- Rolü kullanıcıya atayın ve varsayılan yapın
GRANT app_readwrite TO 'app_backend'@'10.0.1.%';
SET DEFAULT ROLE app_readwrite TO 'app_backend'@'10.0.1.%';
-- Fazla yetkili eski global izinleri kaldırın
REVOKE SUPER, GRANT OPTION ON *.* FROM 'app_backend'@'10.0.1.%';---
#### Adım 3: Wildcard (%) Host Kapatma
-- Kötü: 'deploy'@'%'
-- İyi: 'deploy'@'10.0.1.10', 'deploy'@'10.0.1.11'
-- Yeni host-specific kullanıcı oluşturun
CREATE USER 'deploy'@'10.0.1.10' IDENTIFIED BY 'GucluSifre2026!';
GRANT app_readwrite TO 'deploy'@'10.0.1.10';
SET DEFAULT ROLE app_readwrite TO 'deploy'@'10.0.1.10';
-- Eski % kullanıcısını silmeden önce app testi yapın, ardından kaldırın:
DROP USER 'deploy'@'%';---
#### Adım 4: Anonim ve Test Kullanıcılarını Temizleyin
SELECT user, host FROM mysql.user WHERE user = '' OR user LIKE 'test%';
DROP USER IF EXISTS ''@'localhost', ''@'127.0.0.1';
DROP USER IF EXISTS 'test'@'%';5. Doğrulama / Verification
- Yeni yetkilerin atandığını doğrulayın:
SHOW GRANTS FOR 'app_backend'@'10.0.1.%';
SELECT CURRENT_ROLE();- Uygulama sunucusundan uçtan uca test yapın:
mysql -h mysql-primary -u app_backend -p -e "SHOW TABLES; INSERT INTO test_triage VALUES (1); DROP TABLE test_triage;"
# INSERT başarılı olmalı, DROP yetki hatası (Access denied) vermelidir.- Audit log'da REVOKE işlemlerinin kaydedildiğini teyit edin.
6. Geri Alma / Rollback
- Yetki daraltma sonrası uygulama hata alırsa, alınan SQL dump yedeği ile eski yetkileri geri yükleyin:
mysql -u root -p mysql < /backups/mysql_privs_2026-11-05.sql
FLUSH PRIVILEGES;- **Önleme Checklist — dbaguide Standardı:**
- [ ] `sql_require_primary_key = ON` (yetkisiz tablo oluşturmayı zorlaştırır).
- [ ] `default_authentication_plugin = caching_sha2_password` zorunlu mu?
- [ ] Haftalık cron işi: `pt-show-grants --all > /audit/grants_$(date +%F).sql` alınıp versiyon kontrolünde saklanıyor mu?
- [ ] ProxySQL veya MySQL Enterprise Firewall ile `GRANT ALL` engellendi mi?
- [ ] SOC2 / ISO 27001 gereği 90 günde bir `mysql.user` tablosu audit review yapılıyor mu?
- [ ] Parola politikaları: `password_history = 5` ve `password_reuse_interval = 180`.
Mühendislik Triadı: Doğrudan Teşhis Scriptleri
Bu runbook kriz müdahalesinde darboğazı tespit etmek için önerilen hazır SQL / Shell scriptleri:
MySQL InnoDB Buffer Pool İsabet Oranı
Buffer pool okuma isteklerine karşı diskten yapılan okumaları karşılaştırarak isabet oranını hesaplar. Bellek ayarının yeterli olup olmadığını gösterir.
MySQL Riskli Kullanıcı Hesapları ve Yüksek Yetkiler
Şifresiz, her host'tan erişebilen ve SUPER/FILE gibi tehlikeli yetkilere sahip hesapları bulur. Güvenlik denetimi için temel kontroldür.