چرا فرم شما در برابر CSRF آسیبپذیر است؟ راهنمای wp_nonce_field
تابع wp_nonce_field برای افزودن فیلد nonce به فرمهای وردپرس؛ بررسی پارامترها، action، name، referer و اشتباهات رایج در امنیت فرم.
چرا CSRF یک تهدید جدی است؟
حمله CSRF (Cross-Site Request Forgery) یکی از رایجترین و در عین حال نامرئیترین حملات وب است. در این حمله، مهاجم کاربر واردشده به سایت شما را فریب میدهد تا بهصورت ناخواسته درخواستی به سایت ارسال کند. برای نمونه، کاربری که در فروشگاه شما وارد شده است، به یک صفحه آلوده هدایت میشود. آن صفحه یک فرم مخفی به سایت شما ارسال میکند که درخواست حذف محصول یا تغییر ایمیل کاربر را میفرستد. اگر سایت شما این درخواست را بدون بررسی nonce بپذیرد، حمله موفق میشود. تابعwp_nonce_field ابزار اصلی وردپرس برای جلوگیری از این حمله است.
تابع wp_nonce_field چیست؟
تابعwp_nonce_field() یک تابع هسته وردپرس است که در فایل wp-includes/functions.php تعریف شده است. این تابع دو فیلد مخفی HTML به فرم اضافه میکند:
- _wpnonce: توکن امنیتی
- _wp_http_referer: آدرس صفحه ارسالکننده
این دو فیلد به همراه فرم ارسال میشوند و در سمت سرور با توابع wp_verify_nonce یا check_admin_referer بررسی میشوند.
نکته مهم: nonce یک توکن یکبارمصرف نیست. این توکن تا ۱۲ تا ۲۴ ساعت معتبر است و پس از آن منقضی میشود. این رفتار برای فرمهای طولانی و AJAX مفید است اما نیاز به بازخوانی دارد.
امضای تابع و پارامترها
امضای این تابع بهشکل زیر است:function wp_nonce_field( $action = -1, $name = '_wpnonce', $referer = true, $display = true ) {
// ...
}
پارامتر اول (action) نام عمل است که باید یکتا باشد. مقدار پیشفرض -1 است.
پارامتر دوم (name) نام فیلد مخفی است. مقدار پیشفرض _wpnonce است.
پارامتر سوم (referer) اگر true باشد، فیلد _wp_http_referer نیز اضافه میشود.
پارامتر چهارم (display) اگر true باشد، فیلدها بهصورت مستقیم چاپ میشوند. اگر false باشد، بهصورت رشته بازگردانده میشوند.
خروجی تابع در حالت display => true، چاپ مستقیم فیلدها و در حالت false، رشته HTML است.
نقش action در امنیت
پارامترaction نقش کلیدی در امنیت دارد. این پارامتر تعیین میکند که nonce مربوط به چه عملیاتی است. اگر مهاجم بتواند یک nonce معتبر برای حذف محصول کشف کند، نمیتواند از آن برای تغییر تنظیمات استفاده کند.
الگوی صحیح انتخاب action:
- نام action باید یکتا و معنادار باشد
- از prefix اختصاصی استفاده کنید
- به ازای هر عملیات، یک action جداگانه داشته باشید
نمونه:
wp_nonce_field( 'myplugin_save_settings', 'myplugin_nonce' );
wp_nonce_field( 'myplugin_delete_item', 'myplugin_delete_nonce' );
wp_nonce_field( 'myplugin_update_profile', 'myplugin_profile_nonce' );
نکته مهم: نام action باید در سمت سرور با همان مقدار بررسی شود، وگرنه nonce معتبر نخواهد بود.
پارامتر referer و بررسی اضافی
پارامترreferer باعث اضافه شدن فیلد _wp_http_referer میشود که آدرس صفحه ارسالکننده را در خود نگه میدارد. این فیلد امنیت را در برابر حملات خاص افزایش میدهد.
نکته مهم: در برخی موارد مانند ارسال فرم از یک iframe یا از یک دامنه دیگر، ممکن است referer خالی باشد. برای همین، بررسی referer بهتنهایی کافی نیست و باید با nonce ترکیب شود.
اگر میخواهید فیلد referer را حذف کنید:
wp_nonce_field( 'myplugin_action', '_wpnonce', false );
بررسی nonce در سمت سرور
پس از ارسال فرم، باید nonce در سمت سرور بررسی شود. دو تابع اصلی برای این کار وجود دارد: **wp_verify_nonce**:if ( ! isset( $_POST['myplugin_nonce'] ) ) {
wp_die( 'درخواست نامعتبر' );
}
$nonce = sanitize_text_field( wp_unslash( $_POST['myplugin_nonce'] ) );
if ( ! wp_verify_nonce( $nonce, 'myplugin_save_settings' ) ) {
wp_die( 'درخواست نامعتبر' );
}
**check_admin_referer**:
check_admin_referer( 'myplugin_save_settings', 'myplugin_nonce' );
تابع check_admin_referer اگر nonce نامعتبر باشد، بهصورت خودکار پاسخ -1 با کد وضعیت ۴۰۳ ارسال میکند و پردازش را متوقف میسازد.
نکته مهم: راهنمای این توابع در صفحه wp_verify_nonce و صفحه check_admin_referer آمده است.
کاربردهای عملی در فرمها
فرم تنظیمات افزونه:function myplugin_render_settings_form() {
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( 'دسترسی غیرمجاز' );
}
$settings = get_option( 'myplugin_settings', array() );
?>
<form method="post" action="">
<?php wp_nonce_field( 'myplugin_save_settings', 'myplugin_nonce' ); ?>
<label>
<input type="checkbox" name="enabled" value="1"
<?php checked( ! empty( $settings['enabled'] ) ); ?>>
فعالسازی افزونه
</label>
<button type="submit" name="myplugin_submit">ذخیره</button>
</form>
<?php
}
پردازش فرم پس از ارسال:
add_action( 'admin_init', 'myplugin_handle_settings_form' );
function myplugin_handle_settings_form() {
if ( ! isset( $_POST['myplugin_submit'] ) ) {
return;
}
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( 'دسترسی غیرمجاز' );
}
check_admin_referer( 'myplugin_save_settings', 'myplugin_nonce' );
$settings = array(
'enabled' => ! empty( $_POST['enabled'] ),
);
update_option( 'myplugin_settings', $settings );
wp_safe_redirect( add_query_arg( 'updated', 'true', wp_get_referer() ) );
exit;
}
راهنمای توابع استفادهشده: current_user_can، update_option، get_option، check_admin_referer.
نقش در AJAX
در درخواستهای AJAX، نمیتوان ازwp_nonce_field در فرم HTML استفاده کرد چرا که درخواست بهصورت برنامهنویسی ارسال میشود. در عوض، از wp_create_nonce استفاده میکنید و nonce را به JavaScript پاس میدهید:
wp_localize_script(
'myplugin-script',
'mypluginData',
array(
'ajaxUrl' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'myplugin_ajax_action' ),
)
);
در سمت سرور:
function myplugin_ajax_handler() {
check_ajax_referer( 'myplugin_ajax_action', 'nonce' );
// ادامه پردازش
}
راهنمای توابع AJAX در صفحه wp_localize_script، صفحه check_ajax_referer، صفحه هوک wp_ajax و صفحه هوک wp_ajax_nopriv آمده است.
نکات امنیتی و اشتباهات رایج
اشتباه اول، نبود nonce است. اگر nonce بررسی نشود، حمله CSRF ممکن است. اشتباه دوم، نبود بررسی در سمت سرور است. تنها اضافه کردنwp_nonce_field کافی نیست، باید nonce در سمت سرور بررسی شود.
اشتباه سوم، استفاده از action یکسان برای چند عملیات است. هر عملیات باید action اختصاصی داشته باشد.
اشتباه چهارم، نبود بررسی capability است. nonce تنها ثابت میکند درخواست از طرف خود کاربر است، اما نمیگوید که کاربر مجاز است. راهنمای این تابع در صفحه current_user_can آمده است.
اشتباه پنجم، ذخیره nonce در کش است. اگر صفحهای که nonce دارد کش شود، nonce همه کاربران یکسان میشود و امنیت از بین میرود. در این حالت باید از Fragment Cache استفاده کنید.
اشتباه ششم، استفاده از nonce برای درخواستهای بدون احراز هویت است. برای کاربران مهمان، باید Rate Limiting و بررسیهای اضافی انجام شود.
اشتباه هفتم، نبود escape در فرم است. اگر nonce را در فرم بدون escape چاپ کنید، حفره XSS ایجاد میشود. راهنمای این تابع در صفحه esc_html آمده است.
اشتباه هشتم، نبود تست است. باید در سناریوهای nonce معتبر، nonce نامعتبر، نبود nonce و CSRF تست کنید.
تحلیل فنی پیشرفته
در نگاه مهندسی، تابعwp_nonce_field() یک نقطه معماری در لایه Form Security است که بر چند لایه سیستم اثر میگذارد. لایه اول لایه Token Generation است. nonce با ترکیب اطلاعات کاربر، action و نمک مخفی سایت (NONCE_SALT) تولید میشود. این مکانیزم از پیشبینیپذیری جلوگیری میکند.
لایه دوم لایه Time Window است. nonce در بازه زمانی ۱۲ تا ۲۴ ساعت معتبر است و پس از آن منقضی میشود. این مکانیزم، پنجره حمله را محدود میکند.
لایه سوم لایه Action Binding است. nonce به action گره میخورد و این یعنی nonce یک عملیات برای عملیات دیگر کار نمیکند.
لایه چهارم لایه User Binding است. برای کاربران واردشده، nonce به شناسه کاربر گره میخورد. برای کاربران مهمان، به نشست و آدرس IP. این تفاوت در محافظت از CSRF مؤثر است.
لایه پنجم لایه Cache Compatibility است. اگر فرمی که nonce دارد کش شود، مشکل امنیتی جدی رخ میدهد. برای همین باید فرمهای دارای nonce از کش مستثنی شوند.
لایه ششم لایه Multisite است. در شبکههای Multisite، nonce در هر سایت مستقل کار میکند.
لایه هفتم لایه Testing است. تستهای امنیتی باید همه سناریوها را پوشش دهند.
مفاهیم پایهای CSRF Token در Cross-site request forgery در ویکیپدیا توضیح داده شده است.
برای مطالعه بیشتر روی توابع مرتبط، میتوانید به راهنمای wp_verify_nonce، راهنمای check_admin_referer، راهنمای check_ajax_referer، راهنمای wp_localize_script، راهنمای هوک wp_ajax، راهنمای هوک wp_ajax_nopriv و راهنمای current_user_can مراجعه کنید.
پرسشهای پرتکرار
nonce چقدر معتبر است؟ معمولاً ۱۲ تا ۲۴ ساعت. تفاوتwp_nonce_field و wp_create_nonce چیست؟ اولی فیلد HTML تولید میکند و دومی رشته nonce برمیگرداند.
آیا nonce بهتنهایی کافی است؟ خیر، باید با بررسی capability ترکیب شود.
آیا میتوان از nonce در فرمهای کششده استفاده کرد؟ توصیه نمیشود. باید صفحه از کش مستثنی شود.
آیا nonce در Multisite بین سایتها کار میکند؟ خیر، در هر سایت مستقل است.
ادامه مسیر
تابعwp_nonce_field() ابزار اصلی وردپرس برای محافظت از فرمها در برابر CSRF است. استفاده درست از آن یعنی تعریف action یکتا، بررسی nonce در سمت سرور، ترکیب با بررسی capability، توجه به کش و تست در سناریوهای مختلف. اشتباههای کوچک در این تابع اغلب به حفرههای امنیتی جدی منجر میشوند.
اگر این تابع را در پروژهای واقعی به کار بردهاید و رفتار غیرمنتظرهای دیدهاید — بهخصوص در ترکیب با کش یا در AJAX عمومی — تجربهتان میتواند راهگشای دیگران باشد. کدام بخش بیشترین زمان را از شما گرفت؟ دیدگاه خود را بنویسید.