CSRF Prevention (پیشگیری از جعل درخواست میان‌سایتی) در وردپرس یکی از پایه‌ای‌ترین و در عین حال پرچالش‌ترین لایه‌های امنیتی است. CSRF (Cross-Site Request Forgery - جعل درخواست میان‌سایتی) نوعی حمله است که در آن مهاجم کاربر احراز هویت‌شده را وادار به ارسال درخواست ناخواسته به سایتی که در آن لاگین است می‌کند. برخلاف XSS که به تزریق کد نیاز دارد، CSRF از اعتماد سایت به مرورگر کاربر سوءاستفاده می‌کند و درخواست را از طرف کاربر معتبر نشان می‌دهد. وردپرس از مکانیزم Nonce (Number used once - عدد یک‌بارمصرف) برای مقابله استفاده می‌کند که در قالب توابع wp_nonce_field()، wp_create_nonce() و wp_verify_nonce() پیاده‌سازی شده است. اما Nonce تنها لایه دفاعی نیست و ترکیب آن با SameSite Cookies، بررسی Origin و Referer، و تأیید مضاعف برای اقدامات حساس ضروری است. در پروژه‌های واقعی، بسیاری از آسیب‌پذیری‌های CSRF از افزونه‌هایی ناشی می‌شود که Nonce را نادیده می‌گیرند یا آن را در جای اشتباه بررسی می‌کنند. بدون درک دقیق مکانیزم Nonce و محدودیت‌های آن، دفاع مؤثر ممکن نیست.

نخستین‌باری که با CSRF روبه‌رو شدیم، در یک افزونه سفارشی بود که عملیات حذف کاربر را با یک درخواست GET انجام می‌داد و هیچ Nonce نداشت. مهاجم می‌توانست با یک تصویر ساده در ایمیل، کاربر مدیر را وادار به حذف حساب کند. از آن زمان، هر جا صحبت از فرم یا درخواست تغییردهنده در وردپرس باشد، CSRF را جدی می‌گیریم. در این نوشتار، از ریشه‌های فنی تا دفاع عملی را بررسی می‌کنیم.

CSRF چیست و چرا خطرناک است؟

CSRF (Cross-Site Request Forgery - جعل درخواست میان‌سایتی) نوعی آسیب‌پذیری است که در آن مهاجم کاربر احراز هویت‌شده را وادار به ارسال درخواست ناخواسته به سایتی می‌کند که در آن لاگین است. برخلاف XSS که هدفش اجرای کد در مرورگر است، CSRF از اعتماد سایت به مرورگر کاربر سوءاستفاده می‌کند. این آسیب‌پذیری در OWASP Top 10 سال ۲۰۱۷ در جایگاه هشتم قرار داشت و اگرچه در نسخه ۲۰۲۱ به دسته «Broken Access Control» منتقل شد، همچنان یکی از جدی‌ترین تهدیدها است.

تفاوت کلیدی CSRF با Clickjacking در این است که در Clickjacking، کاربر «واقعاً» کلیک می‌کند و توکن‌های ضد-CSRF نیز معتبر تلقی می‌شوند؛ اما در CSRF، درخواست بدون اطلاع کاربر و از یک منبع خارجی ارسال می‌شود. برای درک عمیق‌تر این هم‌پوشانی، حملات CSRF و روش‌های دفع را ببینید.

CSRF یک حمله «بی‌صدا» است: هیچ کدی در سایت قربانی اجرا نمی‌شود، هیچ ورودی مخربی تزریق نمی‌شود. فقط یک درخواست معتبر از طرف یک کاربر معتبر ارسال می‌شود.

مسیرهای ورود CSRF در وردپرس

وردپرس در چندین نقطه از فرم‌های تغییردهنده استفاده می‌کند. هر یک از این نقاط، یک سطح حمله بالقوه است:

  • فرم‌های پیشخوان: حذف پست، تغییر نقش کاربر، نصب افزونه.
  • AJAX handlers: درخواست‌های wp_ajax_* و wp_ajax_nopriv_*.
  • REST API: endpointهایی که از Cookie برای احراز هویت استفاده می‌کنند.
  • فرم‌های عمومی: ثبت‌نام، ارسال دیدگاه، فرم تماس.
  • افزونه‌ها: بسیاری از افزونه‌ها Nonce را نادیده می‌گیرند.
  • قالب‌ها: فرم‌های سفارشی که Nonce ندارند.

نکته مهم این است که وردپرس هسته از Nonce در همه فرم‌های حساس استفاده می‌کند، اما این محافظت به‌طور خودکار به افزونه‌ها و قالب‌های شخص‌ثالث منتقل نمی‌شود. برای مرور کلی امنیت وردپرس، امنیت وردپرس را ببینید.

Nonce در وردپرس چگونه کار می‌کند؟

Nonce (Number used once - عدد یک‌بارمصرف) در وردپرس یک توکن امنیتی است که به فرم‌ها و URLها اضافه می‌شود و در سمت سرور بررسی می‌شود. برخلاف نام آن، Nonce وردپرس «یک‌بارمصرف» نیست و به‌مدت ۱۲ تا ۲۴ ساعت معتبر است. این طراحی برای سازگاری با کش و چند تب مرورگر است.

ساخت Nonce:

$nonce = wp_create_nonce( 'my_action' );

افزودن به فرم:

<form method="post">
  <?php wp_nonce_field( 'my_action', 'my_nonce' ); ?>
  <input type="submit" value="ذخیره">
</form>

بررسی در سمت سرور:

if ( ! isset( $_POST['my_nonce'] ) ||
     ! wp_verify_nonce( $_POST['my_nonce'], 'my_action' ) ) {
  wp_die( 'Invalid nonce' );
}

برای درک عمیق‌تر این مکانیزم، نانس وردپرس و امنیت فرم را ببینید.

محدودیت‌های Nonce

Nonce یک راه‌حل قوی است، اما کامل نیست:

  • مدت اعتبار محدود: اگر کاربر فرم را ساعت‌ها باز نگه دارد، Nonce منقضی می‌شود.
  • در دسترس بودن برای XSS: اگر سایت در برابر XSS آسیب‌پذیر باشد، مهاجم می‌تواند Nonce را بخواند.
  • عدم محافظت در برابر Clickjacking: در Clickjacking، فرم واقعاً کلیک می‌شود و Nonce معتبر است.
  • وابستگی به Cookie: اگر Cookie ارسال نشود، Nonce بی‌اثر است.
  • پیاده‌سازی نادرست: بسیاری از توسعه‌دهندگان Nonce را در جای اشتباه بررسی می‌کنند.

به همین دلیل، Nonce باید در ترکیب با لایه‌های دیگر استفاده شود. برای درک عمیق‌تر، پیاده‌سازی نانس در فرم‌های سفارشی را ببینید.

مکانیزم فنی حمله CSRF

یک payload ساده CSRF برای تغییر ایمیل کاربر:

<form action="https://victim.com/wp-admin/profile.php" method="POST">
  <input type="hidden" name="email" value="attacker@evil.com">
  <input type="submit" value="برنده شوید!">
</form>
<script>document.forms[0].submit();</script>

اگر سایت در برابر CSRF محافظت نداشته باشد، مرورگر کاربر Cookie احراز هویت را ارسال می‌کند و وردپرس درخواست را معتبر می‌داند. نوع دیگر، استفاده از درخواست GET:

<img src="https://victim.com/wp-admin/user-new.php?action=delete&user=1">

این نوع حمله به‌ویژه زمانی خطرناک است که عملیات حساس با GET انجام شود. برای دیدن نمونه‌های مشابه، حملات CSRF و جلوگیری از آن را ببینید.

دفاع چندلایه در برابر CSRF

دفاع در چند لایه انجام می‌شود. هیچ لایه‌ای به‌تنهایی کافی نیست.

لایه ۱: Nonce برای همه درخواست‌های تغییردهنده

function myplugin_handle_form() {
  if ( ! isset( $_POST['nonce'] ) ||
       ! wp_verify_nonce( $_POST['nonce'], 'myplugin_action' ) ) {
    wp_die( 'Security check failed' );
  }
  if ( ! current_user_can( 'manage_options' ) ) {
    wp_die( 'Insufficient permissions' );
  }
  // پردازش فرم
}

لایه ۲: بررسی Origin و Referer

$origin = $_SERVER['HTTP_ORIGIN'] ?? '';
$allowed = ['https://example.com'];
if ( ! in_array( $origin, $allowed, true ) ) {
  wp_die( 'Invalid origin' );
}

این بررسی، درخواست‌های خارج از دامنه را رد می‌کند. اما نمی‌توان به‌تنهایی به آن اعتماد کرد، زیرا مهاجم می‌تواند Origin را جعل کند.

لایه ۳: SameSite Cookies

; در php.ini
session.cookie_samesite = "Strict"

یا با تنظیم در وردپرس:

add_filter( 'wp_headers', function( $headers ) {
  $headers['Set-Cookie'] = 'wordpress_test_cookie=WP+Cookie+check; SameSite=Strict';
  return $headers;
} );

لایه ۴: تأیید مضاعف برای اقدامات حساس

برای اقدامات بسیار حساس مانند حذف کاربر یا تغییر رمز عبور، تأیید رمز فعلی یا 2FA را الزامی کنید:

if ( ! wp_check_password( $_POST['current_password'], $current_user->user_pass ) ) {
  wp_die( 'Password confirmation required' );
}

برای درک عمیق‌تر اصول امنیتی، اصول امنیت وب را ببینید.

SameSite Cookies و نقش آن

SameSite یک ویژگی Cookie است که مشخص می‌کند Cookie در درخواست‌های cross-site ارسال شود یا نه. سه مقدار دارد:

مقداررفتارسناریو
StrictCookie فقط در درخواست‌های same-siteپیشخوان وردپرس، بانکداری
LaxCookie در ناوبری top-level و same-siteسایت‌های عمومی
NoneCookie در همه درخواست‌ها (نیاز به Secure)سرویس‌های cross-site

در وردپرس، Cookie احراز هویت به‌طور پیش‌فرض SameSite=Lax است که محافظت نسبی در برابر CSRF فراهم می‌کند. اما این محافظت کامل نیست و باید با Nonce ترکیب شود. برای مرور خطاهای مشابه، خطای کوکی وردپرس را ببینید.

اشتباهات رایج در دفع CSRF

  • نادیده گرفتن Nonce در AJAX: بسیاری از توسعه‌دهندگان Nonce را فقط در فرم‌های HTML بررسی می‌کنند.
  • استفاده از Nonce در جای اشتباه: Nonce باید در سمت سرور بررسی شود، نه در JavaScript.
  • تکیه صرف بر Referer: Referer می‌تواند حذف یا جعل شود.
  • فراموش کردن REST API: endpointهایی که با Cookie احراز هویت می‌کنند، نیازمند Nonce هستند.
  • اعتماد به افزونه‌های شخص‌ثالث: بسیاری از افزونه‌ها Nonce را نادیده می‌گیرند.
  • عدم تأیید مضاعف برای اقدامات حساس: حذف کاربر باید تأیید رمز داشته باشد.
  • نادیده گرفتن SameSite: بدون SameSite، Cookie در درخواست‌های cross-site ارسال می‌شود.

برای مرور خطاهای مشابه در پیکربندی، اشتباهات امنیتی رایج در وردپرس را ببینید.

پرسش‌های پرتکرار درباره CSRF Prevention

آیا وردپرس هسته در برابر CSRF آسیب‌پذیر است؟ هسته از Nonce در همه فرم‌های حساس استفاده می‌کند، اما افزونه‌ها و قالب‌های شخص‌ثالث ممکن است آسیب‌پذیر باشند. برای مرور کلی، امنیت وردپرس را ببینید.

آیا Nonce کافی است؟ نه، زیرا Nonce در برابر XSS و Clickjacking محافظت ایجاد نمی‌کند. باید با SameSite و تأیید مضاعف ترکیب شود.

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

چطور بفهمم سایت من در برابر CSRF آسیب‌پذیر است؟ ابزارهای تست خودکار مانند Burp Suite و OWASP ZAP می‌توانند کمک کنند. برای مرور گزینه‌ها، اسکنرهای آسیب‌پذیری وب را ببینید.

آیا CSRF فقط سایت‌های بزرگ را تهدید می‌کند؟ نه، هر سایتی که فرم تغییردهنده داشته باشد، می‌تواند هدف باشد.

برای مطالعه بیشتر درباره این آسیب‌پذیری، صفحه Cross-site request forgery در ویکی‌پدیا مفید است.

خط پایان

CSRF Prevention یکی از آن لایه‌های امنیتی است که در سایه XSS و SQL Injection کمتر دیده می‌شود، اما پیامدهای آن می‌تواند از تغییر ایمیل تا حذف کامل سایت متغیر باشد. در بستر وردپرس، Nonce یک ابزار قدرتمند است، اما کامل نیست. دفاع مؤثر نیازمند ترکیب Nonce با SameSite Cookies، بررسی Origin، تأیید مضاعف برای اقدامات حساس، و اعتبارسنجی سختگیرانه است. اگر سایت شما فرم تغییردهنده دارد، همین امروز بازبینی کنید.

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