مقاله امنیت

۱۱٬۳۳۴ آسیب‌پذیری در یک سال؛ افزونهٔ وردپرس را قبل از نصب چطور بسنجیم

گزارش Patchstack می‌گوید در سال ۲۰۲۵، ۹۱٪ آسیب‌پذیری‌های تازهٔ وردپرس در افزونه‌ها بوده و نزدیک نیمی از آن‌ها هنگام افشا هنوز وصله نداشته‌اند. این‌جا دو چک‌لیست می‌گذارم: یکی برای وقتی افزونه نصب می‌کنید، یکی برای وقتی افزونه می‌نویسید.

حمید اعراب توسعه‌دهنده ارشد بک‌اند وردپرس و ووکامرس
انتشار
زمان مطالعه
۴ دقیقه مطالعه

حمید اعراب ۲۷ مهر ۱۴۰۵ ۴ دقیقه مطالعه

جلد پیکسلی: قفل و دو باگ، با آمار آسیب‌پذیری‌های ۲۰۲۵
فهرست مطالب (9)

هر وقت سایتی برای «نجات» به دستم می‌رسد، اولین سؤالم این نیست که «چه چیزی هک شده؟» می‌پرسم «چند افزونه نصب است و آخرین بار کِی کسی فهرستشان را نگاه کرده؟» تقریباً همیشه جواب بخش بزرگی از ماجرا را روشن می‌کند.

گزارش سالانهٔ Patchstack دربارهٔ امنیت وردپرس در ۲۰۲۶، همین تجربه را با عدد نشان می‌دهد.

عددهایی که باید جدی گرفت

طبق این گزارش، در سال ۲۰۲۵:

  • ۱۱٬۳۳۴ آسیب‌پذیری تازه در اکوسیستم وردپرس پیدا شد؛ ۴۲٪ بیشتر از سال قبل.
  • ۹۱٪ در افزونه‌ها بود و ۹٪ در قالب‌ها. خود هستهٔ وردپرس فقط ۶ مورد گزارش‌شده داشت که همه کم‌اهمیت بودند.
  • ۴۶٪ آسیب‌پذیری‌ها تا زمان افشای عمومی هنوز وصله نشده بودند.
  • حدود نیمی از آسیب‌پذیری‌های پرخطر در ۲۴ ساعت اول مورد سوءاستفاده قرار گرفتند. برای پرهدف‌ترین‌ها، میانهٔ وزنی زمان تا اولین حمله ۵ ساعت بود.
  • کنترل دسترسی شکسته (Broken Access Control) پرسوءاستفاده‌ترین نوع آسیب‌پذیری بود.

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

چک‌لیست اول: قبل از نصب یک افزونه

این‌ها سؤال‌هایی است که قبل از اضافه کردن هر افزونه به سایت مشتری از خودم می‌پرسم:

  1. واقعاً لازمش داریم؟ خیلی از افزونه‌ها کاری می‌کنند که با ده خط کد در افزونهٔ اختصاصی سایت هم انجام می‌شود. هر افزونه یعنی یک سطح حملهٔ تازه و یک منبع تازهٔ آپدیت.
  2. آخرین به‌روزرسانی کِی بوده؟ افزونه‌ای که بیش از یک سال به‌روز نشده، احتمالاً به آسیب‌پذیری‌هایی که پیدا شوند هم سریع واکنش نشان نمی‌دهد.
  3. سازندهٔ آن کیست؟ سابقهٔ پاسخ به گزارش‌های امنیتی را در بخش پشتیبانی و تغییرات نسخه‌ها نگاه کنید. عبارت «security fix» در changelog نشانهٔ بدی نیست؛ یعنی کسی دارد گوش می‌دهد.
  4. نسخهٔ نال یا کرک ممنوع. هیچ صرفه‌جویی‌ای ارزش افزونه‌ای را ندارد که کد ناشناس داخلش است و هرگز آپدیت نمی‌شود.
  5. دسترسی‌هایش را بشناسید. افزونه‌ای که فقط یک فرم ساده است، چرا باید endpoint عمومی REST یا آپلود فایل داشته باشد؟
  6. یک جایگزین هم پیدا کنید. اگر یک روز وصلهٔ امنیتی دیر برسد، باید بدانید کدام افزونه را می‌شود جایگزینش کرد.

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

چک‌لیست دوم: وقتی خودتان افزونه می‌نویسید

بیشتر آسیب‌پذیری‌هایی که در کد سفارشی پروژه‌ها پیدا کرده‌ام، از سه فراموشی ساده آمده‌اند: بررسی دسترسی، nonce، و escape خروجی.

هر کار حساس، بررسی capability لازم دارد

بررسی nonce فقط می‌گوید درخواست از فرم شما آمده؛ نمی‌گوید فرستنده اجازه دارد. هر دو لازم است:

add_action( 'admin_post_my_plugin_export', function () {
	if ( ! current_user_can( 'manage_woocommerce' ) ) {
		wp_die( esc_html__( 'You are not allowed to do this.', 'my-plugin' ), 403 );
	}
	check_admin_referer( 'my_plugin_export' );

	// ... the actual export.
} );

permission_callback در REST هیچ‌وقت __return_true نیست، مگر واقعاً عمومی باشد

register_rest_route( 'my-plugin/v1', '/orders/(?P<id>\d+)/note', array(
	'methods'             => 'POST',
	'callback'            => 'my_plugin_add_note',
	'permission_callback' => fn() => current_user_can( 'edit_shop_orders' ),
	'args'                => array(
		'id'   => array( 'type' => 'integer', 'required' => true, 'minimum' => 1 ),
		'note' => array( 'type' => 'string', 'required' => true, 'sanitize_callback' => 'sanitize_textarea_field' ),
	),
) );

هر مسیری که چیزی را تغییر می‌دهد و permission_callback واقعی ندارد، یک باگ کنترل دسترسی است که فقط منتظر کشف شدن است. همین قاعده دربارهٔ Abilities API در وردپرس ۷ هم صادق است.

ورودی را تمیز کنید، خروجی را escape کنید

ورودی را هنگام دریافت با تابع درست تمیز کنید: absint، sanitize_text_field، sanitize_email و بقیه. خروجی را هم هنگام چاپ با esc_html، esc_attr، esc_url یا wp_kses_post escape کنید. قاعده ساده است: هر چیزی که به HTML برمی‌گردد، درست در لحظهٔ چاپ escape می‌شود، حتی اگر از دیتابیس خودتان آمده باشد. XSS ذخیره‌شده دقیقاً از همین «از دیتابیس خودمان است، امن است» تغذیه می‌کند.

کوئری خام فقط با prepare

$rows = $wpdb->get_results(
	$wpdb->prepare( "SELECT id, total FROM {$wpdb->prefix}my_table WHERE customer_id = %d", $customer_id )
);

اگر فقط یک کار می‌کنید

یک فهرست از همهٔ افزونه‌های سایت بنویسید و جلوی هرکدام سه چیز بگذارید: چرا نصب است، آخرین آپدیتش کِی بوده، و اگر فردا کنار گذاشته شود جایگزینش چیست. هر افزونه‌ای که جواب ستون اول را ندارد، حذفش کنید.

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

منابع

$ ./request_review

همین مشکل را در فروشگاه خودتان دارید؟

توسعه‌دهنده ارشد WordPress و WooCommerce برای معماری، اتصال سیستم‌ها و حل مسائل پیچیده production.

درخواست بررسی فنی

دیدگاه‌ها

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *