یک درخواست ساده کافی است: curl -s https://example.com/wp-json/wp/v2/users. اگر خروجی JSON پر از slug و name بود، همین حالا نام کاربری همهی نویسندگان و مدیران سایت شما عمومی است. مهاجم لازم نیست حدس بزند؛ فهرست را برمیدارد و بعد فقط رمز عبور را روی همان نامهای واقعی امتحان میکند. این نقطهی شروع اکثر حملات credential stuffing روی وردپرس است.
واکنش اولیهی خیلیها این است که کل REST API را خاموش کنند. این کار جواب میدهد، ولی هزینهاش را بعداً میدهید: ویرایشگر بلوک گوتنبرگ از همان API برای ذخیرهی نوشته استفاده میکند، افزونههای فرم و فروشگاه هم همینطور. سایت به ظاهر سالم بالا میآید و بعد در ذخیرهی پیشنویس یا ثبت سفارش، خطای مبهم میدهد. پس هدف، بستن endpointهای حساس است، نه کشتن کل سرویس.
چرا REST API وردپرس فهرست کاربران را لو میدهد
این رفتار باگ نیست؛ طراحی پیشفرض است. مسیر /wp-json/wp/v2/users برای ساخت رابطهای کلاینتمحور ساخته شده و بهصورت پیشفرض فقط کاربرانی را برمیگرداند که حداقل یک نوشتهی منتشرشده دارند. مشکل اینجاست که در بسیاری از سایتها مدیر هم نویسنده است، پس نام کاربری مدیر هم در همان لیست میآید.
راهحل تمیز، فیلتر کردن این endpoint برای کاربران مهمان است. این کد را در functions.php قالب فرزند یا یک mu-plugin بگذارید:
add_filter( 'rest_endpoints', function( $endpoints ) {
if ( ! is_user_logged_in() ) {
unset( $endpoints['/wp/v2/users'] );
unset( $endpoints['/wp/v2/users/(?P<id>[\d]+)'] );
}
return $endpoints;
} );
بعد از این تغییر، همان curl اول باید {"code":"rest_no_route","message":"No route was found..."} برگرداند. اگر هنوز لیست را میبینید، یعنی کش صفحه یا کش object هنوز نسخهی قدیمی را سرو میکند؛ کش را پاک کنید و دوباره تست بگیرید.
اینجا اشتباه میکنند
خطای رایج این است که فقط مسیر /wp/v2/users را میبندند و مسیر ?author=1 را فراموش میکنند. وردپرس با https://example.com/?author=1 شما را به آرشیو نویسنده ریدایرکت میکند و در URL نهایی، author_name لو میرود. علامتش هم این است که تست REST API شما پاس میشود ولی ابزارهای اسکن بیرونی هنوز نام کاربری را پیدا میکنند. برای بستن این راه، ریدایرکت آرشیو نویسنده را غیرفعال کنید یا با یک قاعدهی rewrite آن را به صفحهی اصلی بفرستید.
محدودسازی endpointهای حساس و rate limit
بستن فهرست کاربران کافی نیست. دو endpoint دیگر هم باید کنترل شوند: /wp/v2/posts که میتواند محتوای پیشنویس را در برخی تنظیمات نشت دهد، و مسیر احراز هویت که در وردپرس هستهای وجود ندارد ولی افزونههای JWT و اپلیکیشنها اضافه میکنند.
برای محدودسازی نرخ درخواست، دو مسیر دارید. اگر به سرور دسترسی دارید، در Nginx این را داخل بلاک server بگذارید:
limit_req_zone $binary_remote_addr zone=wpapi:10m rate=20r/m;
location /wp-json/ {
limit_req zone=wpapi burst=5 nodelay;
limit_req_status 429;
try_files $uri $uri/ /index.php?$args;
}
عدد 20r/m یعنی بیست درخواست در دقیقه از هر IP. برای سایتهای معمولی این عدد سخاوتمندانه است؛ ولی اگر سایت شما اپلیکیشن موبایل دارد یا از صفحهی ویرایشگر بلوک زیاد استفاده میکند، همین عدد باعث خطای 429 برای کاربران واقعی میشود. در آن حالت burst را بالا ببرید یا مسیر /wp-json/wp/v2/ را از محدودیت مستثنا کنید و فقط روی مسیرهای احراز هویت سختگیری کنید.
اگر به کانفیگ سرور دسترسی ندارید، افزونههای امنیتی همین کار را در لایهی PHP انجام میدهند، ولی توجه کنید که هر درخواست هنوز به PHP میرسد و بار پردازشی را پرداخت میکنید. برای سایتهای پربازدید، محدودسازی در لبهی شبکه بهتر از محدودسازی در PHP است. اگر سایت شما هدف حملات حجمی هم هست، ترکیب این تنظیمات با محافظت در برابر DDoS منطقیتر از تکیه بر یک لایه است.
احراز هویت روی REST API وردپرس
وردپرس بهصورت پیشفرض از کوکی و nonce برای احراز هویت REST استفاده میکند. این برای مرورگر خوب است، ولی برای اپلیکیشنهای خارجی کار نمیکند و همین باعث میشود توسعهدهندهها سراغ Application Password یا JWT بروند.
Application Password را از مسیر کاربر → پروفایل فعال کنید. هر رمز به یک دستگاه مشخص گره میخورد و در هر لحظه قابل ابطال است. این را به JWT ترجیح میدهم، چون JWT در پیادهسازیهای وردپرسی معمولاً یک کلید مخفی مشترک دارد که اگر لو برود، تا زمان انقضا هیچ راهی برای باطل کردن توکن صادرشده ندارید. Application Password را میتوانید همان لحظه پاک کنید.
نکتهای که کمتر رعایت میشود: هر Application Password باید فقط برای یک سرویس باشد. اگر یک رمز را بین اسکریپت پشتیبانگیری و افزونهی فروشگاه به اشتراک بگذارید، در زمان نشت، نمیفهمید کدام مسیر لو رفته و مجبورید همه را باطل کنید. برای مدیریت این رمزها در تیم، همان اصولی که در مدیریت رمز عبور در تیمها گفته شده اینجا هم دقیقاً برقرار است.
هدرهای امنیتی که واقعاً جلوی سوءاستفاده را میگیرند
سه هدر روی پاسخهای REST بیشترین اثر را دارند:
X-Content-Type-Options: nosniffتا مرورگر پاسخ JSON را بهعنوان HTML تفسیر نکند.Content-Security-Policyمحدود، تا اگر جایی XSS اجرا شد، نتواند درخواست REST با کوکی کاربر بفرستد.Access-Control-Allow-Originمشخص بهجای*، تا سایتهای دیگر نتوانند از مرورگر کاربر شما درخواست بفرستند.
هدر CORS باز، بهتنهایی یک آسیبپذیری نیست، ولی وقتی با کوکی احراز هویت ترکیب شود، تبدیل به یک CSRF کامل میشود. اگر افزونهای این هدر را باز کرده، همان را در اولویت بگذارید.
پایش و لاگ درخواستهای REST
بدون لاگ، نمیفهمید کسی در حال اسکن endpointهای شماست. حداقل کاری که میتوانید بکنید، لاگ کردن درخواستهای 401 و 429 با IP و مسیر است. در Nginx:
log_format apilog '$remote_addr $status $request_uri $http_user_agent';
access_log /var/log/nginx/wpapi.log apilog;
سپس با یک grep " 429 " /var/log/nginx/wpapi.log | awk '{print $1}' | sort | uniq -c | sort -rn | head سریعترین مهاجمها را میبینید. اگر یک IP در چند دقیقه صدها بار 429 گرفته، وقت بستن آن در فایروال است. این لاگها را جدا از لاگ دسترسی اصلی نگه دارید و مطمئن شوید چرخش (rotation) دارند، وگرنه دیسک سرور را پر میکنند. اصول نگهداری و دستنخورده نگهداشتن این لاگها همان چیزی است که در لاگ ممیزی چیست توضیح داده شده.
کدام گزینه را انتخاب کنم؟
| رویکرد | مزیت | هزینه | کجا انتخاب میکنم |
|---|---|---|---|
| غیرفعالسازی کامل REST API | سطح حملهی کوچک | شکستن گوتنبرگ و افزونهها | سایتهای ایستا و بدون ویرایشگر بلوک |
| فیلتر endpointها | امنیت بدون شکستن سایت | نیاز به نگهداری کد | انتخاب پیشفرض من برای اکثر سایتها |
| محدودسازی نرخ در Nginx | بار روی PHP نمیآورد | نیاز به دسترسی سرور | سایتهای پربازدید و هدف حملات |
اگر فقط یک کار میتوانید انجام دهید، فیلتر کردن /wp/v2/users و بستن ?author= را انجام دهید. این دو، بیشترین بازدهی را با کمترین ریسک شکستن سایت دارند. محدودسازی نرخ را بعد از آن اضافه کنید، وقتی مطمئن شدید ترافیک عادی سایت را نمیبندد.
قبل از هر تغییری، یک نسخهی پشتیبان از دیتابیس و فایلها بگیرید و بعد از اعمال، با curl و یک مرورگر واقعی تست کنید. اگر سایت شما روی زیرساخت سرورنت میزبانی میشود و میخواهید این لایهها را از سمت شبکه هم تقویت کنید، خدمات امنیت نقطهی شروع منطقی است. برای بررسی سریع رکوردهای DNS و مسیرهای شبکه هم ابزارهای بررسی DNS و شبکه کار را ساده میکند.
پرسشهای پرتکرار
آیا غیرفعال کردن کامل REST API وردپرس امنتر است؟
نه لزوماً. غیرفعالسازی کامل سطح حمله را کم میکند، ولی ویرایشگر بلوک، افزونههای فرم و بسیاری از افزونههای فروشگاهی از همان API استفاده میکنند و از کار میافتند. در بیشتر موارد، فیلتر کردن endpointهای حساس مثل فهرست کاربران نتیجهی بهتری میدهد.
چطور بفهمم REST API وردپرس سایت من فهرست کاربران را لو میدهد؟
یک درخواست ساده به https://example.com/wp-json/wp/v2/users بفرستید. اگر پاسخ JSON شامل نام و slug کاربران بود، فهرست شما عمومی است. مسیر ?author=1 را هم جداگانه تست کنید، چون ممکن است یکی بسته باشد و دیگری باز.
محدودسازی نرخ درخواست روی REST API چه عددی باید باشد؟
برای سایتهای معمولی بیست درخواست در دقیقه از هر IP نقطهی شروع خوبی است. اگر اپلیکیشن موبایل یا کاربران زیادی با ویرایشگر بلوک دارید، این عدد باعث خطای 429 برای کاربران واقعی میشود و باید burst را بالا ببرید یا مسیرهای خواندنی را مستثنا کنید.
Application Password امنتر است یا JWT؟
برای اکثر سایتها Application Password را ترجیح میدهم، چون هر رمز به یک دستگاه گره میخورد و در هر لحظه قابل ابطال است. JWT در پیادهسازیهای وردپرسی معمولاً یک کلید مشترک دارد و باطل کردن توکن صادرشده در آن ساده نیست.
دیدگاهها ۰
هنوز دیدگاهی ثبت نشده؛ اولین نفر باشید!