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

چرا دیتابیس، هدف اصلی حملات است؟

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

دیتابیس، نه فقط مغز سایت، بلکه صندوق امانات آن است؛ هرچه درِ این صندوق محکم‌تر، خواب شبانه صاحب سایت راحت‌تر.

اصل کمترین دسترسی (Principle of Least Privilege)

در دنیای امنیت، این اصل یکی از پایه‌ای‌ترین اصول است: هر کاربر، فقط باید همان دسترسی‌هایی را داشته باشد که برای انجام کارش لازم است، نه بیشتر. در وردپرس، کاربری که در فایل wp-config.php تعریف می‌شود، باید فقط روی دیتابیس همان سایت، دسترسی SELECT، INSERT، UPDATE، DELETE و چند دسترسی محدود دیگر داشته باشد. سه اشتباه رایج که در پروژه‌های واقعی دیدم:

  • کاربر دیتابیس با دسترسی ALL PRIVILEGES روی تمام دیتابیس‌های اکانت.
  • استفاده از کاربر root در wp-config.php — خطای فاحش و پرهزینه.
  • کاربر دیتابیس با مجوز CREATE، DROP یا GRANT که هیچ‌کدام برای وردپرس لازم نیست.

روش درست: در پنل هاست (مثلاً cPanel که در cPanel چیست و چه کاربردی دارد معرفی کرده‌ام)، یک کاربر دیتابیس اختصاصی برای همان سایت بسازید و فقط دسترسی‌های لازم را به آن بدهید. اگر همان اکانت چند سایت دارد، هر سایت باید کاربر دیتابیس جداگانه داشته باشد. این جداسازی، در صورت نفوذ به یک سایت، انفجار را به همان سایت محدود می‌کند.

مدیریت کاربران MySQL

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

  1. حذف کاربران اضافه: گاهی بعد از مهاجرت یا تغییر افزونه، یک کاربر دیتابیس باقی می‌ماند که هیچ سایتی از آن استفاده نمی‌کند. این کاربران باید حذف شوند تا سطح حمله کاهش یابد.
  2. جداسازی بر اساس نقش: اگر یک برنامهٔ جانبی (مثلاً یک سرویس گزارش‌گیری) به دیتابیس متصل می‌شود، برای آن یک کاربر جداگانه با دسترسی فقط SELECT بسازید، نه به کاربر وردپرس.
  3. محدودسازی به میزبان مشخص: در ساختار کاربر MySQL، user@host تعیین می‌کند کاربر از کدام آدرس می‌تواند وصل شود. اگر وردپرس و MySQL روی همان سرور هستند، کاربر باید فقط از localhost اجازهٔ اتصال داشته باشد. اگر از آدرس % (هر میزبان) استفاده می‌کنید، در واقع دیتابیس را به تمام اینترنت باز کرده‌اید. روش دقیق مشاهده و تنظیم در مدیریت کاربران MySQL آمده است.

رمزهای عبور و احراز هویت

رمز عبور کاربر دیتابیس، اولین خط دفاعی است. سه توصیهٔ عملی:

  • حداقل ۱۶ کاراکتر، ترکیب حروف بزرگ و کوچک، عدد و نماد. رمزهای کوتاه، حتی با وجود احراز هویت مدرن، در برابر حملهٔ brute force (حدس زدن سیستماتیک) آسیب‌پذیرند.
  • رمز یکتا برای هر سایت و هر کاربر. اشتراک‌گذاری رمز بین سایت‌ها، در صورت نفوذ به یکی، همه را قربانی می‌کند.
  • ذخیره‌سازی امن در wp-config.php: این فایل به‌طور پیش‌فرض توسط وردپرس محافظت می‌شود، ولی توصیه می‌کنم با یک قاعدهٔ .htaccess یا Nginx، دسترسی مستقیم به آن را در وب‌سرور ممنوع کنید. جزئیات این تکنیک در چگونه فایل wp-config را امن کنیم؟ آمده است.

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

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

مهم‌ترین اقدامی که اکثر صاحبان سایت از آن غافل‌اند: آیا دیتابیس شما از راه دور قابل دسترسی است؟ در حالت پیش‌فرض، MySQL به‌صورت امن پیکربندی می‌شود که فقط از localhost قابل دسترسی باشد؛ ولی در بعضی از پنل‌ها، به‌دلیل راحتی کاربران، دسترسی از راه دور فعال است. اگر به دسترسی از راه دور نیازی ندارید (که در ۹۵٪ پروژه‌ها نیاز ندارید)، آن را خاموش کنید یا با فایروال، پورت 3306 را برای همه ببندید جز IP‌های شناخته‌شده. اگر می‌خواهید از راه دور به دیتابیس دسترسی داشته باشید، سه لایه محافظت اعمال کنید: محدودسازی IP در پنل هاست، استفاده از SSH Tunnel، و اجبار به اتصال SSL. تنظیم فایروال سرور در فایروال نرم‌افزاری در سرور: راهنمای عملی آمده است.

دفاع در برابر تزریق SQL

حملهٔ تزریق SQL یا SQL Injection یکی از قدیمی‌ترین و خطرناک‌ترین انواع حملات است: مهاجم از طریق ورودی‌های سایت (فرم، پارامتر URL، کوکی) کد SQL وارد می‌کند و کوئری‌های شما را تغییر می‌دهد. سه لایهٔ دفاعی که در پروژه‌های وردپرسی توصیه می‌کنم:

  1. استفاده از توابع آمادهٔ وردپرس: توابعی مثل $wpdb->prepare() کوئری شما را پارامتری می‌کنند و جلوی تزریق را می‌گیرند. هرگز کوئری را با الحاق مستقیم متغیر بسازید.
  2. اعتبارسنجی ورودی‌ها: هر ورودی کاربر، قبل از استفاده، باید اعتبارسنجی و پاک‌سازی شود — همان اصل «اعتبارسنجی و پاک‌سازی» که در اعتبارسنجی داده‌ها در کدنویسی وردپرس و پاک‌سازی داده‌ها در کدنویسی وردپرس آمده است.
  3. مجوز کاربر دیتابیس محدود: اگر یک حملهٔ تزریق SQL موفق شود ولی کاربر دیتابیس فقط دسترسی SELECT و INSERT روی یک جدول داشته باشد، خسارت محدود می‌شود. اینجاست که اصل کمترین دسترسی، لایهٔ دوم دفاعی می‌شود.

رمزنگاری داده‌های حساس

در فروشگاه‌های ووکامرس و سایت‌های عضویتی، حتی اگر تمام لایه‌های دفاعی بالا فعال باشند، باید داده‌های حساس را رمزنگاری کنید. سه سطح رمزنگاری توصیه می‌شود:

  • رمزنگاری در حال انتقال (In-Transit): تمام ارتباط بین وردپرس و MySQL باید از طریق SSL/TLS انجام شود. اگر MySQL و PHP روی همان سرور هستند، این لایه اهمیت کمتری دارد، ولی اگر دیتابیس روی سرور جداگانه است، الزامی است.
  • رمزنگاری در حال ذخیره (At-Rest): خود فایل‌های دیتابیس MySQL باید روی دیسک رمزنگاری‌شده باشند. روی بیشتر هاست‌های اشتراکی این لایه فعال است؛ روی سرور اختصاصی، باید خودتان پیکربندی کنید.
  • رمزنگاری ستون‌های حساس: برای داده‌هایی مثل شماره کارت بانکی، کد ملی، یا اطلاعات پزشکی، توصیه می‌کنم در سطح کد هم رمزنگاری اعمال شود. وردپرس توابع رمزنگاری داخلی (مثل wp_hash و سرویس‌های crypt) ارائه می‌دهد، ولی برای داده‌های حساس، استفاده از کتابخانه‌های تخصصی‌تر مثل libsodium توصیه می‌شود. مسیر دقیقش در رمزنگاری دیتابیس چگونه انجام می‌شود؟ آمده است.

جدول چک‌لیست امنیت MySQL

حوزهاقدامفرکانس
کاربرانحذف کاربران اضافه، جداسازی بر اساس نقشفصلی
رمزهارمز ۱۶+ کاراکتر یکتا، تغییر دوره‌ایسالانه
شبکهبستن پورت 3306 از راه دوربار اول
SQL Injectionاستفاده از prepare، اعتبارسنجی ورودیهر توسعه
رمزنگاریSSL/TLS و رمزنگاری ستون‌های حساسبار اول
لاگ‌هافعال کردن slow query log و audit logماهانه
بکاپبکاپ امن، ذخیره‌سازی خارج از سرورهفتگی

بکاپ امن دیتابیس

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

پایش و لاگ‌گیری

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

از دید معماری: دیتابیس به‌عنوان مرز نهایی اعتماد

در نگاه توسعه‌دهندهٔ ارشد، دیتابیس MySQL در وردپرس یک «مرز نهایی اعتماد» است: هر چیزی که تا این لایه برسد، در واقع از تمام لایه‌های قبلی عبور کرده و کاربر فرض می‌شود معتبر است. اگر این مرز شکسته شود، هیچ‌چیز دیگری نمی‌تواند سایت را نجات دهد. سه اصل معماری که این مرز را تقویت می‌کند:

اصل اول — جداسازی لایه‌ها. دیتابیس باید در شبکه‌ای جدا از وب‌سرور قرار بگیرد یا حداقل روی پورت غیرقابل‌دسترس از بیرون. این جداسازی، در برابر حملات ویروسی و ransomware هم اثر پیشگیرانه دارد. در سایت‌های تک‌سرور، این جداسازی در سطح شبکه اتفاق نمی‌افتد ولی با محدودسازی localhost و استفاده از سوکت Unix، اثر مشابهی می‌دهد. اصل دوم — حداقل دسترسی در هر سطح. این اصل را نه فقط برای کاربر دیتابیس، بلکه برای افزونه‌ها هم اعمال کنید: افزونه‌ای که فقط نیاز به خواندن جدول دارد، نباید مجوز نوشتن بگیرد. اگر افزونه‌ای این جداسازی را رعایت نمی‌کند، افزونهٔ امنی نیست. اصل سوم — قابل بازسازی بودن. هر داده‌ای که در دیتابیس است، باید بتوان از یک منبع مستقل بازسازی کرد: بکاپ، لاگ، یا منبع اولیه. این اصل، اهمیت استراتژیک بکاپ را بیشتر از یک نسخهٔ پشتیبان ساده می‌کند؛ بکاپ بخشی از معماری امنیتی است، نه یک دکمهٔ اضطراری.

در پروژه‌های واقعی، رعایت این سه اصل باعث شده در چند پرونده، حتی وقتی یک افزونهٔ ضعیف روی سایت شناسایی شد و مهاجم توانست به لایهٔ وردپرس نفوذ کند، دیتابیس سالم بماند و سایت بدون از دست دادن داده بازسازی شود. اگر از این منظر به امنیت نگاه کنید، متوجه می‌شوید که امنیت دیتابیس، مسئولیت یک افزونه یا یک تنظیم نیست؛ یک تصمیم معماری است که در تمام لایه‌های پروژه اثر می‌گذارد. برای توسعه‌دهندگانی که در سطح کد کار می‌کنند، پیشنهاد می‌کنم مسیر نوشتن کد PHP امن برای وردپرس و SQL Injection چیست و چگونه جلوگیری کنیم؟ را ادامه دهند. و یک توصیهٔ همیشگی: به‌جای تمرکز روی پیدا کردن «یک جادوی امنیتی»، روی ساختن لایه‌به‌لایه دفاع سرمایه‌گذاری کنید. امنیت واقعی از کنار هم گذاشتن اقدام‌های کوچک و منظم به دست می‌آید، نه از یک افزونهٔ معجزه‌گر.

اگر روی دیتابیس وردپرس خودتان اقدامی برای امنیت انجام داده‌اید که در یک حادثهٔ واقعی نجاتتان داد — مثل جداسازی کاربر دیتابیس یا محدودسازی IP — سناریو را در دیدگاه بنویسید. همین جزئیات ساده، برای توسعه‌دهندهٔ بعدی که به‌دنبال راه‌حل عملی است، از هر مستند انگلیسی ارزشمندتر است. 🔐