لوگوی هاکان سرور
لوگوی هاکان سرور

امنیت وردپرس؛ ۲۰ اقدام عملی از هاست تا افزونه برای جلوگیری از هک

سپر و قفل کنار سرور با نشان وردپرس و علامت هشدار؛ راهنمای امنیت وردپرس و جلوگیری از هک
فهرست مطالب
  1. به‌روزرسانی هسته، افزونه‌ها و پوسته‌ها
  2. ۱. به‌روزرسانی خودکار هسته را خاموش نکنید
  3. ۲. افزونه‌ها و پوسته‌ها را مرتب به‌روز کنید
  4. ۳. افزونه و پوستهٔ بلااستفاده را حذف کنید
  5. ۴. قالب و افزونهٔ نال‌شده نصب نکنید
  6. حساب‌های کاربری و دسترسی‌ها
  7. ۵. برای هر حساب رمز یکتا و بلند بگذارید
  8. ۶. ورود دومرحله‌ای را برای مدیران فعال کنید
  9. ۷. کاربر admin و مدیران اضافه را حذف کنید
  10. ۸. رمزهای عبور برنامه را بازبینی کنید
  11. محافظت از صفحهٔ ورود و XML-RPC
  12. ۹. تلاش‌های ورود را محدود کنید
  13. ۱۰. XML-RPC را اگر لازم ندارید ببندید
  14. HTTPS و تنظیمات wp-config.php
  15. ۱۱. کل سایت را روی HTTPS ببرید
  16. ۱۲. ویرایشگر فایل پیشخوان را خاموش کنید
  17. ۱۳. کلیدهای امنیتی را تازه کنید
  18. ۱۴. مجوز فایل‌ها و wp-config.php را درست کنید
  19. امنیت در سطح هاست و سرور
  20. ۱۵. از نسخهٔ پشتیبانی‌شدهٔ PHP استفاده کنید
  21. ۱۶. اجرای PHP را در پوشهٔ uploads ببندید
  22. ۱۷. فهرست پوشه‌ها و فایل‌های حساس را ببندید
  23. بکاپ، افزونهٔ امنیتی و پایش
  24. ۱۸. هر روز بکاپ بگیرید و نسخه‌ای بیرون از هاست نگه دارید
  25. ۱۹. فقط یک افزونهٔ امنیتی نصب و تنظیم کنید
  26. ۲۰. لاگ فعالیت، تغییر فایل‌ها و Search Console را پایش کنید
  27. پاک‌سازی سایت هک‌شدهٔ وردپرس
  28. نشانه‌های هک شدن
  29. گام ۱: سایت را از دسترس عموم خارج کنید
  30. گام ۲: یک نسخه نگه دارید و رمزهای هاست را عوض کنید
  31. گام ۳: اگر بکاپ سالم دارید، از آن شروع کنید
  32. گام ۴: هسته را بررسی و با نسخهٔ سالم جایگزین کنید
  33. گام ۵: افزونه‌ها و پوسته‌ها را از منبع رسمی دوباره نصب کنید
  34. گام ۶: uploads، mu-plugins و فایل‌های پنهان را بگردید
  35. گام ۷: کاربران، تنظیمات و دیتابیس را بررسی کنید
  36. گام ۸: رمزها و کلیدها را عوض کنید و درخواست بررسی بدهید
  37. رفع مشکلات رایج بعد از امن‌سازی
  38. هاست مناسب برای سایت وردپرسی
  39. پرسش‌های متداول
  40. آیا وردپرس امن است؟
  41. کدام افزونهٔ امنیتی وردپرس را نصب کنیم؟
  42. تغییر پیشوند جدول‌های دیتابیس (wp_) لازم است؟
  43. هاست اشتراکی امن‌تر است یا سرور مجازی؟
  44. اگر افزونهٔ نال‌شده را اسکن کنم و پاک باشد، می‌توانم استفاده کنم؟
  45. جمع‌بندی

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

بیشتر حمله‌ها کار ربات‌هایی است که خودکار دنبال افزونهٔ آسیب‌پذیر و رمز ضعیف می‌گردند. طبق گزارش سالانهٔ Patchstack، از ۱۱٬۳۳۴ آسیب‌پذیری تازهٔ سال ۲۰۲۵، ۹۱٪ در افزونه‌ها و ۹٪ در پوسته‌ها بود و فقط ۶ مورد در هستهٔ وردپرس. مسیر منوها مطابق پیشخوان فارسی وردپرس 7.1 است، آخرین نسخه در زمان نگارش این مطلب (آبان ۱۴۰۵).

به‌طور خلاصه: برای یک وبلاگ یا سایت شرکتی کوچک، این شش کار بیشترین اثر را دارند:

  1. هسته، افزونه‌ها و پوسته‌ها را به‌روز نگه دارید و هر چه استفاده نمی‌کنید، حذف کنید.
  2. قالب یا افزونهٔ نال‌شده نصب نکنید.
  3. برای هر مدیر رمز یکتا و ورود دومرحله‌ای بگذارید.
  4. سایت را کامل روی HTTPS ببرید و DISALLOW_FILE_EDIT را در wp-config.php بگذارید.
  5. هر روز بکاپ بگیرید و یک نسخه را بیرون از هاست نگه دارید.
  6. فقط یک افزونهٔ امنیتی نصب و تنظیم کنید.

فروشگاه‌ها و سایت‌های پربازدید بقیهٔ فهرست را هم اجرا کنند.

به‌روزرسانی هسته، افزونه‌ها و پوسته‌ها

۱. به‌روزرسانی خودکار هسته را خاموش نکنید

وردپرس نسخه‌های جزئی (مثل 7.1.3 به 7.1.4) را که برای رفع اشکال و وصلهٔ امنیتی منتشر می‌شوند، خودکار نصب می‌کند و از وردپرس 5.6، در نصب‌های تازه نسخه‌های اصلی را هم. اگر در wp-config.php ثابت AUTOMATIC_UPDATER_DISABLED یا DISALLOW_FILE_MODS با مقدار true، یا WP_AUTO_UPDATE_CORE با مقدار false هست، وصله‌های امنیتی هم نصب نمی‌شوند؛ اگر عمدی نیست، حذفش کنید. روی هاست داخل ایران، در اختلال اینترنت بین‌الملل ممکن است به‌روزرسانی انجام نشود؛ بعد از وصل شدن، دستی بررسی کنید.

بررسی: در پیشخوان › به‌روزرسانی‌ها نباید پیام «این سایت به‌روزرسانی خودکار نگارش‌های جدید وردپرس را دریافت نخواهد کرد.» را ببینید.

۲. افزونه‌ها و پوسته‌ها را مرتب به‌روز کنید

طبق همان گزارش، مهاجمان حدود نیمی از آسیب‌پذیری‌های پرخطر را در ۲۴ ساعت اول بعد از انتشار به کار گرفته‌اند؛ پس به‌روزرسانی ماهانه دیر است. در افزونه‌ها › افزونه‌های نصب‌شده، لینک «فعال‌سازی به‌روزرسانی‌های خودکار» را برای افزونه‌های ساده‌ای مثل فرم تماس و سئو بزنید (برای پوسته‌ها، در نمایش › پوسته‌ها روی تصویر پوسته کلیک کنید)؛ وردپرس روزی دو بار به‌روزرسانی‌ها را بررسی و نصب می‌کند و نتیجه را ایمیل می‌کند. ووکامرس و درگاه پرداخت را بعد از بکاپ، دستی به‌روز کنید (هفته‌ای یک بار و وصلهٔ امنیتی را همان روز) و یک سفارش آزمایشی بگیرید.

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

wp plugin list --fields=name,version,update,wporg_last_updated

بررسی: در پیشخوان › به‌روزرسانی‌ها نباید موردی در انتظار مانده باشد؛ اگر به‌روزرسانی خودکار انجام نمی‌شود، هشدارهای ابزارها › سلامت سایت را ببینید.

۳. افزونه و پوستهٔ بلااستفاده را حذف کنید

فایل‌های افزونهٔ غیرفعال روی هاست می‌مانند و اگر آسیب‌پذیر باشند، ممکن است مستقیم از مرورگر اجرا شوند؛ پس افزونهٔ بی‌استفاده را غیرفعال و بعد «حذف» کنید. از پوسته‌ها فقط پوستهٔ فعال، پوستهٔ والدش (اگر پوستهٔ فرزند دارید) و یک پوستهٔ پیش‌فرض وردپرس برای عیب‌یابی بماند.

بررسی: در فهرست افزونه‌ها، فیلتر «غیر فعال» دیگر نباید دیده شود.

۴. قالب و افزونهٔ نال‌شده نصب نکنید

نسخهٔ نال‌شده (Nulled) نسخهٔ پولی‌ای است که بررسی لایسنس از کدش حذف شده و در سایت‌های دانلود رایگان پخش می‌شود؛ کسی که کد را دستکاری کرده، می‌تواند هر چیز دیگری هم به آن اضافه کند. بدافزار WP-VCD که Wordfence در سال ۲۰۱۹ گزارش کرد، از همین راه پخش شد و به سایت‌های دیگر همان هاست هم سرایت می‌کرد. اگر خرید نسخهٔ اصلی ممکن نیست، جایگزین رایگانی از مخزن رسمی افزونه‌های وردپرس بگیرید و قالبی را که از سایت دانلود رایگان گرفته‌اید، آلوده فرض کنید.

حساب‌های کاربری و دسترسی‌ها

۵. برای هر حساب رمز یکتا و بلند بگذارید

رمز تکراری از رمز کوتاه هم خطرناک‌تر است: رمزی که در سایت دیگری لو رفته، روی صفحهٔ ورود شما هم امتحان می‌شود. رمز وردپرس، cPanel، FTP، دیتابیس و ایمیل بازیابی هر کدام باید جدا، تصادفی و دست‌کم ۱۶ نویسه‌ای باشد؛ دکمهٔ «تنظیم رمز عبور جدید» در کاربران › شناسنامه چنین رمزی می‌سازد. رمزها را در مدیر رمز عبوری مثل KeePassXC (آفلاین) نگه دارید.

بررسی: در مدیر رمز، هیچ دو حسابی نباید رمز یکسان داشته باشند.

۶. ورود دومرحله‌ای را برای مدیران فعال کنید

با ورود دومرحله‌ای (2FA)، رمز دزدیده‌شده بدون کد شش‌رقمی اپلیکیشن گوشی به کار نمی‌آید. وردپرس 2FA داخلی ندارد؛ اگر افزونهٔ امنیتی‌تان آن را دارد (Wordfence، AIOS و Kadence Security در نسخهٔ رایگان)، از همان استفاده کنید، وگرنه افزونهٔ سبک Two Factor از مشارکت‌کنندگان وردپرس کافی است:

  1. در کاربران › شناسنامه، بخش «گزینه‌های دو عاملی» (Two-Factor Options)، «نرم‌افزار تأیید هویت» را فعال و کد QR را با اپلیکیشنی مثل Google Authenticator اسکن کنید.
  2. «کدهای بازیابی» (Recovery Codes) را هم فعال کنید و کدها را جدا از گوشی نگه دارید.
  3. از همهٔ مدیران و ویرایشگرها بخواهید همین کار را بکنند. روش ایمیلی را روش اصلی نکنید؛ اگر ایمیل سایت نرسد، به پیشخوان راه ندارید.

بررسی: از پنجرهٔ ناشناس مرورگر وارد شوید؛ بعد از رمز باید کد دومرحله‌ای خواسته شود.

۷. کاربر admin و مدیران اضافه را حذف کنید

نام کاربری راز نیست، اما admin اولین حدس ربات‌هاست؛ اگر نام کاربری‌تان این است، مدیر تازه‌ای بسازید و کاربر قدیمی را با گزینهٔ «همهٔ محتوا را به کاربر دیگری نسبت دهید.» حذف کنید. مهم‌تر از نام، تعداد مدیران است؛ هر نفر فقط نقشی را بگیرد که لازم دارد:

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

فهرست مدیران را هر چند ماه مرور کنید:

wp user list --role=administrator --fields=ID,user_login,user_email,user_registered

بررسی: در تنظیمات › عمومی، «نقش پیش‌فرض کاربر تازه» باید «مشترک» باشد و اگر ثبت‌نام عمومی لازم ندارید، «هر کسی می‌تواند نام‌نویسی کند» تیک نخورده باشد.

۸. رمزهای عبور برنامه را بازبینی کنید

رمز عبور برنامه (Application Password) برای اتصال اپلیکیشن‌ها و سرویس‌های خودکارسازی از راه REST API و XML-RPC است؛ با آن نمی‌شود وارد پیشخوان شد، اما دسترسی همان کاربر را دارد و ورود دومرحله‌ای، مثلاً در افزونهٔ Two Factor، عمداً جلویش را نمی‌گیرد. در شناسنامهٔ هر مدیر، بخش «رمزهای عبور برنامه» را با ستون‌های «آخرین استفاده» و «آخرین IP» ببینید و رمز ناشناس را «ابطال» کنید. اگر هیچ سرویسی لازمش ندارد، با این فایل در wp-content/mu-plugins (پوشه را اگر نیست بسازید) خاموشش کنید؛ Wordfence این کار را به‌طور پیش‌فرض می‌کند:

<?php
// wp-content/mu-plugins/disable-application-passwords.php
add_filter( 'wp_is_application_passwords_available', '__return_false' );

بررسی: روی سایت HTTPS، این بخش باید از صفحهٔ شناسنامه حذف شده باشد.

محافظت از صفحهٔ ورود و XML-RPC

۹. تلاش‌های ورود را محدود کنید

وردپرس به‌طور پیش‌فرض تلاش‌های ناموفق ورود را محدود نمی‌کند. Wordfence، Kadence Security و AIOS این محدودیت را در نسخهٔ رایگان دارند؛ مثلاً بعد از ۵ تلاش ناموفق، آن IP را برای مدتی قفل کنید. روی سرور مجازی با Nginx، محدودیت در خود وب‌سرور سبک‌تر است؛ نمونهٔ زیر از مستندات وردپرس است و برای PHP 8.3 روی Ubuntu 24.04 کامل شده. سوکت را با نسخهٔ خودتان تطبیق دهید و بعد sudo nginx -t و sudo systemctl reload nginx را بزنید:

# http {} level (e.g. top of the site file, outside server {})
limit_req_zone $binary_remote_addr zone=logins:10m rate=10r/m;

# inside server {}
location = /wp-login.php {
    limit_req zone=logins burst=20 nodelay;
    include snippets/fastcgi-php.conf;
    fastcgi_pass unix:/run/php/php8.3-fpm.sock;
}

بررسی: با اینترنت گوشی، تا IP اصلی‌تان قفل نشود، چند بار رمز اشتباه بزنید تا پیام قفل‌شدن را ببینید؛ در Nginx بعد از حدود ۲۰ درخواست پیاپی خطای 503 می‌آید.

تغییر نشانی ورود و CAPTCHA: عوض کردن نشانی wp-login.php فقط لاگ‌ها را خلوت می‌کند و طبق مستندات وردپرس نباید تنها دفاع باشد. CAPTCHAهای خارجی مثل reCAPTCHA ممکن است برای بخشی از کاربران داخل ایران درست بارگذاری نشوند و حتی شما را از پیشخوان بیرون نگه دارند؛ برای ورود مدیر، محدودیت تلاش و 2FA کافی است.

۱۰. XML-RPC را اگر لازم ندارید ببندید

فایل xmlrpc.php رابط قدیمی انتشار از راه دور است و طبق مستندات وردپرس، هدف رایج حدس رمز، به‌ویژه با روش system.multicall که چند تلاش را در یک درخواست جا می‌دهد. Jetpack و برخی اپلیکیشن‌ها هنوز به آن نیاز دارند؛ اگر از هیچ‌کدام استفاده نمی‌کنید، در Apache یا LiteSpeed این بلوک را به .htaccess اصلی، بیرون از بلوک # BEGIN WordPress، اضافه کنید:

# Block xmlrpc.php
<Files "xmlrpc.php">
  <IfModule mod_authz_core.c>
    Require all denied
  </IfModule>
  <IfModule !mod_authz_core.c>
    Order allow,deny
    Deny from all
  </IfModule>
</Files>

در Nginx:

location = /xmlrpc.php {
    deny all;
}

بررسی: https://example.com/xmlrpc.php پیش از بستن پیام XML-RPC server accepts POST requests only. و بعد از آن خطای 403 می‌دهد. اگر Jetpack قطع شد، قاعده را بردارید یا طبق راهنمای Jetpack فقط IPهای آن را مجاز کنید.

HTTPS و تنظیمات wp-config.php

۱۱. کل سایت را روی HTTPS ببرید

بدون HTTPS، رمز و کوکی ورود روی وای‌فای عمومی یا هر جای مسیر قابل شنود است. گواهی رایگان Let’s Encrypt یا AutoSSL در cPanel برای بیشتر سایت‌ها کافی است؛ تفاوت گواهی رایگان و پولی را در SSL چیست؟ ببینید و اگر گواهی پولی لازم دارید، صفحهٔ خرید SSL را. بعد از نصب گواهی، در تنظیمات › عمومی «نشانی وردپرس (URL)» و «نشانی سایت (URL)» را با https:// ذخیره کنید، در cPanel › Domains کلید Force HTTPS Redirect را روشن کنید و FORCE_SSL_ADMIN را طبق اقدام بعد اضافه کنید.

بررسی: http://example.com/wp-login.php باید خودکار به https برود و مرورگر هشدار محتوای ناامن (Mixed Content) ندهد.

۱۲. ویرایشگر فایل پیشخوان را خاموش کنید

«ویرایشگر پرونده پوسته» و «ویرایشگر پرونده افزونه» اجازه می‌دهند کد PHP را از داخل پیشخوان ویرایش کنید؛ اگر رمز یک مدیر لو برود، ساده‌ترین راه کاشتن در پشتی (Backdoor) همین است. یک نسخه از wp-config.php (در پوشهٔ اصلی سایت) دانلود کنید و این دو خط را بالای خطی بگذارید که با /* That's all, stop editing! شروع می‌شود:

define( 'DISALLOW_FILE_EDIT', true );
define( 'FORCE_SSL_ADMIN', true );

خط دوم را فقط وقتی بگذارید که HTTPS سایت کار می‌کند، وگرنه به پیشخوان راه ندارید. ثابت اول را با DISALLOW_FILE_MODS اشتباه نگیرید که به‌روزرسانی‌ها را هم می‌بندد. WP_DEBUG را هم روی سایت اصلی روشن نگذارید؛ لاگ پیش‌فرضش در wp-content/debug.log از مرورگر قابل دانلود است.

بررسی: منوهای ویرایشگر پرونده باید حذف شده باشند. اگر صفحهٔ سفید یا خطای 500 دیدید، نقطه‌ویرگول یا نقل‌قولی جا افتاده است؛ نسخهٔ قبلی را برگردانید (راه‌های بیشتر در حل مشکل صفحه سفید وردپرس).

۱۳. کلیدهای امنیتی را تازه کنید

هشت ثابت AUTH_KEY تا NONCE_SALT در wp-config.php کوکی‌های ورود را امضا می‌کنند. اگر هنوز put your unique phrase here دارند یا احتمال لو رفتن فایل هست، هشت خط تازه را از سرویس رسمی کلیدهای وردپرس جایگزین کنید یا wp config shuffle-salts را بزنید. این کار همهٔ کوکی‌ها را باطل می‌کند و همه، از جمله مهاجمی که کوکی دزدیده، باید دوباره وارد شوند.

بررسی: با بارگذاری دوبارهٔ پیشخوان باید به صفحهٔ ورود بروید.

۱۴. مجوز فایل‌ها و wp-config.php را درست کنید

طبق مستندات وردپرس دربارهٔ مجوز فایل‌ها، پوشه‌ها 755، فایل‌ها 644 و wp-config.php سخت‌گیرانه‌تر، 400 یا 440 باشد؛ 777 را هرگز، حتی برای uploads، ندهید. از Terminal یا SSH در cPanel (یا با گزینهٔ Permissions در File Manager):

cd ~/public_html
find . -type d -exec chmod 755 {} \;
find . -type f -exec chmod 644 {} \;
chmod 400 wp-config.php
ls -l wp-config.php

اگر بعد از 400 خطای 500 دیدید، PHP با کاربری غیر از مالک فایل اجرا می‌شود؛ به 644 برگردید و از پشتیبانی هاست بپرسید. برای ویرایش بعدی این فایل، مجوز را موقتاً 600 کنید و بعد به 400 برگردانید.

بررسی: خروجی ls باید با -r-------- شروع شود و سایت عادی باز شود.

امنیت در سطح هاست و سرور

قاعده‌های .htaccess این بخش برای Apache و LiteSpeed است؛ OpenLiteSpeed از .htaccess فقط قاعده‌های بازنویسی را می‌خواند. همهٔ سایت‌های یک حساب cPanel با یک کاربر لینوکس اجرا می‌شوند و آلودگی یکی می‌تواند به بقیه هم برسد؛ سایت‌های آزمایشی و رهاشده را حذف یا جدا کنید. از ارائه‌دهنده هم بپرسید فایروال وب (WAF) سمت سرور، مثل ModSecurity، فعال است یا نه.

۱۵. از نسخهٔ پشتیبانی‌شدهٔ PHP استفاده کنید

وردپرس PHP 8.3 یا جدیدتر را توصیه می‌کند. در زمان نگارش این مطلب (آبان ۱۴۰۵) شاخه‌های 8.2 تا 8.5 وصلهٔ امنیتی می‌گیرند و پشتیبانی 8.2 در ۱۰ دی ۱۴۰۵ (۳۱ دسامبر ۲۰۲۶) تمام می‌شود. نسخهٔ فعلی را در ابزارها › سلامت سایت › اطلاعات، بخش «سرور» ببینید و در cPanel با MultiPHP Manager (یا Select PHP Version در هاست‌های CloudLinux) عوض کنید؛ پیش از آن بکاپ بگیرید و بعد از آن صفحه‌های اصلی و پرداخت را آزمایش کنید.

بررسی: سلامت سایت نباید هشدار نگارش منسوخ PHP بدهد.

۱۶. اجرای PHP را در پوشهٔ uploads ببندید

در wp-content/uploads فقط باید عکس و فایل باشد. اگر افزونه‌ای آسیب‌پذیر آپلود فایل PHP را ممکن کند، مهاجم با باز کردن آن در مرورگر کد خودش را اجرا می‌کند. در Apache یا LiteSpeed این بلوک را در .htaccess داخل خود wp-content/uploads بگذارید (اگر فایل هست، به انتهایش اضافه کنید). این قاعده با هر ترکیب حروف کوچک و بزرگ، پسوندهای .php8 و .phtml را هم که هندلر MultiPHP در cPanel به PHP می‌سپارد و نام‌هایی مثل shell.php.jpg را که Apache با AddHandler ممکن است اجرا کند، می‌بندد:

<FilesMatch "\.(?i:php[0-9]?|phtml|phar)(\.|$)">
  <IfModule mod_authz_core.c>
    Require all denied
  </IfModule>
  <IfModule !mod_authz_core.c>
    Order allow,deny
    Deny from all
  </IfModule>
</FilesMatch>

در Nginx این بلوک از مستندات وردپرس را داخل server {} و پیش از location ~ \.php$ بگذارید، چون Nginx قاعده‌های regex را به ترتیب بررسی می‌کند و اولین تطبیق برنده است:

location ~* /(?:uploads|files)/.*\.php$ {
    deny all;
}

بررسی: یک فایل آزمایشی بسازید، از بیرون صدا بزنید و پاکش کنید. پاسخ باید 403 باشد؛ 200 یعنی قاعده اعمال نشده و باید از ارائه‌دهنده کمک بخواهید:

cd ~/public_html
echo '<?php echo "php-runs";' > wp-content/uploads/test-php.php
curl -s -o /dev/null -w "%{http_code}\n" https://example.com/wp-content/uploads/test-php.php
rm wp-content/uploads/test-php.php

۱۷. فهرست پوشه‌ها و فایل‌های حساس را ببندید

با روشن بودن نمایش فهرست پوشه، هر کسی با باز کردن /wp-content/uploads/ همهٔ فایل‌ها را می‌بیند. این خط‌ها را به .htaccess اصلی، بیرون از بلوک # BEGIN WordPress تا # END WordPress اضافه کنید؛ بخش دوم، قاعدهٔ صفحهٔ Hardening مستندات وردپرس برای بستن دسترسی مستقیم به wp-config.php است:

Options -Indexes

<Files "wp-config.php">
  <IfModule mod_authz_core.c>
    Require all denied
  </IfModule>
  <IfModule !mod_authz_core.c>
    Order allow,deny
    Deny from all
  </IfModule>
</Files>

در cPanel می‌توانید فهرست پوشه را بدون ویرایش فایل هم از Advanced › Indexes با گزینهٔ No Indexing ببندید (در Nginx به‌طور پیش‌فرض خاموش است). اگر بعد از ذخیره خطای 500 گرفتید، هاست تغییر Options را اجازه نمی‌دهد؛ خط اول را بردارید و از پشتیبانی بخواهید. فایل بکاپ (.zip و .sql) را هم هرگز در public_html نگذارید.

بررسی: https://example.com/wp-content/uploads/ باید خطای 403 بدهد، نه فهرست فایل‌ها.

بکاپ، افزونهٔ امنیتی و پایش

۱۸. هر روز بکاپ بگیرید و نسخه‌ای بیرون از هاست نگه دارید

هیچ اقدامی احتمال هک را صفر نمی‌کند؛ بکاپ است که حادثه را به یک بازگردانی چندساعته تبدیل می‌کند. از فایل‌ها و دیتابیس بکاپ بگیرید و نسخه‌ها را چند هفته نگه دارید، چون آلودگی معمولاً دیر کشف می‌شود. نسخهٔ رایگان UpdraftPlus بکاپ زمان‌بندی‌شده را به FTP، فضای سازگار با Amazon S3، Google Drive یا Dropbox می‌فرستد؛ برای مقصد، حسابی جدا با دسترسی فقط به پوشهٔ بکاپ بسازید. روی هاست داخل ایران، سرویس‌های ابری خارجی ممکن است در اختلال اینترنت بین‌الملل در دسترس نباشند؛ گاهی یک نسخه را روی کامپیوتر خودتان هم دانلود کنید.

بررسی: هر ماه یک بکاپ را روی زیردامنه‌ای آزمایشی بازگردانی کنید؛ بکاپی که بازگردانی‌اش امتحان نشده، فقط یک فرض است.

۱۹. فقط یک افزونهٔ امنیتی نصب و تنظیم کنید

افزونهٔ امنیتی جای اقدام‌های قبلی را نمی‌گیرد و دو افزونهٔ هم‌زمان جز کار تکراری و تداخل حاصلی ندارند. نسخه‌های رایگان، طبق صفحهٔ هر افزونه در مخزن وردپرس:

افزونه در نسخهٔ رایگان نکته
Wordfence Security فایروال داخل وردپرس، اسکن بدافزار، محدودیت ورود، 2FA قواعد فایروال و امضاها در نسخهٔ رایگان ۳۰ روز دیرتر می‌رسند
Kadence Security (نام قبلی: Solid Security و iThemes Security) محدودیت ورود، 2FA، تشخیص تغییر فایل، بررسی آسیب‌پذیری‌ها reCAPTCHA و ثبت فعالیت کاربران فقط در Pro
All-In-One Security (AIOS) قفل ورود، 2FA، تشخیص تغییر فایل، قواعد فایروال اسکن بدافزار فقط در نسخهٔ پولی
Sucuri Security ثبت رویدادها، بررسی یکپارچگی فایل‌ها، اسکن از بیرون محدودیت ورود و فایروال فقط با سرویس پولی

برای بیشتر سایت‌ها یکی از سه افزونهٔ اول، با 2FA، محدودیت ورود و اسکن زمان‌بندی‌شده، کافی است؛ فایروال ابری (مثل Cloudflare) برای فروشگاه یا سایتی ارزش دارد که هدف حملهٔ مداوم است. سرویس‌های پولی شرکت‌های آمریکایی ممکن است به‌دلیل تحریم به کاربران ایرانی فروخته نشوند (Sucuri این را صریحاً نوشته است) و بخش ابری افزونه‌ها هم ممکن است از سرور داخل ایران پاسخ نگیرد؛ بعد از نصب، یک اسکن کامل را آزمایش کنید.

۲۰. لاگ فعالیت، تغییر فایل‌ها و Search Console را پایش کنید

  • لاگ فعالیت: اگر افزونهٔ امنیتی‌تان ثبت نمی‌کند چه کسی کی وارد شد یا چه افزونه‌ای نصب کرد، Simple History یا WP Activity Log را نصب کنید.
  • تغییر فایل‌ها: هشدار تغییر فایل PHP بیرون از زمان به‌روزرسانی را جدی بگیرید.
  • Google Search Console: سایت را ثبت کنید تا اگر گوگل محتوای هک‌شده یا بدافزار دید، ایمیل بگیرید؛ گزارش Security issues در بخش Security & Manual Actions است.

پاک‌سازی سایت هک‌شدهٔ وردپرس

ترتیب مهم است: اول دسترسی مهاجم را می‌بندید، بعد کدها را با نسخهٔ سالم جایگزین می‌کنید و در پایان رمزهای وردپرس را عوض می‌کنید تا در پشتی جامانده رمز تازه را نبیند. دستورهای WP-CLI را در پوشهٔ سایت و با --skip-plugins --skip-themes اجرا کنید تا کد آلودهٔ افزونه‌ها و پوسته‌ها اجرا نشود (wp-config.php و mu-pluginها باز هم بارگذاری می‌شوند). اگر SSH ندارید یا پیام wp: command not found گرفتید، راه بدون خط فرمان هر گام را دنبال کنید.

نشانه‌های هک شدن

  • گوگل زیر نتیجهٔ سایت نوشته است This site may be hacked یا کروم صفحهٔ قرمز هشدار نشان می‌دهد؛ وضعیت را با ابزار Safe Browsing site status هم ببینید.
  • جست‌وجوی site:example.com صفحه‌هایی با عنوان ژاپنی، دارویی یا قمار نشان می‌دهد که شما نساخته‌اید.
  • کاربرانی که از گوگل یا با موبایل می‌آیند به سایت دیگری فرستاده می‌شوند، اما شما چیزی نمی‌بینید.
  • مدیر یا افزونهٔ ناشناس یا فایل PHP در uploads پیدا شده، یا هاست به‌خاطر ارسال اسپم حساب را محدود کرده است.

گام ۱: سایت را از دسترس عموم خارج کنید

در cPanel › Files › Directory Privacy، پوشهٔ public_html را انتخاب کنید، گزینهٔ Password protect this directory را بزنید، ذخیره کنید و یک کاربر بسازید تا بازدیدکننده‌ها و ربات‌ها پشت رمز بمانند. حالت تعمیر وردپرس (wp maintenance-mode activate) مناسب نیست، چون وردپرس آن را بعد از ۱۰ دقیقه نادیده می‌گیرد.

گام ۲: یک نسخه نگه دارید و رمزهای هاست را عوض کنید

  1. از فایل‌ها و دیتابیس، در همین وضعیت آلوده، بکاپی با نام روشن (مثلاً infected-1405-08-08) بیرون از هاست نگه دارید و برای پیدا کردن راه نفوذ، لاگ دسترسی وب‌سرور را هم از cPanel › Metrics › Raw Access بردارید.
  2. رمز cPanel، حساب‌های FTP و ایمیل بازیابی را عوض کنید، حساب FTP ناشناس را حذف و در Security › SSH Access کلید ناشناس را پاک کنید.
  3. در Advanced › Cron Jobs دنبال دستوری بگردید که خودتان نساخته‌اید؛ کرون‌جاب مخرب، بدافزار را دوباره نصب می‌کند.

گام ۳: اگر بکاپ سالم دارید، از آن شروع کنید

اگر بکاپی از پیش از تاریخ آلودگی دارید، آن را بازگردانی و بلافاصله همه‌چیز را به‌روز کنید، وگرنه از همان راه دوباره هک می‌شوید. سفارش‌ها و نوشته‌های بعد از آن تاریخ را فقط به‌صورت داده از نسخهٔ آلوده منتقل کنید و گام‌های ۴ تا ۸ را باز هم اجرا کنید؛ ممکن است بکاپ هم آلوده بوده باشد.

گام ۴: هسته را بررسی و با نسخهٔ سالم جایگزین کنید

cd ~/public_html
wp core verify-checksums
wp core download --version="$(wp core version)" --skip-content --force
wp core verify-checksums --include-root

دستور دوم بدون بارگذاری وردپرس، هسته را با چک‌سام رسمی مقایسه می‌کند: File doesn't verify against checksum یعنی فایل دستکاری شده و File should not exist یعنی فایل اضافه، که کنار پیام Success پایانی هم می‌آید؛ پس همهٔ خروجی را بخوانید. دستور سوم هستهٔ همان نسخه را از نو می‌نویسد و به wp-config.php و افزونه‌ها، پوسته‌ها و آپلودها دست نمی‌زند، اما فایل‌های اضافه را باید خودتان حذف کنید. --include-root پوشهٔ اصلی را هم می‌گردد؛ آنجا robots.txt، .user.ini، فایل فایروال افزونهٔ امنیتی و پوشهٔ سایت‌های دیگر طبیعی‌اند، اما فایل PHP ناآشنا مشکوک است.

بدون SSH، گزینهٔ Check WordPress Integrity در WP Toolkit (اگر هاست‌تان دارد) همین بررسی و نصب دوباره را انجام می‌دهد؛ وگرنه بستهٔ همان نسخه را از wordpress.org بگیرید، wp-admin و wp-includes را در File Manager حذف و از بسته دوباره آپلود کنید.

گام ۵: افزونه‌ها و پوسته‌ها را از منبع رسمی دوباره نصب کنید

wp plugin verify-checksums --all --skip-plugins --skip-themes

در جدول خروجی، Checksum does not match یعنی فایل تغییر کرده و File was added یعنی فایل اضافه شده؛ افزونه‌های بیرون از مخزن پیام Could not retrieve the checksums ... skipping می‌گیرند. هر افزونهٔ مشکل‌دار را با نامش از ستون plugin_name به‌جای SLUG دوباره نصب کنید؛ --force پوشهٔ قبلی را پاک و نسخهٔ تازه را جایگزین می‌کند و تنظیمات در دیتابیس می‌ماند:

wp plugin install SLUG --force --skip-plugins --skip-themes
wp plugin verify-checksums SLUG --skip-plugins --skip-themes
  • افزونه و پوستهٔ تجاری را از سازنده دوباره بگیرید. بدون SSH، پوشهٔ هر افزونه را در File Manager حذف و zip تازه‌اش را از wordpress.org آپلود کنید.
  • پوسته‌های مخزن را با wp theme install SLUG --force دوباره نصب کنید؛ WP-CLI برای پوسته چک‌سام ندارد و فایل‌های پوستهٔ فرزند را باید خودتان بخوانید.
  • برای نصب دوباره، افزونه را از پیشخوان «حذف» نکنید؛ ممکن است داده‌ها و تنظیماتش هم پاک شود.

گام ۶: uploads، mu-plugins و فایل‌های پنهان را بگردید

find wp-content/uploads -type f \( -iname '*.php' -o -iname '*.php[0-9]' -o -iname '*.phtml' -o -iname '*.phar' -o -iname '*.php.*' \)
ls -la wp-content/mu-plugins
grep -rlE --include='*.php' 'eval[[:space:]]*\((base64_decode|gzinflate|gzuncompress|str_rot13)' wp-content
grep -rn --include='.htaccess' --include='.user.ini' --include='php.ini' 'auto_prepend_file' .
find . -name '.htaccess'
  • در uploads نباید هیچ فایل PHP باشد؛ هر چه پیدا شد، حذف کنید (نسخه‌اش در بکاپ آلوده هست).
  • فایل‌های mu-plugins خودکار اجرا می‌شوند و در پیشخوان فقط زیر فیلتر «لازم» دیده می‌شوند؛ فایل ناشناس را بررسی کنید.
  • تطبیق دستور سوم الزاماً بدافزار نیست، اما کد مبهم‌شده بیرون از افزونه‌های رسمی بسیار مشکوک است.
  • auto_prepend_file پیش از هر صفحه فایلی را اجرا می‌کند؛ جز فایل فایروال افزونهٔ امنیتی‌تان (مثل wordfence-waf.php یا aios-bootstrap.php)، مقدار ناشناس را بردارید و .htaccessها را هم برای ریدایرکت ناشناس بخوانید.

گام ۷: کاربران، تنظیمات و دیتابیس را بررسی کنید

wp user list --role=administrator --fields=ID,user_login,user_email,user_registered --skip-plugins --skip-themes
wp user application-password list ID --skip-plugins --skip-themes
wp option get admin_email --skip-plugins --skip-themes
wp option get users_can_register --skip-plugins --skip-themes
wp option get default_role --skip-plugins --skip-themes
wp db search '<script' --skip-plugins --skip-themes
  • مدیر ناشناس را با wp user delete ID --reassign=YOUR_ID حذف کنید تا محتوایش به حساب شما برسد.
  • رمز عبور برنامهٔ ناشناس هر مدیر را با wp user application-password delete ID UUID (یا همه را با --all) پاک کنید؛ عوض کردن رمز کاربر و کلیدهای امنیتی آن را باطل نمی‌کند.
  • ایمیل مدیران و admin_email باید مال خودتان باشد؛ بازیابی رمز به ایمیل هر کاربر می‌رود.
  • اگر ثبت‌نام لازم ندارید، users_can_register باید 0 باشد و default_role هرگز administrator نباشد.
  • جست‌وجوی آخر، اسکریپت‌های داخل نوشته‌ها و تنظیمات را نشان می‌دهد و اسکریپتی که به دامنهٔ ناشناس می‌رود، مشکوک است؛ بدون SSH، جدول‌های posts و options را در phpMyAdmin بگردید.

گام ۸: رمزها و کلیدها را عوض کنید و درخواست بررسی بدهید

  1. رمز همهٔ مدیران و رمز کاربر دیتابیس را عوض کنید (cPanel › Manage My Databases، در نسخه‌های قدیمی MySQL Databases، بخش Current Users)، رمز تازه را در DB_PASSWORD بنویسید و wp config shuffle-salts را بزنید؛ اگر مجوز wp-config.php را 400 کرده‌اید، پیش از آن موقتاً 600 کنید.
  2. اقدام‌های این راهنما، به‌ویژه ۱۶ و به‌روزرسانی همهٔ افزونه‌ها، را اجرا کنید تا راه نفوذ بسته شود؛ بعد Directory Privacy را بردارید.
  3. در Search Console، وقتی همهٔ موارد گزارش Security issues در همهٔ صفحه‌ها رفع شد، Request Review را بزنید و بنویسید مشکل چه بود، چه کردید و نتیجه چه شد. طبق مستندات گوگل بررسی از چند روز تا چند هفته طول می‌کشد؛ تا نتیجه نیامده، درخواست تازه نفرستید.
  4. تا چند هفته اسکن روزانه و لاگ فعالیت را دنبال کنید؛ برگشتن آلودگی یعنی در پشتی هنوز باقی است.

اگر آلودگی برگشت یا سایت فروشگاهی با اطلاعات مشتری است، پاک‌سازی را به متخصص بسپارید.

رفع مشکلات رایج بعد از امن‌سازی

نشانه علت محتمل راه‌حل
Error establishing a database connection بعد از تغییر رمز دیتابیس DB_PASSWORD در wp-config.php به‌روز نشده رمز تازه را آنجا بنویسید
ریدایرکت بی‌پایان بعد از FORCE_SSL_ADMIN Cloudflare با حالت Flexible؛ وردپرس HTTPS را نمی‌بیند حالت Full (strict) با گواهی روی خود هاست
پشت ورود دومرحله‌ای یا CAPTCHA مانده‌اید گوشی گم شده یا CAPTCHA بارگذاری نمی‌شود کد بازیابی، یا تغییر نام پوشهٔ افزونه در File Manager

هاست مناسب برای سایت وردپرسی

برای یک سایت وردپرسی معمولی سرور مجازی لازم نیست؛ تقریباً همهٔ این اقدام‌ها روی هاست اشتراکی cPanel اجراشدنی است. هاست لینوکس هاکان سرور بر پایهٔ cPanel و با وب‌سرور LiteSpeed، در ایران و اروپا ارائه می‌شود. برای سایت وردپرسی، صفحه‌های هاست وردپرس و پشتیبانی وردپرس را هم ببینید. پشتیبانی فنی ۲۴ ساعته از طریق تیکت در دسترس است.

اگر وردپرس روی سرور مجازی است، امنیت خود سرور هم با شماست؛ برای آن، ۹ راهکار برای افزایش امنیت سرور لینوکس را ببینید.

پرسش‌های متداول

آیا وردپرس امن است؟

هسته را تیم امنیتی وردپرس نگهداری و خودکار وصله می‌کند؛ بیشتر سایت‌ها از راه افزونه یا پوستهٔ قدیمی، نسخهٔ نال‌شده یا رمز لورفته هک می‌شوند، نه از خود هسته.

کدام افزونهٔ امنیتی وردپرس را نصب کنیم؟

یکی، نه چندتا. Wordfence، Kadence Security و AIOS در نسخهٔ رایگان محدودیت ورود، 2FA و بررسی تغییر فایل‌ها را دارند؛ هر کدام که روی هاست‌تان بدون خطا اسکن می‌کند، کافی است.

تغییر پیشوند جدول‌های دیتابیس (wp_) لازم است؟

برای سایت موجود معمولاً نه. طبق مستندات وردپرس، پیشوند متفاوت فقط جلوی برخی حمله‌های آماده‌ای را می‌گیرد که wp_ را فرض می‌کنند و خود آسیب‌پذیری را رفع نمی‌کند، اما تغییرش روی سایت فعال ممکن است دسترسی مدیران را خراب کند. در نصب تازه، پیشوند دیگری بدهید.

هاست اشتراکی امن‌تر است یا سرور مجازی؟

به مدیریت بستگی دارد: در هاست اشتراکی امنیت سرور با ارائه‌دهنده است، اما در سرور مجازی سیستم‌عامل، وب‌سرور و فایروال هم با شماست. بدون تجربهٔ مدیریت سرور، هاست اشتراکی انتخاب امن‌تری است.

اگر افزونهٔ نال‌شده را اسکن کنم و پاک باشد، می‌توانم استفاده کنم؟

نه. در پشتی را می‌شود طوری مبهم کرد که از اسکنر رد شود، و نسخهٔ نال‌شده هیچ‌وقت وصلهٔ امنیتی رسمی نمی‌گیرد؛ نسخهٔ اصلی یا جایگزین رایگان مخزن وردپرس را نصب کنید.

جمع‌بندی

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

قدم بعدی: امروز اقدام‌های ۱ تا ۷ را انجام دهید، این هفته بکاپ بیرونی و اقدام‌های ۱۲ و ۱۶ را اضافه کنید و پیش از پایان ماه، یک بازگردانی آزمایشی انجام دهید.