dataiotech Labs
Runbook #62
mysql
SecuritySEV2 · DEGRADATION20-40 dakika / 20-40 minsmysql administrator / CREATE USER / GRANT OPTION / AUDIT_ADMIN

MySQL Security: Privilege Leak Triage, GRANT Audit & Least Privilege Enforcement

Identifying over-privileged users, wildcard (%) host grants, and unauthorized SUPER/GRANT ALL permissions via audit logs, safely migrating to MySQL 8.0 role-based least privilege architecture.

Related Metrics:M19M20
#mysql-security#grant#revoke#least-privilege#audit-log#roles#wildcard-host

Operational Safety & Risk Notice

This runbook contains operational intervention steps; several are write-impacting (KILL, terminate, DROP, RESIZE). Verify in a staging environment first, and apply sequentially in production with verification.

🟢 Salt Okunur (Read-Only)·🟡 Geri Alınabilir (Reversible)·🔴 Yüksek Etkili / Geri Alınamaz (High Impact)

1. Symptom & Signals

- 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. Business & Technical 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. Diagnosis & Root-Cause Analysis

🟢 Read-Only1. **Wildcard (`%`) host ile açık kullanıcıları listeleyin (En Tehlikeli):**
sql
SELECT user, host, Grant_priv, Super_priv, authentication_string 
FROM mysql.user 
WHERE host = '%' AND user NOT IN ('app_readonly', 'mysql.sys', 'mysql.session');
🟢 Read-Only2. **GRANT ALL veya SUPER yetkisi olanları tarayın:**
sql
SELECT user, host, Grant_priv, Super_priv, Create_user_priv, Reload_priv
FROM mysql.user 
WHERE Super_priv = 'Y' OR Grant_priv = 'Y';
🟢 Read-Only3. **Uygulama şemalarındaki tüm yetkileri dökün:**
sql
SELECT * FROM information_schema.user_privileges WHERE grantee LIKE "'%app%'";
SELECT * FROM information_schema.schema_privileges WHERE grantee LIKE "'%app%'";
🟢 Read-Only4. **Son 7 günde kim ne yapmış? (Audit log analizi):**
sql
-- 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 grant

4. Remediation & Action Plan

### 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ı)

bash
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)

sql
-- 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

sql
-- 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

sql
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. Verification & Health Checks

- Yeni yetkilerin atandığını doğrulayın:

sql
SHOW GRANTS FOR 'app_backend'@'10.0.1.%';
SELECT CURRENT_ROLE();

- Uygulama sunucusundan uçtan uca test yapın:

bash
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. Rollback & Contingency Plan

- Yetki daraltma sonrası uygulama hata alırsa, alınan SQL dump yedeği ile eski yetkileri geri yükleyin:

bash
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`.

Engineering Triad: Diagnostic Scripts

Diagnostic scripts recommended during this runbook triage to pinpoint the root cause:

All Scripts (100)
#27 · SQLMonitoring

MySQL InnoDB Buffer Pool Hit Ratio

Compares buffer pool read requests against disk reads to compute the hit ratio. Tells you whether memory sizing is sufficient.

#35 · SQLSecurity

Risky MySQL User Accounts and Excess Privileges

Finds accounts with no password, open host access, or dangerous privileges like SUPER and FILE. A baseline check for security audits.

More MYSQL Runbooks