باگ body-parser: سرور Express با یه درخواست از کار میافته
خلاصهٔ کاملتر
تیم Express خبر داده که middleware معروف body-parser یه آسیبپذیری denial of service با شناسهٔ CVE-2026-111181 داره. یعنی یه کلاینت بدخواه با فقط یه درخواست میتونه سرور رو از کار بندازه. چون body-parser بهطور پیشفرض درخواستهای فشرده (مثلاً gzip) رو قبول میکنه، بیشتر نسخههای آپدیتنشده آسیبپذیرن.
ریشهٔ مشکل اینه که stream مربوط به decompression (یعنی جریانی که دادهٔ فشرده رو باز میکنه) بیشتر از خود پردازش درخواست عمر میکنه. وقتی گزینهٔ inflate روشنه، تابع contentstream() یه stream برای باز کردن فشردهسازی میسازه و درخواست رو با pipe() بهش وصل میکنه. بعد از خوندن body، body-parser تابع next() رو صدا میزنه ولی درخواست هنوز به decoder وصله و هیچ listener برای رویداد error روش نذاشته.
حالا اگه بدنهٔ درخواست یه بخش gzip معتبر باشه و بعدش یه بایت صفر و چند بایت اضافه بیاد، اتفاقها اینطوری پیش میرن. decoder به آخر بخش معتبر میرسه و تموم شدن رو اعلام میکنه ولی بسته نمیشه. کتابخونهٔ raw-body دادهٔ بازشده رو برمیگردونه و listenerهاش رو برمیداره. اپ هم درخواست رو عادی جواب میده.
ولی بایتهای اضافه هنوز دارن وارد decoder باز میشن و چون کار decompression تموم شده، خطا میده. تنها handler موجود همونیه که pipe() گذاشته و فقط خطا رو دوباره پرتاب میکنه. پس Node اونو به یه uncaught exception تبدیل میکنه و کل پروسه کرش میکنه. این رفتار هم تو یه سرور ساده با node:http دیده میشه، هم تو اپهای معمولی Express 4 و 5.
خطرش جدیه. با پخش کردن چند تا درخواست بین نودها میشه یه کلاستر کامل پشت load balancer رو خوابوند. اگه parser سراسری و قبل از احراز هویت نصب شده باشه، درخواست حتی لازم نیست به یه route معتبر بخوره یا لاگین کرده باشه.
راهحل اصلی آپدیته. اگه هم اپت اصلاً به بدنهٔ فشرده نیاز نداره، میتونی inflate رو false کنی تا درخواستهای فشرده قبل از ساخته شدن decoder رد بشن. فقط حواست باشه این روش همهٔ بدنههای فشرده رو رد میکنه.
نکات کلیدی:
- شناسهٔ آسیبپذیری CVE-2026-111181 و advisory مربوطه GHSA-27r3-jxmh-p828 هست
- نسخههای 2.0.0 تا 2.3.0 آسیبپذیرن و تو 2.4.0 درست شده
- نسخههای 1.20.8 و قبلتر آسیبپذیرن و تو 1.20.9 درست شده
- علت: نبودن listener برای error روی stream باز کردن فشردهسازی بعد از next()
- راه موقت: گذاشتن inflate: false اگه به بدنهٔ فشرده نیاز نداری
- یه درخواست کافیه تا پروسهٔ Node کرش کنه، حتی بدون احراز هویت




