وقتی صحبت از امنیت MySQL میشود، خیلی از مدیران سرور فقط به فکر رمز عبور قوی هستند. اما واقعیت این است که نصب پیشفرض MySQL شامل چند ضعف امنیتی جدی است که اگر به آنها رسیدگی نکنید، سرور شما در معرض نفوذ قرار میگیرد. کاربر ناشناس (anonymous user)، اتصال راهدور باز و کاربران با دسترسیهای بیش از حد نیاز، سه مشکل رایجی هستند که در بسیاری از سرورهای تولیدی دیده میشوند. در این مقاله قدمبهقدم یاد میگیرید که این مشکلات را برطرف کنید و یک پایگاه داده امن و مقاوم تحویل بگیرید.
چرا امنیت MySQL در نصب پیشفرض ضعیف است؟
MySQL بعد از نصب، بهصورت پیشفرض روی localhost و با کاربر root (بدون رمز یا با رمز ساده) اجرا میشود. علاوه بر این، اسکریپت نصب، یک کاربر ناشناس با نام خالی ایجاد میکند که هر کسی بتواند بدون احراز هویت به پایگاه داده متصل شود. این وضعیت در محیط توسعه شاید قابل قبول باشد، اما در سرور تولیدی، یک فاجعه امنیتی است.
بسیاری از حملات موفق به MySQL، نه از طریق باگهای نرمافزاری، بلکه از طریق پیکربندی نادرست رخ میدهند. مهاجم با یک اسکن ساده پورت، سرویس MySQL را روی پورت 3306 پیدا میکند و اگر کاربر ناشناس فعال باشد یا root رمز ضعیفی داشته باشد، کار تمام است. بنابراین اولین قدم برای امنیت MySQL، پاکسازی نصب پیشفرض است.
بررسی وضعیت فعلی سرور
قبل از هر تغییری، وضعیت فعلی را بررسی کنید. با SSH به سرور متصل شوید و دستور زیر را اجرا کنید:
mysql -u root -p
سپس این کوئری را بزنید تا لیست کاربران را ببینید:
SELECT user, host, authentication_string FROM mysql.user;
خروجی معمولاً شامل کاربر root با host های مختلف (localhost، 127.0.0.1، ::1) و یک کاربر با نام خالی (user='') است. آن کاربر خالی همان کاربر ناشناس است که باید حذف شود.
حذف کاربر ناشناس و پاکسازی پایگاههای داده پیشفرض
کاربر ناشناس به هر کسی که از هر هاستی متصل شود، اجازه ورود بدون رمز میدهد. این یعنی اگر MySQL روی پورت 3306 از بیرون در دسترس باشد، هر کسی میتواند وارد شود. برای حذف این کاربر، داخل محیط mysql دستور زیر را اجرا کنید:
DROP USER ''@'localhost';
DROP USER ''@'hostname';
اگر نام هاست دقیق را نمیدانید، از کوئری قبلی استفاده کنید و host مربوط به کاربر خالی را پیدا کنید. سپس دستور DROP را با همان host اجرا کنید.
بعد از حذف کاربر ناشناس، پایگاههای داده پیشفرض test و test\_% را هم حذف کنید. این پایگاهها بهصورت پیشفرض برای همه کاربران قابل دسترسی هستند و میتوانند برای پر کردن دیسک یا سوءاستفاده مورد استفاده قرار گیرند:
DROP DATABASE IF EXISTS test;
DROP DATABASE IF EXISTS test\_%;
در نهایت، تغییرات را اعمال کنید:
FLUSH PRIVILEGES;
اشتباه رایج: فراموشکردن FLUSH PRIVILEGES
بعد از هر تغییر در جدول mysql.user یا دستورات GRANT/REVOKE، باید FLUSH PRIVILEGES اجرا شود تا MySQL جدولهای مجوز را دوباره بارگذاری کند. اگر این کار را نکنید، تغییرات تا ریاستارت سرویس اعمال نمیشوند و ممکن است فکر کنید کارتان انجام شده در حالی که هنوز کاربر ناشناس فعال است.
محدودکردن اتصال راهدور به MySQL
یکی از مهمترین تصمیمها در امنیت MySQL این است که سرویس روی چه آدرسی گوش دهد. اگر برنامه شما روی همان سرور اجرا میشود (مثلاً PHP-FPM و MySQL روی یک سرور)، نیازی به اتصال راهدور نیست و باید MySQL را فقط روی localhost ببندید.
فایل پیکربندی اصلی MySQL معمولاً در مسیر /etc/mysql/mysql.conf.d/mysqld.cnf (در اوبونتو) یا /etc/my.cnf (در CentOS) قرار دارد. خط مربوط به bind-address را پیدا کنید و آن را بهصورت زیر تغییر دهید:
bind-address = 127.0.0.1
اگر این خط وجود ندارد، آن را در بخش [mysqld] اضافه کنید. بعد از تغییر، سرویس را ریاستارت کنید:
sudo systemctl restart mysql
حالا با دستور زیر بررسی کنید که MySQL فقط روی localhost گوش میدهد:
sudo netstat -tlnp | grep 3306
خروجی باید فقط شامل 127.0.0.1:3306 باشد. اگر آدرس 0.0.0.0 یا آدرس IP عمومی را میبینید، یعنی هنوز از بیرون در دسترس است.
اگر واقعاً به اتصال راهدور نیاز دارید
در برخی معماریها (مثلاً چند سرور برنامه که به یک پایگاه داده مرکزی وصل میشوند) اتصال راهدور ضروری است. در این حالت، بهجای بازکردن کامل پورت، این کارها را انجام دهید:
- بهجای 0.0.0.0، آدرس IP خصوصی سرور را در bind-address قرار دهید.
- از فایروال (iptables یا ufw) فقط IP سرورهای برنامه را مجاز کنید.
- اتصال را روی یک پورت غیراستاندارد اجرا نکنید؛ این کار امنیت واقعی ایجاد نمیکند و فقط باعث پیچیدگی میشود.
- حتماً SSL/TLS را برای اتصال راهدور فعال کنید تا ترافیک رمزنگاری شود.
به یاد داشته باشید که هر اتصال راهدوری سطح حمله را افزایش میدهد. اگر راهی برای اجتناب از آن دارید، اجتناب کنید.
ساخت کاربر با حداقل دسترسی (اصل Least Privilege)
یکی از اصول طلایی امنیت MySQL این است که هر برنامه یا توسعهدهنده فقط دسترسیهایی را داشته باشد که واقعاً به آن نیاز دارد. استفاده از کاربر root برای اتصال برنامهها، یک اشتباه بزرگ است. اگر برنامه شما هک شود، مهاجم با دسترسی root میتواند همه پایگاههای داده را حذف کند.
برای ساخت یک کاربر محدود، ابتدا تصمیم بگیرید برنامه شما به چه دسترسیهایی نیاز دارد. یک برنامه وب معمولی فقط به SELECT، INSERT، UPDATE و DELETE روی یک پایگاه داده خاص نیاز دارد. دستور زیر یک کاربر با این سطح دسترسی ایجاد میکند:
CREATE USER 'app_user'@'localhost' IDENTIFIED BY 'YourStrongPassword!2024';
GRANT SELECT, INSERT, UPDATE, DELETE ON myapp_db.* TO 'app_user'@'localhost';
FLUSH PRIVILEGES;
در این دستور:
'app_user'@'localhost'یعنی این کاربر فقط از روی خود سرور میتواند متصل شود.myapp_db.*یعنی دسترسی فقط به پایگاه داده myapp_db و همه جدولهای آن.- دستور GRANT فقط چهار عملیات اصلی را مجاز میکند.
اگر برنامه شما نیاز به ایجاد جدول یا تغییر ساختار دارد (مثلاً در مرحله توسعه)، میتوانید دسترسی ALTER و CREATE را هم اضافه کنید، اما در محیط تولید این کار را نکنید. برای مهاجرت ساختار، از یک کاربر جداگانه با دسترسی DDL استفاده کنید و بعد از اتمام کار، آن را حذف کنید.
بررسی دسترسیهای یک کاربر
برای اینکه مطمئن شوید کاربر دسترسی اضافی ندارد، از دستور زیر استفاده کنید:
SHOW GRANTS FOR 'app_user'@'localhost';
خروجی باید فقط شامل همان دستور GRANT باشد که تعریف کردید. اگر چیزی مثل GRANT ALL PRIVILEGES یا USAGE ON *.* میبینید، یعنی کاربر دسترسی سراسری دارد و باید اصلاح شود.
اشتباه رایج: استفاده از wildcard در host
برخی از توسعهدهندگان برای راحتی، کاربر را با host بهصورت 'app_user'@'%' ایجاد میکنند. این یعنی کاربر میتواند از هر آدرسی متصل شود. اگر برنامه شما روی همان سرور است، حتماً از localhost استفاده کنید. اگر اتصال راهدور لازم است، بهجای % آدرس IP دقیق سرور برنامه را بنویسید:
CREATE USER 'app_user'@'192.168.1.50' IDENTIFIED BY 'YourStrongPassword!2024';
این کار باعث میشود حتی اگر رمز عبور لو برود، مهاجم فقط از آن IP خاص میتواند متصل شود.
تقویت رمز عبور root و سیاستهای پسورد
کاربر root در MySQL باید یک رمز عبور قوی و منحصربهفرد داشته باشد. اگر هنوز رمز پیشفرض یا ضعیف است، آن را تغییر دهید:
ALTER USER 'root'@'localhost' IDENTIFIED BY 'NewVeryStrongPassword!2024';
FLUSH PRIVILEGES;
MySQL نسخه 8 بهصورت پیشفرض افزونه caching\_sha2\_password را فعال کرده که امنیت بهتری نسبت به mysql\_native\_password دارد. اگر برنامه قدیمی شما با این افزونه مشکل دارد، بهجای برگشتن به افزونه ضعیفتر، کتابخانههای برنامه را بهروزرسانی کنید.
همچنین میتوانید سیاست پیچیدگی رمز را فعال کنید. در MySQL 8، این کار با نصب کامپوننت validate\_password انجام میشود:
INSTALL COMPONENT 'file://component_validate_password';
SET GLOBAL validate_password.policy = STRONG;
با این تنظیم، MySQL از ایجاد رمزهای ضعیف جلوگیری میکند و حداقل طول و ترکیب کاراکترها را الزامی میکند.
جمعبندی و چکلیست نهایی
امنیت MySQL یک فرآیند یکباره نیست، بلکه باید بهصورت دورهای بررسی شود. بعد از انجام تمام مراحل بالا، این چکلیست را مرور کنید:
- کاربر ناشناس حذف شده است؟ (SELECT user FROM mysql.user WHERE user='';)
- پایگاههای test حذف شدهاند؟
- bind-address فقط روی 127.0.0.1 است (یا IP خصوصی محدود)؟
- هیچ برنامهای با کاربر root به MySQL وصل نمیشود؟
- هر برنامه یک کاربر مجزا با حداقل دسترسی دارد؟
- رمز root قوی و منحصربهفرد است؟
- پورت 3306 از بیرون با فایروال بسته است (مگر نیاز واقعی)؟
اگر به تازگی یک سرور مجازی یا اختصاصی از ServerNet تهیه کردهاید و MySQL روی آن نصب است، همین امروز این مراحل را انجام دهید. چند دقیقه وقت صرفکردن برای پیکربندی امن، بسیار کمهزینهتر از مقابله با عواقب یک نفوذ است. به یاد داشته باشید که امنیت پایگاه داده، بخشی از امنیت کلی سرور شماست و نباید آن را نادیده گرفت.