اولین بار که با یک پرونده‌ی امنیتی جدی روبرو شدم، سایت یک مشتری بود که بعد از هک شدن، همه‌ی داده‌هایش پاک شده بود. بعد از بررسی لاگ‌ها، معلوم شد برنامه‌ی PHP آن سایت، از کاربر root MySQL برای اتصال استفاده می‌کرد. کافی بود یک آسیب‌پذیری ساده در یکی از افزونه‌ها پیدا شود تا مهاجم، نه فقط بتواند داده‌ها را بخواند، بلکه با دسترسی کامل، همه‌چیز را پاک کند. آن روز برایم روشن شد که مدیریت کاربران MySQL، یک بحث جانبی نیست؛ خط اول دفاعی است که اگر درست پیاده نشود، تمام لایه‌های امنیتی بعدی بی‌اثر می‌شوند. در این مقاله، همان مسیری را می‌روم که امروز در هر پروژه‌ی جدید طی می‌کنم: از ساخت کاربر و اعطای مجوز، تا نقش‌ها، محدودسازی IP، مدیریت رمز و اشتباهات امنیتی که در بازبینی پروژه‌ها زیاد دیده‌ام.

چرا مدیریت کاربران، پایه‌ی امنیت است؟

اگر تازه با MySQL آشنا می‌شوید، اول آموزش MySQL از صفر را بخوانید. اما فرض کنیم با مفاهیم پایه راحت هستید و مدتی است با دیتابیس کار می‌کنید. مدیریت کاربران MySQL، به مجموعه‌ای از دستورات و تصمیم‌ها گفته می‌شود که تعیین می‌کنند چه کسی به دیتابیس متصل شود و به چه چیزی دسترسی داشته باشد. سادگی این تعریف، گمراه‌کننده است؛ چرا که در پروژه‌های واقعی، هر تصمیم اشتباه در این حوزه، می‌تواند به فاجعه‌ی امنیتی منجر شود.

سه دلیل که در پروژه‌های واقعی به آن‌ها رسیده‌ام، نشان می‌دهد چرا این حوزه از سایر بخش‌های MySQL حیاتی‌تر است:

  • اولین هدف مهاجم: وقتی یک مهاجم به دیتابیس شما نفوذ می‌کند، اولین چیزی که به‌دنبالش می‌گردد، دسترسی‌های کاربر فعلی است. اگر آن کاربر مجوزهای حداقلی داشته باشد، خسارت محدود می‌شود. اگر مجوز کامل داشته باشد، همه‌چیز از دست می‌رود.
  • کاهش سطح حمله: هر کاربر اضافی، هر مجوز اضافی، و هر رمز عبور ضعیف، یک سطح حمله‌ی جدید است. پاکسازی دوره‌ای این سطوح، بخشی از همان انضباطی است که در بهترین روش‌های امنیت MySQL رویش تأکید کرده‌ام.
  • قابل‌ردیابی بودن: وقتی هر برنامه و هر کاربر، حساب اختصاصی خودش را داشته باشد، لاگ‌های دیتابیس نشان می‌دهند که چه کسی چه کار کرده. این قابلیت ردیابی، در حادثه‌ها تفاوت بین «می‌دانم چه اتفاقی افتاد» و «نمی‌دانم از کجا خورد» است.

نکته‌ی مهمی که در پروژه‌های واقعی به آن رسیده‌ام: مدیریت کاربران MySQL، به‌ندرت یک پروژه‌ی یک‌باره است. مثل رمز عبور، باید دوره‌ای بازبینی شود. تجربه‌ی من این است که هر شش ماه، یک مرور کاربران و مجوزها انجام دهم — چون با رشد پروژه، کاربران اضافه می‌شوند، ولی پاکسازی، اغلب فراموش می‌شود.

در امنیت دیتابیس، هر کاربری که مجوز بیشتری از نیازش دارد، یک درِ پشتی باز گذاشته است — و در پشتی، همیشه بعد از بحران کشف می‌شود.

کاربر و هاست: جفت همیشگی

یکی از مفاهیمی که در MySQL بسیار مهم است ولی در ابزارهای گرافیکی زیادی نادیده گرفته می‌شود، جفت «کاربر و هاست» است. در MySQL، یک کاربر با ترکیب username و host شناسایی می‌شود. یعنی "ali"@"localhost" و "ali"@"%"، دو کاربر متفاوت‌اند، حتی اگر نام کاربری‌شان یکی باشد.

این تفکیک، یک امکان امنیتی بسیار قدرتمند است که در پروژه‌های واقعی به‌کارم آمده:

  • localhost: کاربر فقط از همان سرور می‌تواند متصل شود. مناسب برنامه‌هایی که روی همان سرور دیتابیس اجرا می‌شوند.
  • IP خاص (مثل 192.168.1.5): کاربر فقط از یک آدرس مشخص. مناسب اتصال یک سرور برنامه به دیتابیس.
  • بازه‌ی شبکه‌ای (مثل 192.168.1.%): کاربر از هر IP در آن شبکه. مناسب برای محیط‌های داخلی با چند سرور.
  • %: کاربر از هر آدرسی در دنیا. این گزینه، در بیشتر موارد یک اشتباه است.

تجربه‌ی من: بسیاری از پروژه‌ها، کاربرانشان را با % می‌سازند چون «راحت» است. بعد از یک آسیب‌پذیری ساده در برنامه، این کاربر از هر جای دنیا قابل دسترسی است — که پنجره‌ی حمله را چند برابر می‌کند. توصیه‌ی همیشگی من: حداقل تا زمانی که برنامه واقعاً نیاز ندارد، کاربران را با localhost محدود کنید. اگر باید از سرور دیگری متصل شوند، از IP خاص استفاده کنید نه %. جزئیات کامل این رویکرد را در محدودسازی دسترسی خارجی به دیتابیس با مثال‌های عملی آورده‌ام.

نکته‌ی ظریفی که در پروژه‌های واقعی به آن برخورده‌ام: وقتی کاربر را با "ali"@"localhost" می‌سازید، اگر برنامه‌ای از 127.0.0.1 به‌جای localhost متصل شود، ممکن است عدم تطابق رخ دهد. برای اطمینان، همیشه هم localhost و هم 127.0.0.1 را در نظر بگیرید — یا از یک کاربر مشترک با localhost استفاده کنید و برنامه را مجبور کنید با همان نام متصل شود.

ساخت کاربر و مدیریت رمز عبور

ساخت کاربر در MySQL، با دستور CREATE USER انجام می‌شود:

CREATE USER "app_user"@"localhost" IDENTIFIED BY "StrongP@ssw0rd-2026!";

سه نکته‌ی مهم در همین یک خط که در پروژه‌های واقعی به آن‌ها رسیده‌ام:

۱) رمز عبور، در کوئری نباشد

اگر این دستور را در یک اسکریپت یا فایل migration بنویسید، رمز عبور در گیت‌‌هاب یا لاگ سرور باقی می‌ماند. راه‌حل: از متغیرهای محیطی یا ابزارهای مدیریت رمز استفاده کنید. مثلاً در یک اسکریپت bash:

DB_PASSWORD=$(cat /etc/secrets/db_password)
mysql -u root -p -e "CREATE USER \"app_user\"@\"localhost\" IDENTIFIED BY \"$DB_PASSWORD\";"

در پروژه‌های واقعی، همین نکته‌ی کوچک، جلوی بسیاری از نشت‌های اطلاعاتی را گرفته است. اصول کلی مدیریت رمز عبور را در مدیریت رمز عبور امن با جزئیات توضیح داده‌ام — این اصول، فراتر از دیتابیس، در همه‌ی سرویس‌ها صادق است.

۲) پلاگین احراز هویت

در MySQL ۸.۰، پیش‌فرض پلاگین احراز هویت caching_sha2_password است که امن‌تر از mysql_native_password قدیمی است. ولی بعضی کلاینت‌های قدیمی از آن پشتیبانی نمی‌کنند. اگر با خطای اتصال روبرو شدید، این احتمال را در نظر بگیرید:

-- برای سازگاری با کلاینت قدیمی
CREATE USER "app_user"@"localhost"
IDENTIFIED WITH mysql_native_password BY "StrongPass";

توجه: استفاده از mysql_native_password فقط به‌عنوان راه‌حل موقت توصیه می‌شود؛ بهتر است کلاینت‌ها را به‌روز کنید و از caching_sha2_password استفاده کنید.

۳) تغییر رمز عبور

ALTER USER "app_user"@"localhost" IDENTIFIED BY "NewStrongPass!";

-- اجبار به تغییر در اتصال بعدی
ALTER USER "app_user"@"localhost" PASSWORD EXPIRE;

در پروژه‌های واقعی، وقتی رمز عبور یک کاربر را تغییر می‌دهید، حتماً بعد از آن FLUSH PRIVILEGES بزنید تا مطمئن شوید تغییرات اعمال شده — هرچند از MySQL ۵.۷ به بعد، این دستور برای تغییرات کاربران لازم نیست، ولی به‌عنوان یک عادت امن، آن را در نظر بگیرید.

چهار سطح مجوز

در MySQL، مجوزها در چهار سطح قابل اعطا هستند. فهم دقیق این سطوح، کلید مدیریت درست کاربران است:

سطحنحوکاربرد
جهانیGRANT ... ON *.*دسترسی به همه‌ی دیتابیس‌ها
دیتابیسGRANT ... ON mydb.*دسترسی به همه‌ی جدول‌های یک دیتابیس
جدولGRANT ... ON mydb.usersدسترسی فقط به یک جدول خاص
ستونGRANT ... (name, email) ON mydb.usersدسترسی فقط به ستون‌های خاص

در پروژه‌های واقعی، قاعده‌ی طلایی من این است: مجوز را در پایین‌ترین سطحی که کار می‌کند، اعطا کنید. یعنی اگر برنامه‌ای فقط به یک دیتابیس نیاز دارد، همان سطح دیتابیس کافی است — به آن ON *.* ندهید. اگر برنامه‌ای فقط یک جدول را می‌خواند، همان سطح جدول کافی است. این سطح‌بندی، در روز حادثه، تفاوت بین «مهاجم فقط یک جدول را دید» و «مهاجم همه‌ی داده‌های کسب‌وکار را گرفت» است.

مجوزهای پرکاربرد

فهرست کامل مجوزهای MySQL طولانی است، ولی در پروژه‌های واقعی، این پنج مجوز، بیشترین کاربرد را دارند:

  • SELECT: خواندن داده.
  • INSERT: درج داده‌ی جدید.
  • UPDATE: به‌روزرسانی داده‌ی موجود.
  • DELETE: حذف داده.
  • EXECUTE: اجرای رویه‌های ذخیره‌شده (Stored Procedures).

سه مجوزی که در پروژه‌های واقعی زیاد دیده‌ام بی‌دلیل اعطا می‌شوند ولی خطرناک‌اند:

  • DROP: حذف جدول یا دیتابیس. فقط برای اسکریپت‌های migration در محیط staging لازم است.
  • CREATE USER: ساخت کاربر جدید. فقط برای مدیر دیتابیس لازم است، نه برای برنامه.
  • GRANT OPTION: اجازه‌ی اعطای مجوز به دیگران. این مجوز، در پروژه‌های واقعی، تقریباً هیچ‌وقت برای برنامه‌ها لازم نیست.

اعطا و لغو مجوز

اعطای مجوز، با دستور GRANT انجام می‌شود:

-- دسترسی خواندن و نوشتن روی یک دیتابیس
GRANT SELECT, INSERT, UPDATE, DELETE
ON mydb.*
TO "app_user"@"localhost";

-- دسترسی فقط خواندن روی یک جدول خاص
GRANT SELECT
ON mydb.products
TO "report_user"@"localhost";

-- اعمال تغییرات
FLUSH PRIVILEGES;

و لغو مجوز، با REVOKE:

REVOKE DELETE ON mydb.* FROM "app_user"@"localhost";

-- لغو همه‌ی مجوزها
REVOKE ALL PRIVILEGES ON mydb.* FROM "app_user"@"localhost";

سه نکته‌ی مهم در اعطا و لغو مجوز که در پروژه‌های واقعی به آن‌ها رسیده‌ام:

  • مجوزهای سطح بالا را به سطح پایین نمی‌توان از ارث برد: اگر به کاربری مجوز ON *.* بدهید و بعد بخواهید آن را با REVOKE ... ON mydb.* لغو کنید، آن مجوز لغو نمی‌شود — چون در سطح بالاتری اعطا شده. برای لغو، باید REVOKE ... ON *.* بزنید.
  • SHOW GRANTS را جدی بگیرید: بعد از هر تغییر مجوز، این دستور را اجرا کنید تا مطمئن شوید وضعیت واقعی، همان است که انتظار دارید. در پروژه‌های واقعی، بارها دیده‌ام که مجوزی «گم شده» یا «برگشته» به‌دلیل یک اشتباه ساده.
  • مستندسازی کنید: هر کاربر، هر مجوز و دلیل وجودش را در یک فایل یادداشت کنید. دو سال بعد، خودتان هم یادتان نمی‌آید چرا یک کاربر خاص، مجوز EXECUTE دارد.
در اعطای مجوز، سخاوتِ امروز، پشیمانیِ فرداست؛ مجوز حداقل، امنیت حداکثر.

نقش‌ها (Roles) در MySQL 8

اگر نسخه‌ی MySQL شما ۸.۰ یا بالاتر است، ابزار قدرتمندی در اختیار دارید: نقش‌ها (Roles). نقش، مجموعه‌ای از مجوزهاست که می‌توانید به چند کاربر به‌طور یکسان اعطا کنید. مثال:

-- ساخت یک نقش
CREATE ROLE "app_readonly";

-- اعطای مجوز به نقش
GRANT SELECT ON mydb.* TO "app_readonly";

-- فعال‌سازی نقش به‌طور پیش‌فرض
SET DEFAULT ROLE "app_readonly" TO "report_user"@"localhost";

-- یا اعطای نقش به یک کاربر
GRANT "app_readonly" TO "report_user"@"localhost";

مزیت نقش‌ها در پروژه‌های واقعی، در سه چیز است:

  • کاهش تکرار: اگر ده کاربر باید همان مجموعه‌ی مجوزها را داشته باشند، به‌جای ده بار GRANT، یک نقش می‌سازید و به ده کاربر می‌دهید.
  • نگهداری آسان‌تر: اگر مجوزی باید تغییر کند، فقط نقش را تغییر می‌دهید. همه‌ی کاربران، تغییر را می‌بینند.
  • مدیریت در محیط‌های بزرگ: در سیستم‌هایی با ده‌ها کاربر و ده‌ها جدول، بدون نقش، مدیریت مجوزها به کابوس تبدیل می‌شود.

نکته‌ی مهم: در MySQL، نقش‌ها به‌طور پیش‌فرض پس از اعطا فعال نیستند. یعنی اگر نقشی به کاربر بدهید ولی آن را ACTIVE نکنید، کاربر نمی‌تواند از آن استفاده کند. برای فعال‌سازی خودکار، از SET DEFAULT ROLE استفاده کنید — همان‌طور که در مثال بالا نشان دادم. اگر با مفهوم نقش‌ها در دیگر سیستم‌ها هم آشنا هستید، تفاوت احراز هویت و مجوزدهی تفکیک این دو مفهوم را روشن می‌کند — نقش در MySQL، به دسته‌ی مجوزدهی تعلق دارد.

کاربر برنامه در برابر کاربر انسانی

یکی از مفاهیمی که در مدیریت کاربران MySQL زیاد دیده‌ام نادیده گرفته می‌شود، تفکیک بین کاربر برنامه و کاربر انسانی است. این تفکیک، به‌نظر ساده می‌آید ولی در پروژه‌های واقعی، تفاوت‌های عملی مهمی می‌سازد:

کاربر برنامه

این کاربر توسط برنامه (PHP، Python، Node.js و…) برای اتصال به دیتابیس استفاده می‌شود. ویژگی‌هایش:

  • رمز عبور پیچیده و طولانی (۳۲ کاراکتر یا بیشتر).
  • محدود به IP یا localhost سرور برنامه.
  • مجوزهای حداقلی که برنامه واقعاً نیاز دارد.
  • رمز عبور دوره‌ای تغییر می‌کند (هر ۶ ماه یا سالانه).
  • نباید بتواند با آن از طریق کلاینت (مثل phpMyAdmin) وارد شود — به این منظور، از REQUIRE استفاده کنید:
-- جلوگیری از ورود مستقیم به دیتابیس
CREATE USER "app_user"@"localhost"
IDENTIFIED BY "StrongPass"
REQUIRE NONE;

کاربر انسانی

این کاربر برای انسان‌هایی است که گاهی به دیتابیس متصل می‌شوند — مدیر سیستم، توسعه‌دهنده، تحلیلگر داده. ویژگی‌هایش:

  • هر نفر یک حساب جداگانه — برای قابل‌ردیابی بودن.
  • رمز عبور یکتا و قابل‌تغییر — نه رمز مشترک در تیم.
  • مجوزهای متناسب با نقش شخص (مثلاً تحلیلگر فقط SELECT).
  • در صورت تغییر نقش یا خروج از تیم، حساب غیرفعال یا حذف شود.

در پروژه‌های واقعی، یکی از بزرگ‌ترین اشتباهات این است که همه‌ی توسعه‌دهندگان با یک حساب مشترک وارد دیتابیس می‌شوند. نتیجه: در حادثه، نمی‌دانید چه کسی چه‌کار کرد. اصول کلی این تفکیک، همان چیزی است که در مدیریت رمز عبور امن برای همه‌ی سیستم‌ها توصیه کرده‌ام.

محدودسازی دسترسی به IP

علاوه بر تعریف هاست در زمان ساخت کاربر، MySQL امکان محدودسازی دقیق‌تری هم دارد:

-- ساخت کاربر با محدودیت IP
CREATE USER "app_user"@"192.168.1.5" IDENTIFIED BY "StrongPass";

-- اعمال محدودیت روی یک کاربر موجود
ALTER USER "app_user"@"%"
REQUIRE IP ADDRESS "192.168.1.5";

تجربه‌ی من در پروژه‌های واقعی: در سیستم‌هایی که چند سرور برنامه به یک دیتابیس مشترک وصل می‌شوند، محدودسازی IP یک لایه‌ی امنیتی بسیار مؤثر است. حتی اگر رمز عبور برنامه‌ی یکی از سرورها لو برود، مهاجم نمی‌تواند از جای دیگری به دیتابیس وصل شود. اصول کامل‌تر این محدودسازی را در محدودسازی دسترسی خارجی به دیتابیس با جزئیات آورده‌ام.

نکته‌ی مهم: محدودسازی IP در MySQL، در سطح کاربر انجام می‌شود، نه در سطح فایروال. برای امنیت کامل، هم از فایروال (مثلاً ufw یا iptables) استفاده کنید، هم از محدودسازی MySQL. اصول این لایه‌بندی، در فایروال نرم‌افزاری روی سرور آمده است.

پایش و بررسی کاربران

مدیریت کاربران، فقط ساخت و مجوز دادن نیست؛ پایش دوره‌ای هم بخشی از آن است. سه دستور که در پروژه‌های واقعی به‌طور مرتب استفاده می‌کنم:

۱) فهرست کاربران

SELECT user, host, account_locked, password_expired
FROM mysql.user
ORDER BY user, host;

این کوئری، فهرست کامل کاربران را با وضعیت قفل و انقضای رمز نشان می‌دهد. تجربه‌ی من: هر سه ماه، این فهرست را با فهرست کاربران «که باید باشند» مقایسه می‌کنم — کاربرانی که در پروژه نیستند ولی در MySQL هستند، کاندیدای حذف‌اند.

۲) بررسی مجوزهای یک کاربر

SHOW GRANTS FOR "app_user"@"localhost";

بعد از هر تغییر در برنامه، این دستور را اجرا کنید تا مطمئن شوید مجوزها همان است که انتظار دارید.

۳) پایش اتصال‌های فعال

SELECT user, host, db, command, time, state
FROM information_schema.PROCESSLIST
ORDER BY time DESC;

این کوئری، اتصال‌های فعال را نشان می‌دهد — چه کاربری، به کدام دیتابیس، با چه دستوری، چقدر زمان. در حادثه‌ها، این ابزار اولین جایی است که به آن نگاه می‌کنم. اگر با دستورات پرکاربرد MySQL آشنایی کم دارید، دستورات پرکاربرد MySQL فهرست کامل‌تری از این ابزارها در اختیارتان می‌گذارد.

سیاست رمز عبور و انقضا

در MySQL ۸.۰ به بعد، می‌توانید سیاست رمز عبور را در سطح سرور تعریف کنید:

-- فعال‌سازی افزونه‌ی اعتبارسنجی رمز
INSTALL COMPONENT "file://component_validate_password";

-- تنظیم سیاست
SET GLOBAL validate_password.policy = "MEDIUM";
SET GLOBAL validate_password.length = 12;
SET GLOBAL validate_password.mixed_case_count = 1;
SET GLOBAL validate_password.number_count = 1;
SET GLOBAL validate_password.special_char_count = 1;

با این تنظیمات، MySQL اجازه نمی‌دهد کاربر رمز عبور ضعیف انتخاب کند. سه نکته‌ی عملی که در پروژه‌های واقعی به آن‌ها رسیده‌ام:

  • طول حداقل ۱۲ کاراکتر: رمزهای کوتاه‌تر، در برابر حمله‌ی Brute Force آسیب‌پذیرند. برای پروژه‌های حساس، ۱۶ کاراکتر انتخاب بهتری است.
  • انقضای رمز عبور: در محیط‌های سازمانی، می‌توانید با PASSWORD EXPIRE INTERVAL 180 DAY، کاربران را مجبور کنید هر شش ماه رمز را تغییر دهند. ولی برای حساب‌های برنامه، این کار توصیه نمی‌شود — چون به‌روزرسانی خودکار رمز، پیچیدگی‌های خاص خودش را دارد.
  • قفل حساب بعد از تلاش‌های ناموفق: در MySQL ۸.۰، می‌توانید تعریف کنید که بعد از چند تلاش ناموفق، حساب قفل شود:
CREATE USER "app_user"@"localhost"
IDENTIFIED BY "StrongPass"
FAILED_LOGIN_ATTEMPTS 5
PASSWORD_LOCK_TIME 1;

این تنظیمات، جلوی حمله‌ی Brute Force را می‌گیرد. اگر با روش‌های جلوگیری از این نوع حمله در بسترهای دیگر هم آشنا هستید، جلوگیری از حملات Brute Force در وردپرس همان اصول را در سطح برنامه توضیح می‌دهد.

حذف کاربر و پاکسازی

حذف کاربر، با DROP USER انجام می‌شود:

DROP USER "old_user"@"localhost";

-- حذف امن‌تر: اگر کاربر وجود نداشته باشد، خطا ندهد
DROP USER IF EXISTS "old_user"@"localhost";

سه نکته‌ی مهم در حذف کاربر که در پروژه‌های واقعی به آن‌ها رسیده‌ام:

  • اول بررسی، بعد حذف: قبل از حذف، مطمئن شوید برنامه‌ای از آن کاربر استفاده نمی‌کند. یک راه ساده: کوئری SHOW PROCESSLIST را چند بار در ساعات مختلف اجرا کنید و ببینید آیا کاربر مورد نظر در حال اتصال است.
  • حذف مجوزها قبل از حذف کاربر: می‌توانید اول با REVOKE ALL PRIVILEGES مجوزها را بگیرید و چند روز صبر کنید. اگر برنامه‌ای خطا نداد، بعد کاربر را حذف کنید. این رویکرد محافظه‌کارانه، در پروژه‌های تولیدی ارزشمند است.
  • حذف کاربر، جدول‌هایش را حذف نمی‌کند: اگر کاربری جدول‌هایی در MySQL دارد، آن جدول‌ها باقی می‌مانند. برای پاکسازی کامل، باید جدول‌ها را جداگانه حذف کنید یا مالکیت را به کاربر دیگری منتقل کنید.

اشتباهات امنیتی که در پروژه‌های واقعی دیده‌ام

در بازبینی مدیریت کاربران MySQL در پروژه‌های مختلف، این اشتباهات را زیاد دیده‌ام:

  • استفاده از کاربر root در برنامه: همان فاجعه‌ی مقدمه. حتی اگر برنامه‌ای در محیط داخلی اجرا می‌شود، حساب root برای آن استفاده نکنید. اگر برنامه هک شود، مهاجم به کل دیتابیس دسترسی دارد.
  • اعطای ON *.* به همه: بعضی تیم‌ها برای راحتی، به همه‌ی کاربران مجوز کل سرور می‌دهند. این تصمیم، در روز اول مشکلی ایجاد نمی‌کند، ولی در روز حادثه، همه‌چیز را در معرض خطر می‌گذارد.
  • کاربر با % برای برنامه‌های داخلی: کاربری که از هر آدرسی در دنیا قابل دسترسی است، پنجره‌ی حمله را به‌شدت بزرگ می‌کند. برای برنامه‌های داخلی، همیشه localhost یا IP خاص انتخاب کنید.
  • رمز عبور مشترک بین چند برنامه: اگر سه برنامه از یک حساب استفاده می‌کنند، در روز حادثه، هیچ‌وقت نمی‌فهمید کدام‌شان مقصر بوده. همیشه هر برنامه یک حساب جداگانه.
  • رمز عبور ضعیف: رمزهای کوتاه یا قابل حدس، در برابر Brute Force آسیب‌پذیرند. تنظیمات سیاست رمز MySQL را جدی بگیرید.
  • فراموش کردن FLUSH PRIVILEGES: در نسخه‌های قدیمی MySQL، تغییر مجوزها بدون این دستور اعمال نمی‌شد. اگر پروژه‌ی شما روی نسخه‌ی قدیمی است، این دستور را فراموش نکنید.
  • عدم پاکسازی کاربران قدیمی: با گذشت زمان، کاربران زیادی در دیتابیس انباشته می‌شوند — توسعه‌دهندگانی که پروژه را ترک کرده‌اند، حساب‌های تست فراموش‌شده، حساب‌های برنامه‌های قدیمی. هر شش ماه یک پاکسازی انجام دهید.
  • نداشتن مستندسازی: دو سال بعد، هیچ‌کس نمی‌داند چرا یک کاربر خاص، مجوز EXECUTE دارد. یک فایل USERS.md که نقش هر کاربر و دلیل وجودش را توضیح دهد، این ابهام را از بین می‌برد.
  • نادیده‌گرفتن لاگ‌ها: MySQL می‌تواند لاگ اتصال‌ها و مجوزها را نگه دارد. فعال‌کردن general_log در محیط staging و بررسی دوره‌ای آن، الگوهای مشکوک را نشان می‌دهد.
  • ترکیب کاربر انسانی و برنامه: اگر توسعه‌دهنده‌ای با همان حساب برنامه وارد دیتابیس شود، خطاهای انسانی می‌تواند داده را آلوده کند. هر کدام از این دو، حساب جداگانه لازم دارند.
  • عدم استفاده از نقش‌ها در MySQL 8: بدون نقش، مدیریت مجوزها به‌سرعت پیچیده می‌شود. اگر نسخه‌ی MySQL شما ۸.۰ یا بالاتر است، از این ابزار استفاده کنید.
  • ساخت کاربر جدید بدون نیاز واقعی: بعضی تیم‌ها برای هر کار کوچکی کاربر جدید می‌سازند. این رویکرد، فهرست کاربران را به‌سرعت شلوغ می‌کند. قبل از ساخت کاربر جدید، بپرسید آیا می‌توان با نقش‌ها یا مجوزهای کاربر موجود حل کرد.

یک توصیه‌ی عملی از تجربه: در پروژه‌های واقعی، هر شش ماه یک «بازبینی کاربران» انجام دهید. در این بازبینی، سه سؤال را از خودتان بپرسید. اول، «هر کاربر برای چه هدفی ساخته شده و آیا هنوز آن هدف وجود دارد؟» دوم، «آیا مجوزهای هر کاربر، با نیاز واقعی امروز هم‌خوان است؟» سوم، «آیا رمز عبور هر کاربر برنامه، در چند ماه گذشته تغییر کرده است؟» اگر جواب هر سه سؤال صادقانه «بله» است، دیتابیس شما در وضعیت امنی قرار دارد. اگر نه، همین امروز یک گام بردارید — چون در امنیت دیتابیس، تأخیر یعنی ریسک. اگر با امنیت دیتابیس در بسترهای دیگر هم درگیر هستید، امنیت دیتابیس چیست مفاهیم پایه را روشن می‌کند و امنیت دیتابیس وردپرس همان اصول را در بستر وردپرس نشان می‌دهد.

سخن آخر

مدیریت کاربران MySQL، از یک CREATE USER ساده شروع می‌شود ولی در پروژه‌های واقعی، به یک تصمیم راهبردی درباره‌ی امنیت دیتابیس تبدیل می‌شود. سه نکته‌ی اصلی که در این مقاله به آن‌ها رسیدیم: اول، جفت کاربر و هاست را جدی بگیرید — برای برنامه‌های داخلی، localhost یا IP خاص، نه %؛ دوم، مجوز را در پایین‌ترین سطحی که کار می‌کند اعطا کنید — سطح دیتابیس، جدول یا حتی ستون، بسته به نیاز واقعی؛ سوم، نقش‌ها (Roles) در MySQL 8.0، ابزار قدرتمندی برای مدیریت مجوزها در پروژه‌های بزرگ هستند — اگر در نسخه‌ی جدید MySQL هستید، از آن بهره ببرید.

اگر امروز می‌خواهید شروع کنید، سه کار کوچک پیشنهاد می‌کنم: فهرست کاربران MySQL خود را با SELECT user, host FROM mysql.user ببینید و هر کاربری که مدت‌هاست استفاده نشده را حذف کنید؛ اگر برنامه‌ای از root استفاده می‌کند، برایش یک کاربر اختصاصی با مجوز حداقل بسازید؛ و برای هر کاربر، یک یادداشت کوتاه با نقش و دلیل وجودش بنویسید. همین سه کار، در بیشتر پروژه‌ها، سطح امنیت را به‌طور محسوس بالا می‌برد. اگر تجربه‌ای از مدیریت کاربران MySQL در پروژه‌های خودتان دارید — مخصوصاً اگر با یک حادثه یا تصمیم خاص روبرو شده‌اید — در دیدگاه‌ها بنویسید؛ همین نکته‌های میدانی، برای خواننده‌ی بعدی از هر مستند رسمی ارزشمندتر است. 🛡️