مقاله ووکامرس

PHP 8.6 در راه است؛ تغییری کوچک که می‌تواند بازگشت از درگاه بانک را خراب کند

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

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

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

جلد پیکسلی: کلوچهٔ گازخورده و تنظیمات سشن PHP 8.6
فهرست مطالب (8)

هر نسخهٔ تازهٔ PHP معمولاً با فهرستی از قابلیت‌های جذاب معرفی می‌شود و PHP 8.6 هم از این قاعده مستثنا نیست. ولی این بار مهم‌ترین تغییر برای خیلی از فروشگاه‌های ایرانی در فهرست قابلیت‌ها نیست؛ در فهرست «تغییرات ناسازگار» است، در قالب سه خط که پیش‌فرض‌های سشن را عوض می‌کنند.

PHP 8.6 طبق برنامهٔ رسمی ۱۹ نوامبر ۲۰۲۶ منتشر می‌شود؛ یعنی حدود سه هفتهٔ دیگر. اگر فروشگاه یا افزونه‌ای دارید که با درگاه بانکی کار می‌کند، این یادداشت را تا آخر بخوانید.

سه پیش‌فرض تازه برای سشن

پیشنهاد «پیش‌فرض‌های امن برای پیکربندی سشن» در فهرست پیشنهادهای رسمی PHP با رأی تقریباً یکپارچه پذیرفته شد. از PHP 8.6، این سه تنظیم به‌طور پیش‌فرض روشن‌اند:

  • **session.use_strict_mode = 1:** PHP دیگر شناسهٔ سشنی را که خودش نساخته نمی‌پذیرد.
  • **session.cookie_httponly = 1:** جاوااسکریپت دیگر نمی‌تواند کوکی سشن را از document.cookie بخواند.
  • **session.cookie_samesite = Lax:** مرورگر کوکی سشن را در درخواست‌های POST که از سایت دیگری می‌آیند، نمی‌فرستد.

هر سه از نظر امنیتی کاملاً درست‌اند. دو مورد اول در عمل برای بیشتر سایت‌ها بی‌دردسرند. اما سومی دقیقاً به جایی می‌خورد که فروشگاه‌های ایرانی زیاد از آن استفاده می‌کنند.

مشکل کجاست: بازگشت از بانک

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

از دید مرورگر، این درخواست POST از دامنهٔ بانک به دامنهٔ شما رفته است؛ یعنی یک درخواست POST بین‌سایتی. با پیش‌فرض تازهٔ SameSite=Lax، مرورگر کوکی سشن PHP را همراه این درخواست نمی‌فرستد.

مسیر بازگشت از درگاه بانک و کوکی سشنی که با SameSite=Lax جا می‌ماند
با SameSite=Lax، کوکی سشن PHP در بازگشت POST از بانک فرستاده نمی‌شود.

حالا فرض کنید افزونهٔ درگاه شما شمارهٔ سفارش یا مبلغ را پیش از رفتن به بانک در $_SESSION گذاشته باشد و در بازگشت، همان را از سشن بخواند. روی PHP 8.6 با تنظیمات پیش‌فرض، سشن در این لحظه خالی است. نتیجه می‌تواند سفارشی باشد که پولش کم شده ولی در سایت «پرداخت‌نشده» می‌ماند؛ یعنی دقیقاً همان تیکتی که هیچ فروشنده‌ای دوست ندارد ببیند.

یک نکتهٔ مهم: خود هستهٔ وردپرس از سشن PHP استفاده نمی‌کند و ووکامرس هم سشن خودش را با کوکی و دیتابیس مدیریت می‌کند. پس این مشکل مستقیماً مربوط به افزونه‌ها، قالب‌ها و کدهای سفارشی است که خودشان session_start() را صدا می‌زنند. متأسفانه در افزونه‌های درگاه پرداخت ایرانی، این الگو کم نیست.

اول: بفهمید درگیر هستید یا نه

یک جست‌وجوی ساده در پوشهٔ افزونه‌ها و قالب کافی است:

grep -rnE "session_start|\$_SESSION" wp-content/plugins wp-content/themes \
  --include=*.php | grep -v "/vendor/"

هر نتیجه‌ای که در افزونهٔ درگاه پرداخت یا مسیر بازگشت از بانک باشد، باید بررسی شود.

دوم: راه‌حل درست

راه‌حل اصلی این است که مسیر بازگشت اصلاً به سشن تکیه نکند. این الگو هم با PHP 8.6 کار می‌کند و هم امن‌تر است:

  1. شناسهٔ سفارش را در خود آدرس بازگشت بگذارید. ووکامرس برای هر سفارش یک کلید تصادفی دارد (order_key). آدرس بازگشت را با شناسهٔ سفارش و همین کلید بسازید.
  2. در بازگشت، سفارش را از روی آدرس پیدا کنید، کلید را با hash_equals() مقایسه کنید و مبلغ را از خود سفارش بخوانید، نه از چیزی که از بیرون آمده.
  3. پرداخت را همیشه با سرویس تأیید (verify) بانک نهایی کنید. هیچ‌وقت فقط به پارامترهایی که بانک در بازگشت فرستاده اعتماد نکنید.
// Building the return URL before redirecting to the bank.
$callback = add_query_arg(
	array(
		'order' => $order->get_id(),
		'key'   => $order->get_order_key(),
	),
	WC()->api_request_url( 'my_gateway' )
);

// On return: find the order from the URL, not from $_SESSION.
$order = wc_get_order( absint( $_GET['order'] ?? 0 ) );
if ( ! $order || ! hash_equals( $order->get_order_key(), (string) ( $_GET['key'] ?? '' ) ) ) {
	wp_die( esc_html__( 'Invalid payment return.', 'my-gateway' ), 400 );
}
// Then verify the transaction with the bank's API using $order->get_total().

اگر کد افزونه دست شما نیست، برای سازنده‌اش تیکت بزنید و صریحاً بپرسید با session.cookie_samesite=Lax تست کرده یا نه.

راه‌حل موقت، با احتیاط

اگر تا انتشار PHP 8.6 فرصت اصلاح ندارید، می‌توانید پیش‌فرض قدیمی را برگردانید: session.cookie_samesite را در php.ini خالی بگذارید، یا فقط برای همان مسیر با session_set_cookie_params() مقدار None و secure بدهید. ولی یادتان باشد این یعنی کنار گذاشتن یک لایهٔ محافظت در برابر حملات CSRF. این را مُسکن ببینید، نه درمان.

قابلیت‌های تازه‌ای که ارزش دیدن دارند

PHP 8.6 فقط دربارهٔ سشن نیست. چند قابلیت هم دارد که کد روزمره را تمیزتر می‌کند:

  • کاربرد جزئی تابع (Partial Function Application): می‌توانید بخشی از آرگومان‌ها را از قبل پر کنید و بقیه را با ? خالی بگذارید. این قابلیت با عملگر pipe در PHP 8.5 ترکیب خیلی خوبی دارد، چون pipe فقط تابع‌های یک‌آرگومانی را می‌پذیرد.
  • **clamp():** مقداری را بین یک حداقل و حداکثر نگه می‌دارد؛ مثلاً درصد تخفیفی که نباید از صفر کمتر یا از صد بیشتر شود.
  • **کلاس Time\Duration:** برای نمایش بازه‌های زمانی، با متدهایی برای جمع و ضرب و امکان مقایسهٔ مستقیم.
$slug = str_replace( ' ', '-', ? );          // partial application
echo $slug( 'Hello World' );                 // Hello-World
echo 'Hello World' |> $slug;                 // works with the pipe operator too
$percent = clamp( $discount, min: 0, max: 100 );

جمع‌بندی

PHP 8.6 نسخهٔ پرسروصدایی نیست، ولی تغییر پیش‌فرض‌های سشنش از آن تغییرهایی است که بی‌صدا چیزی را خراب می‌کنند. خبر خوب این است که هنوز سه هفته وقت دارید. یک بار کد درگاه پرداختتان را بگردید، اگر به $_SESSION تکیه دارد اصلاحش کنید، و پیش از ارتقای سرور، یک پرداخت آزمایشی را روی PHP 8.6 از اول تا آخر انجام دهید.

منابع

$ ./request_review

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

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

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

دیدگاه‌ها

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

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