Nonce در وردپرس (نانس) یک توکن امنیتی یک‌بارمصرف است که به‌عنوان ستون فقرات دفاع در برابر CSRF (Cross-Site Request Forgery - جعل درخواست میان‌سایتی) در فرم‌ها و درخواست‌های AJAX عمل می‌کند. این توکن که با توابع wp_create_nonce()، wp_nonce_field() و wp_verify_nonce() پیاده‌سازی می‌شود، به هر فرم یا درخواست یک امضای منحصربه‌فرد متصل به نشست کاربر و اکشن موردنظر اضافه می‌کند. بدون Nonce، مهاجم می‌تواند با یک صفحه مخرب، کاربر مدیر را وادار به ارسال درخواست ناخواسته کند و عملیات حساسی مانند حذف کاربر، تغییر رمز عبور یا نصب افزونه را اجرا نماید. امنیت فرم وردپرس به‌طور مستقیم به Nonce وابسته است، زیرا این توکن ثابت می‌کند درخواست از یک منبع معتبر و از طرف کاربر واقعی ارسال شده است. در این نوشتار، از ریشه‌های فنی Nonce تا پیاده‌سازی حرفه‌ای آن در فرم‌ها، AJAX و REST API را بررسی می‌کنیم و نشان می‌دهیم چرا نادیده گرفتن آن، یک بدهی امنیتی پنهان و خطرناک است.

نخستین‌باری که یک آسیب‌پذیری CSRF را در یک افزونه سفارشی کشف کردیم، علت دقیقاً غیبت Nonce در یک درخواست AJAX بود. مهاجم می‌توانست با یک صفحه ساده، کاربر مدیر را وادار به تغییر تنظیمات حساس کند. از آن زمان، Nonce را نه به‌عنوان یک توصیه، بلکه به‌عنوان یک قانون قطعی در همه فرم‌ها و درخواست‌های تغییردهنده اعمال می‌کنیم. این نوشتار، حاصل تجربه عملی در پیاده‌سازی و عیب‌یابی Nonce در ده‌ها پروژه وردپرسی است.

Nonce در وردپرس چیست؟

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

Nonce در وردپرس از ترکیب چند عنصر ساخته می‌شود: یک مقدار تصادفی، شناسه کاربر (User ID)، اکشن موردنظر و یک بازه زمانی (tick). همین ترکیب، توکن را به کاربر و اکشن خاص گره می‌زند و امکان بازاستفاده توسط مهاجم را از بین می‌برد. برای درک عمیق‌تر این مکانیزم، نانس وردپرس و امنیت فرم را ببینید.

سه تابع اصلی Nonce در وردپرس عبارتند از:

  • wp_create_nonce( $action ): ساخت Nonce برای یک اکشن مشخص.
  • wp_nonce_field( $action, $name ): افزودن فیلد مخفی Nonce به فرم.
  • wp_verify_nonce( $nonce, $action ): بررسی اعتبار Nonce در سمت سرور.

علاوه بر این، توابع check_admin_referer()، check_ajax_referer() و wp_nonce_url() نیز برای زمینه‌های خاص طراحی شده‌اند. برای درک جایگاه این مفهوم در امنیت وب، اصول امنیت وب را ببینید.

Nonce یک «امضای دیجیتال سبک» است: ثابت می‌کند درخواست از طرف کاربر واقعی و برای اکشن مشخصی ارسال شده است. بدون آن، هر درخواست معتبر به‌نظر می‌رسد.

چرا امنیت فرم به Nonce وابسته است؟

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

در وردپرس، فرم‌های حساس متعددی وجود دارند که بدون Nonce می‌توانند قربانی شوند:

  • حذف کاربر یا تغییر نقش او.
  • نصب، فعال‌سازی یا حذف افزونه.
  • تغییر رمز عبور یا ایمیل مدیر.
  • حذف پست یا برگه.
  • تغییر تنظیمات سایت.
  • تأیید پرداخت در فروشگاه‌های ووکامرس.

یک مثال ساده از حمله CSRF بدون Nonce:

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

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

مکانیزم فنی Nonce

Nonce وردپرس از ترکیب چهار عنصر ساخته می‌شود:

$nonce = substr( wp_hash( $tick . '|' . $action . '|' . $uid . '|' . $token ), -12, 10 );

در این فرمول:

  • $tick: بازه زمانی ۱۲ ساعته که هر ۱۲ ساعت تغییر می‌کند.
  • $action: نام اکشن موردنظر (مثلاً save_settings).
  • $uid: شناسه کاربر جاری.
  • $token: توکن نشست کاربر که در Cookie ذخیره می‌شود.

نتیجه، یک رشته ۱۰ کاراکتری است که به‌عنوان Nonce استفاده می‌شود. همین ترکیب، Nonce را به کاربر و اکشن گره می‌زند. اگر مهاجم Nonce یک کاربر را بدزدد، نمی‌تواند آن را برای کاربر دیگری استفاده کند. برای درک عمیق‌تر ساختار امنیتی، نوشتن کد PHP امن برای وردپرس را ببینید.

Nonce و Nonce Life

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

  • Nonce Life: بازه جاری که ۱۲ ساعت است.
  • Nonce Life (قدیمی): بازه قبلی که در ۱۲ ساعت اول پذیرفته می‌شود.

این یعنی یک Nonce در مجموع تا ۲۴ ساعت معتبر است. این طراحی، تجربه کاربری را در چند تب باز بهبود می‌بخشد، اما سطح حمله را کمی افزایش می‌دهد. برای مرور محدودیت‌ها، پیاده‌سازی نانس در فرم‌های سفارشی را ببینید.

چرخه عمر و مدت اعتبار Nonce

Nonce وردپرس سه مرحله را طی می‌کند:

مرحلهوضعیتمدت
تولیدNonce ساخته و به فرم اضافه می‌شودلحظه نمایش فرم
اعتبارNonce پذیرفته می‌شود۰ تا ۲۴ ساعت
انقضاNonce رد می‌شودبعد از ۲۴ ساعت

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

در پروژه‌های واقعی، این محدودیت می‌تواند چالش‌برانگیز باشد. اگر کاربر فرمی را ساعت‌ها باز نگه دارد و سپس ارسال کند، Nonce منقضی می‌شود و پیام «لینک منقضی شده» نمایش داده می‌شود. راه‌حل، رفرش دوره‌ای Nonce با JavaScript یا نمایش پیام راهنما است. برای مرور خطاهای مشابه، اشتباهات امنیتی رایج در وردپرس را ببینید.

پیاده‌سازی Nonce در فرم‌ها و AJAX

پیاده‌سازی Nonce در سه سطح انجام می‌شود:

سطح ۱: فرم‌های HTML

<?php wp_nonce_field( 'myplugin_save_settings', 'myplugin_nonce' ); ?>

در سمت سرور:

if ( ! isset( $_POST['myplugin_nonce'] ) ||
     ! wp_verify_nonce( $_POST['myplugin_nonce'], 'myplugin_save_settings' ) ) {
  wp_die( 'Security check failed' );
}

سطح ۲: درخواست‌های AJAX

// در JavaScript
jQuery.post( ajaxurl, {
  action: 'myplugin_save',
  nonce: mypluginData.nonce,
  data: formData,
} );

// در PHP
add_action( 'wp_ajax_myplugin_save', function() {
  check_ajax_referer( 'myplugin_save', 'nonce' );
  // پردازش
} );

برای تزریق Nonce به JavaScript:

wp_localize_script( 'myplugin-script', 'mypluginData', [
  'nonce' => wp_create_nonce( 'myplugin_save' ),
  'ajaxurl' => admin_url( 'admin-ajax.php' ),
] );

سطح ۳: URLها

$url = wp_nonce_url( admin_url( 'admin.php?page=myplugin&action=delete&id=' . $id ), 'myplugin_delete_' . $id );

در سمت سرور:

check_admin_referer( 'myplugin_delete_' . $id );

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

Nonce در REST API وردپرس

در REST API وردپرس، Nonce از طریق هدر X-WP-Nonce ارسال می‌شود. این هدر به‌طور خودکار توسط wp-api-fetch در JavaScript مدیریت می‌شود، اما در درخواست‌های دستی باید صریحاً اضافه شود:

fetch( '/wp-json/myplugin/v1/settings', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'X-WP-Nonce': wpApiSettings.nonce,
  },
  body: JSON.stringify( data ),
} );

در سمت سرور، وردپرس به‌طور خودکار Nonce را بررسی می‌کند اگر کاربر با Cookie احراز هویت شده باشد. اما برای endpointهای حساس، توصیه می‌شود بررسی Nonce را صریحاً انجام دهید. برای درک عمیق‌تر REST API، استفاده از REST API در وردپرس را ببینید.

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

Nonce یک ابزار قدرتمند است، اما کامل نیست:

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

به همین دلیل، Nonce باید با لایه‌های دیگر ترکیب شود:

  • SameSite Cookies: محدودسازی ارسال Cookie در درخواست‌های cross-site.
  • بررسی Origin و Referer: لایه دفاعی اضافی.
  • تأیید مضاعف: برای اقدامات حساس، تأیید رمز فعلی.
  • 2FA: برای ورود و اقدامات حساس.

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

اشتباهات رایج در استفاده از Nonce

  • نادیده گرفتن Nonce در AJAX: بسیاری از توسعه‌دهندگان Nonce را فقط در فرم‌های HTML بررسی می‌کنند.
  • بررسی Nonce در JavaScript: Nonce باید در سمت سرور بررسی شود.
  • استفاده از یک Nonce برای چند اکشن: هر اکشن باید Nonce جداگانه داشته باشد.
  • اعتماد به Nonce به‌تنهایی: باید با بررسی سطح دسترسی ترکیب شود.
  • فراموش کردن REST API: endpointهایی که با Cookie احراز هویت می‌کنند، نیازمند Nonce هستند.
  • نادیده گرفتن Nonce در URLهای حذف: حذف از طریق GET باید Nonce داشته باشد.
  • عدم مدیریت انقضا: باید پیام مناسب برای Nonce منقضی نمایش داده شود.
  • استفاده از wp_verify_nonce بدون بررسی وجود: باید وجود فیلد را هم بررسی کنید.

برای درک عمیق‌تر XSS، راهنمای پیشگیری از حملات XSS را ببینید.

پرسش‌های پرتکرار درباره Nonce در وردپرس

آیا Nonce در وردپرس واقعاً یک‌بارمصرف است؟ نه، برخلاف نام آن، Nonce وردپرس به‌مدت ۱۲ تا ۲۴ ساعت معتبر است و می‌توان از آن چندین بار استفاده کرد.

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

آیا Nonce در برابر Clickjacking محافظت می‌کند؟ نه، در Clickjacking کاربر واقعاً کلیک می‌کند و Nonce معتبر است. برای دفاع در برابر Clickjacking، از هدرهای X-Frame-Options و frame-ancestors استفاده کنید.

آیا افزونه‌های امنیتی Nonce را خودکار اضافه می‌کنند؟ نه، Nonce باید در کد فرم‌ها و درخواست‌های AJAX اضافه شود. افزونه‌های امنیتی می‌توانند لایه‌های اضافی فراهم کنند، اما جایگزین Nonce نیستند. برای مرور کلی، امنیت وردپرس را ببینید.

چطور بفهمم فرم من Nonce دارد؟ کد فرم را جست‌وجو کنید. هر فرمی که عملیات تغییردهنده انجام می‌دهد، باید wp_nonce_field() داشته باشد.

آیا استفاده از Nonce روی عملکرد سایت تأثیر دارد؟ تأثیر آن حداقلی است، زیرا محاسبه Nonce سریع و بهینه است. برای مرور خطاهای مشابه، اشتباهات امنیتی رایج در وردپرس را ببینید.

برای مطالعه بیشتر درباره این مفهوم، صفحه Cryptographic nonce در ویکی‌پدیا مفید است.

خط پایان

Nonce یکی از آن لایه‌های امنیتی است که در سایه XSS و SQL Injection کمتر دیده می‌شود، اما در واقع «قفل اصلی» فرم‌های وردپرس است. بدون Nonce، هر فرم تغییردهنده می‌تواند قربانی CSRF شود و مهاجم می‌تواند بدون اطلاع کاربر، عملیات حساسی اجرا کند. در بستر وردپرس، توابع Nonce متعددی وجود دارد که بسیاری از آن‌ها نادیده گرفته می‌شوند. دفاع مؤثر نیازمند استفاده از Nonce در همه فرم‌ها، AJAX و REST API، ترکیب آن با بررسی سطح دسترسی، و افزودن لایه‌های تکمیلی مانند SameSite است. اگر سایت شما فرم تغییردهنده دارد، همین امروز کد را بازبینی کنید.

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