امنیت و User Management
MySQL دسترسی را در سه سطح بررسی میکند:
۱۳.۱ مدل امنیتی MySQL
MySQL دسترسی را در سه سطح بررسی میکند:
- کاربر میتواند اتصال برقرار کند؟ (User + Host + Password)
- کاربر روی این Database/Table دسترسی دارد؟ (Privileges)
- کاربر روی این Column دسترسی دارد؟ (Column-level)
هویت کاربر = ترکیب User + Host. مثلاً 'app'@'localhost' با 'app'@'192.168.1.10' دو کاربر متفاوتاند.
۱۳.۲ مدیریت کاربران
SQL-- ساخت کاربر CREATE USER 'webapp'@'localhost' IDENTIFIED BY 'StrongPassword2026!'; CREATE USER 'webapp'@'192.168.1.%' IDENTIFIED BY 'StrongPassword2026!'; CREATE USER 'webapp'@'%' IDENTIFIED BY 'StrongPassword2026!'; -- ⚠️ خطرناک -- تنظیمات اضافی هنگام ساخت CREATE USER 'webapp'@'localhost' IDENTIFIED BY 'StrongPassword2026!' PASSWORD EXPIRE INTERVAL 90 DAY -- منقضی شدن خودکار PASSWORD HISTORY 5 -- نمیتواند ۵ پسورد قبلی را تکرار کند PASSWORD REUSE INTERVAL 365 DAY -- پسورد قدیمی حداقل ۱ سال FAILED_LOGIN_ATTEMPTS 5 -- پس از ۵ بار اشتباه قفل PASSWORD_LOCK_TIME 1; -- ۱ روز قفل -- تغییر پسورد ALTER USER 'webapp'@'localhost' IDENTIFIED BY 'NewPassword!'; -- نمایش کاربران SELECT User, Host, account_locked, password_expired FROM mysql.user ORDER BY User, Host; -- حذف کاربر DROP USER 'webapp'@'localhost'; -- قفل/باز کردن ALTER USER 'webapp'@'localhost' ACCOUNT LOCK; ALTER USER 'webapp'@'localhost' ACCOUNT UNLOCK;
۱۳.۳ Privileges – مدیریت دسترسی
سطوح Privilege
- Global: روی همه دیتابیسها (
*.*) - Database: روی یک دیتابیس (
db.*) - Table: روی یک جدول (
db.table) - Column: روی ستونهای خاص
- Routine: روی procedure/function
SQL-- کاربر اپلیکیشن (دسترسی محدود به یک دیتابیس) GRANT SELECT, INSERT, UPDATE, DELETE ON shop_db.* TO 'webapp'@'localhost'; -- کاربر فقط خواندن (برای گزارشگیری) GRANT SELECT ON shop_db.* TO 'reporter'@'localhost'; -- کاربر backup GRANT SELECT, RELOAD, LOCK TABLES, REPLICATION CLIENT, SHOW VIEW, EVENT, TRIGGER, PROCESS ON *.* TO 'backup'@'localhost'; -- DBA کامل (با احتیاط) GRANT ALL PRIVILEGES ON *.* TO 'dba'@'localhost' WITH GRANT OPTION; -- Privileges در سطح ستون GRANT SELECT (id, name, email), UPDATE (email) ON users.users TO 'limited'@'localhost'; -- نمایش privileges SHOW GRANTS FOR 'webapp'@'localhost'; -- بازپسگیری REVOKE DELETE ON shop_db.* FROM 'webapp'@'localhost'; REVOKE ALL PRIVILEGES, GRANT OPTION FROM 'webapp'@'localhost'; -- اعمال تغییرات (در نسخههای قدیمی) FLUSH PRIVILEGES;
privilegeهای مهم
| Privilege | توضیح |
|---|---|
| SELECT | خواندن |
| INSERT, UPDATE, DELETE | تغییر داده |
| CREATE, DROP | ساخت/حذف جدول |
| ALTER | تغییر ساختار |
| INDEX | ساخت ایندکس |
| EXECUTE | اجرای procedure |
| CREATE ROUTINE | ساخت procedure |
| TRIGGER | ساخت trigger |
| RELOAD | FLUSH |
| SUPER | تنظیمات گلوبال (خطرناک) |
۱۳.۴ Roles – گروهبندی Privileges
بهجای دادن privilege به هر کاربر، یک Role میسازیم و چندین کاربر آن را میگیرند:
SQL-- ساخت roleها CREATE ROLE 'app_read', 'app_write', 'app_admin'; -- تخصیص privilege به role GRANT SELECT ON shop_db.* TO 'app_read'; GRANT SELECT, INSERT, UPDATE, DELETE ON shop_db.* TO 'app_write'; GRANT ALL ON shop_db.* TO 'app_admin'; -- اختصاص role به کاربر GRANT 'app_read' TO 'reporter1'@'%', 'reporter2'@'%'; GRANT 'app_write' TO 'webapp'@'%'; GRANT 'app_admin' TO 'admin'@'localhost'; -- فعالسازی role (در سشن کاربر) SET ROLE 'app_write'; -- یا default ALTER USER 'webapp'@'%' DEFAULT ROLE 'app_write'; -- نمایش SHOW GRANTS FOR 'webapp'@'%' USING 'app_write';
۱۳.۵ SSL/TLS برای اتصالات رمزنگاری شده
اگر کلاینت و سرور MySQL در شبکههای مختلف هستند (مثل سرور MySQL در ابر و اپلیکیشن جای دیگر)، اتصال باید رمزنگاری شود.
Bash - تولید certificate# MySQL خودکار در نصب جدید certificate تولید میکند: ls /var/lib/mysql/*.pem # ca.pem, server-cert.pem, server-key.pem, ... # یا دستی با mysql_ssl_rsa_setup mysql_ssl_rsa_setup --datadir=/var/lib/mysql # my.cnf [mysqld] ssl-ca=/etc/mysql/ca.pem ssl-cert=/etc/mysql/server-cert.pem ssl-key=/etc/mysql/server-key.pem require_secure_transport=ON # اجبار همه کلاینتها به SSL
SQL-- بررسی SSL SHOW VARIABLES LIKE 'have_ssl'; -- اجبار کاربر به SSL ALTER USER 'webapp'@'%' REQUIRE SSL; -- یا قویتر: certificate خاص ALTER USER 'webapp'@'%' REQUIRE X509; -- بررسی اتصال فعلی SHOW STATUS LIKE 'Ssl_cipher';
۱۳.۶ جلوگیری از SQL Injection
SQL Injection معمولاً در سطح اپلیکیشن رخ میدهد، نه دیتابیس. اما در سطح DB هم میتوانیم سختگیری کنیم:
قانون طلایی: همیشه Prepared Statement
PHP - PDO// ❌ خطرناک - SQL Injection $id = $_GET['id']; $result = $pdo->query("SELECT * FROM users WHERE id = $id"); // ✅ امن - Prepared Statement $stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?"); $stmt->execute([$_GET['id']]); $users = $stmt->fetchAll(); // PDO با تنظیمات امن $pdo = new PDO($dsn, $user, $pass, [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_EMULATE_PREPARES => false, // مهم! PDO::MYSQL_ATTR_USE_BUFFERED_QUERY => true, ]);
Python - mysqlclient/PyMySQL# ❌ خطرناک cursor.execute(f"SELECT * FROM users WHERE id = {user_id}") # ✅ امن cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,)) # Django ORM بهصورت پیشفرض parameterized است User.objects.filter(id=user_id) User.objects.raw("SELECT * FROM users WHERE id = %s", [user_id])
کم کردن سطح حمله در DB
- کاربر اپلیکیشن نباید DROP TABLE داشته باشد
- FILE privilege را بهجز کاربران DBA ندهید (LOAD DATA INFILE)
- SUPER privilege را بهشدت محدود کنید
- local_infile روی Server را غیرفعال کنید مگر لازم باشد
۱۳.۷ Encryption at Rest – رمزنگاری داده روی دیسک
InnoDB از MySQL 5.7+ از tablespace encryption پشتیبانی میکند:
my.cnf[mysqld] # Keyring plugin (انتخاب یکی) early-plugin-load=keyring_file.so keyring_file_data=/var/lib/mysql-keyring/keyring # یا keyring_okv (Oracle Key Vault) برای enterprise # یا keyring_aws (AWS KMS)
SQL-- ساخت جدول رمزنگاری شده CREATE TABLE sensitive_data ( id INT PRIMARY KEY, secret VARCHAR(255) ) ENCRYPTION='Y'; -- تبدیل جدول موجود ALTER TABLE sensitive_data ENCRYPTION='Y'; -- بررسی SELECT TABLE_NAME, CREATE_OPTIONS FROM INFORMATION_SCHEMA.TABLES WHERE CREATE_OPTIONS LIKE '%ENCRYPTION%';
۱۳.۸ Audit Log
برای production نیاز به ثبت دسترسیها داریم. MySQL plugin اختصاصی نیاز دارد:
MariaDB Audit Plugin (built-in)
SQLINSTALL SONAME 'server_audit'; SET GLOBAL server_audit_logging = ON; SET GLOBAL server_audit_events = 'CONNECT,QUERY,TABLE'; SET GLOBAL server_audit_file_path = '/var/log/mysql/audit.log'; SET GLOBAL server_audit_file_rotate_size = 100000000; -- 100MB -- بررسی SHOW VARIABLES LIKE 'server_audit%';
Percona Audit Plugin
برای MySQL community، plugin Percona جایگزین خوبی است.
۱۳.۹ چکلیست امنیت Production
Security Checklist□ root با پسورد قوی، فقط از localhost قابل اتصال □ کاربر anonymous حذف شود □ دیتابیس test حذف شود □ هر اپلیکیشن کاربر اختصاصی با کمترین privilege □ FILE privilege فقط برای DBA □ SUPER privilege به حداقل □ bind-address روی 127.0.0.1 (مگر لازم باشد) □ skip-networking یا فقط شبکه داخلی □ SSL برای اتصالات راه دور □ require_secure_transport=ON اگر external □ password expiration و failed_login_attempts فعال □ Audit Log فعال □ Backupها رمزنگاری شده □ Patchهای امنیتی بروز □ نسخه MySQL پنهان (validate_password plugin) □ Firewall: فقط port 3306 از سرور اپلیکیشن □ Local infile غیرفعال □ Strict sql_mode فعال □ Performance Schema برای monitoring فعال
۱۳.۱۰ بررسی امنیتی
SQL - Audit Script-- 1. کاربران بدون پسورد SELECT User, Host FROM mysql.user WHERE authentication_string = '' OR authentication_string IS NULL; -- 2. کاربران با پسورد قابل پیشبینی -- (نمیتوان مستقیم چک کرد، اما کاربر anonymous را حذف کنید) SELECT User, Host FROM mysql.user WHERE User = ''; -- 3. کاربران از هر هاست (%) SELECT User, Host FROM mysql.user WHERE Host = '%'; -- 4. کاربران با ALL PRIVILEGES SELECT User, Host FROM mysql.user WHERE Super_priv = 'Y' OR Grant_priv = 'Y'; -- 5. دیتابیسهای test SHOW DATABASES LIKE 'test%'; -- 6. تنظیمات حساس SHOW VARIABLES LIKE 'local_infile'; -- باید OFF SHOW VARIABLES LIKE 'secure_file_priv'; -- باید مسیر محدود SHOW VARIABLES LIKE 'skip_networking'; SHOW VARIABLES LIKE 'bind_address';
۱۳.۱۱ خلاصه فصل
- هویت = User + Host؛ از
%فقط در صورت ضرورت استفاده کنید - Principle of Least Privilege: کمترین دسترسی لازم
- هر اپلیکیشن کاربر اختصاصی با privilege محدود به دیتابیس خود
- Roles مدیریت چندین کاربر را آسان میکنند
- SSL/TLS برای اتصالات راه دور اجباری
- Prepared Statement = جلوی SQL Injection
- Encryption at Rest برای دادههای حساس
- Audit Log در Production فعال باشد