فایروال اپلیکیشن وبی که فقط روشن است، چیزی دربارهی آنچه میگیرد به شما نمیگوید. اثربخشی دو عدد است که با هم سنجیده میشوند: WAF چه سهمی از هر کلاس حمله را مسدود میکند، و چه مقدار از ترافیک سالم را به اشتباه پس میزند. این نوشته نشان میدهد چطور هر دو را بسنجید، مهاجم کجا یک بار مخرب (payload) را بازبستهبندی میکند تا از قاعده رد شود، و حلقهی تنظیمی که این دو عدد را به سمت درست میبرد. این همان روشی است که در تحلیلگر اثربخشی حفاظت ما، APEA، دنبال میکنیم.
WAF یک کلید خاموشوروشن نیست؛ مجموعهای از قاعدههاست که باید متناسب با یک اپلیکیشن تنظیم شود. سنجش اثربخشیاش هم یک بله/خیر نیست؛ اندازهگیریای است پروفایلبهپروفایل، با شاهدی پشت هر عدد.
چرا «روشن بودن» کافی نیست
هر WAF با مجموعهای از قاعدههای پیشفرض میآید. مجموعه قواعد هستهی OWASP (CRS)، نسخهی ۴ (۲۰۲۴)، مبنای متنباز و شناختهشدهای است که بیشتر WAFهای تجاری روی آن ساخته میشوند. نقطهی شروع خوبی است، اما اپلیکیشن شما را نمیشناسد: نمیداند کدام مسیر ورودی آزاد میگیرد، کدام پارامتر همیشه دادهی عجیب دارد، و کدام قابلیت اصلاً استفاده نمیشود. برای همین یک نصب پیشفرض یا زیادی سختگیر است و مشتری را میراند، یا زیادی نرم است و حمله را رد میکند. سنجش، جای این حدسها را با عددی پر میکند که میشود روی آن عمل کرد.
CRS به هر درخواست امتیاز میدهد: هر قاعدهای که تطبیق بخورد امتیاز ناهنجاری را بالا میبرد، و درخواست وقتی از آستانهی ورودی (پیشفرض ۵) بگذرد مسدود میشود. همین طراحی است که باعث میشود یک قاعدهی ناشیانه بتواند کاربر واقعی را مسدود کند.
نمونه؛ یک مثبت کاذب از پیشفرضها. ویرایشگرِ محتوای یک فروشگاه این یادداشت محصول را ذخیره میکند:
برای انتخاب سایز از کلید SELECT استفاده کنید و بعد هر دو نیمهی مجموعه را UNION کنید.
در سطح پارانویای بالاتر، کلیدواژههای SQL یعنی SELECT و UNION هر کدام یک قاعدهی CRS را فعال میکنند، امتیاز از ۵ میگذرد، و ذخیره با ۴۰۳ رد میشود. هیچ حملهای در کار نبوده؛ قاعده فقط نمیداند این مسیر، متن معمولی میپذیرد. تا وقتی کسی نسنجیده، این رد شدن روی داشبورد سبز دیده نمیشود؛ ویرایشگر فقط میبیند که «ذخیره نشد».
نرخ تشخیص، کلاسبهکلاس
اثربخشی هرگز یک عدد کلی نیست، چون یک WAF میتواند در برابر یک کلاس عالی و در برابر کلاسی دیگر ناتوان باشد. کار را به تفکیک دستههای حمله انجام میدهیم، همانطور که راهنمای آزمون امنیت وب OWASP (WSTG)، نسخهی ۴٫۲ (۲۰۲۰)، آنها را گروهبندی میکند: تزریق، اسکریپت میانسایتی، پیمایش مسیر، تزریق فرمان سیستمعامل، گنجاندن فایل، و مانند اینها. برای هر دسته مجموعهای از درخواستهای حملهی شناختهشده را از WAF عبور میدهیم و میشماریم چند تا مسدود شد و چند تا رد شد. نتیجه یک نقشه است: کجا محکم است، کجا سوراخ دارد.
نمونه؛ یک کاوش. علیه نسخهی آزمایشی (staging) اپلیکیشن، یک تزریق SQL کتابی را به مسیر ورود میفرستیم:
POST /api/login HTTP/1.1
Host: staging.shop.example
Content-Type: application/json
{"username":"admin","password":"' OR '1'='1"}
WAF با ۴۰۳ پاسخ میدهد و قاعدهی CRS ۹۴۲۱۰۰ (تزریق SQL) را ثبت میکند. این یک درخواست، فقط یک نقطهداده است. امتیاز کلاس از دهها گونه به دست میآید؛ عملگرها، سبکهای کامنت، بزرگی و کوچکی حروف و ترفندهای فاصله، که همه به یک شکل فرستاده میشوند و میشماریم WAF چند تا را میگیرد. کلاسی که ۹ تا از ۱۰ ورودی کتابی را میگیرد اما ۲ تا از ۱۰ گونه را، سوراخ دارد؛ نه نمرهی ۹۰٪.
هر کلاس در برابر تعریف روشنی از معنای نتیجه سنجیده میشود:
| کلاس حمله | نمونهی کاوش | «مسدود» یعنی | «رد» یعنی |
|---|---|---|---|
| تزریق SQL | ' OR '1'='1 در فیلد ورود |
WAF پیش از اجرای پرسوجو ۴۰۳ میدهد | بار مخرب به لایهی پایگاهداده میرسد |
| XSS بازتابی | <svg onload=alert(1)> در عبارت جستوجو |
WAF درخواست را میزداید یا رد میکند | اسکریپت در صفحه بازتاب میشود |
| پیمایش مسیر | ../../etc/passwd در پارامتر فایل |
WAF دنبالهی پیمایش را رد میکند | درخواست به مدیریت فایل میرسد |
| تزریق فرمان سیستمعامل | ; id چسبانده به فیلد نام میزبان |
WAF متاکاراکترهای پوسته را رد میکند | فرمان به پوستهی سیستم میرسد |
| گنجاندن فایل | php://filter/... در پارامتر include |
WAF این wrapper را رد میکند | wrapper به منطق include میرسد |
یک نکتهی مهم: آزمون حمله باید علیه هدفی کنترلشده انجام شود که مجاز به آزمودنش هستید، نه سامانهی زندهی دیگران؛ هدف، سنجش خودِ WAF است، نه بهرهبرداری از کسی.
مثبت کاذب روی ترافیک واقعی
نیمهی دیگرِ کار، مشتری است. WAFی که همهچیز را مسدود کند صددرصد «امن» است و صددرصد بیفایده. برای همین نرخ مثبت کاذب را روی نمونهای از ترافیک واقعیِ اپلیکیشن میسنجیم؛ در حالت فقط-ثبت، تا چیزی واقعاً مسدود نشود، و میشماریم قاعدهها چند درخواستِ سالم را میگرفتند.
نمونه؛ یک درخواست سالم که به اشتباه مسدود شد. با بازپخشِ یک روز ترافیکِ نمونهی تولید، یک درخواست توی چشم میزند:
POST /api/profile HTTP/1.1
Content-Type: application/json
{"bio":"سرپرست تیم \"ops\" هستم و به تازهکارها کمک میکنم؛ o'brien@example.com"}
این درخواست به خاطر گیومهها و علامت & یک قاعدهی XSS و به خاطر یک آپاستروفِ تنها یک قاعدهی تزریق SQL را فعال میکند و امتیاز ۱۰ میگیرد، در برابر آستانهی ۵. مشتری واقعیای که این بیوگرافی را ذخیره کند ۴۰۳ میگیرد. این یک مثبت کاذب است، و چون bio یک فیلد متنآزاد است، به بسیاری از کاربران عادی برمیخورد، نه یکی. امنیت و روانبودن دو کفهی یک ترازو هستند؛ بدون ترافیک واقعی نمیشود وزنهی هیچ کفه را پیدا کرد.
چطور یک بار مخرب از قاعده رد میشود
مهاجم به قاعدهی خام حمله نمیکند؛ همان حمله را طوری بازبستهبندی میکند که از کنار قاعده رد شود، و بعد به خودِ اپلیکیشن تکیه میکند که دوباره بازش کند.
بخش مهمی از ارزیابی، آزمودنِ همین خانوادههاست:
| خانواده | کاری که میکند | چرا رد میشود |
|---|---|---|
| کدگذاری | همان بار مخرب را با کدگذاری URL، یونیکد یا دولایه مینویسد | قاعده الگوی خام را میبیند، نه شکل کدشده را |
| تغییر نوع محتوا | داده را در قالبی میفرستد که WAF کمتر بازرسی میکند | بازرسی برای آن نوع محتوا کامل تنظیم نشده است |
| آلودگی پارامتر | یک پارامتر را چندبار تکرار میکند تا اجزای مختلف متفاوت آن را بخوانند | قاعده یک نسخه را میبیند و اپلیکیشن نسخهی دیگر را |
| بدنهی بزرگ | بار مخرب را پس از حجمی میگذارد که از آن به بعد بازرسی نمیشود | WAF برای کارایی، فقط ابتدای بدنه را میخواند |
نمونه؛ دور زدن با کدگذاری. بار مخرب خام ' OR 1=1 -- مسدود میشود، همانطور که بالا دیدیم. آن را دوبار-URL-کدشده میفرستیم:
username=admin&password=%2527%2520OR%25201%253D1%2520--
حالا WAF فقط بایتهای درصد-escapeشده را میبیند؛ الگوی SQLاش هرگز تطبیق نمیخورد و درخواست رد میشود. اگر اپلیکیشن پیش از استفاده مقدار را دو بار بازگشایی کند، که پیشفرضِ رایجِ خیلی از فریمورکهاست، تزریق روی پایگاهداده اجرا میشود. یافته این نیست که «WAF خراب است»؛ دقیق است: قاعدهی ۹۴۲۱۰۰ روی شکل کاملاً بازگشاییشدهی این پارامتر اعمال نمیشود. هر خانوادهای که از WAF رد شود یک قاعدهی تنظیمنشدهی مشخص را نشان میدهد، و کار بعدی بستنِ همان شکاف است.
حلقهی تنظیم
ارزیابی نقطهی پایان نیست؛ نقطهی شروعِ یک حلقه است.
ابتدا WAF را در حالت پایش (فقط-ثبت) میگذاریم تا ببینیم بدون مسدودکردنِ کسی چه چیزی را میگرفت. بعد قاعدههای پرخطا را به همان مسیرها و پارامترهایی که به آنها نیاز دارند محدود میکنیم و استثناها را دقیق و هدفمند مینویسیم، نه با خاموشکردنِ کل قاعده. سپس آستانهی امتیاز ناهنجاری را بازبینی و دوباره اندازهگیری میکنیم. هر دور، هم نرخ تشخیص بالا میرود و هم مثبت کاذب پایین میآید، تا جایی که هر دو عدد در جای قابلقبول بایستند.
نمونه؛ یک دور. در حالت پایش، WAF آنچه را مسدود میکرد ثبت میکند. یک خط نمونه:
[2026-09-20T10:14:02Z] would-block score=10 uri=/api/profile param=bio rules=942100,941130 client=203.0.113.45
دیدنِ param=bio که برای صدها کلاینتِ متمایز و قانونی روشن میشود، اصلاح را دقیق به ما میگوید: قاعدههای ۹۴۲۱۰۰ و ۹۴۱۱۳۰ را فقط روی پارامتر bio در مسیر /api/profile محدود کن. این استثنا را اعمال میکنیم، دوباره میسنجیم، و مطمئن میشویم کلاس تزریق SQL هنوز روی فرم ورود، همانجا که باید، مسدود میکند. چون اپلیکیشن و ترافیکش مدام عوض میشوند، این حلقه هرگز کاملاً بسته نمیشود؛ یک بازبینی دورهای زنده نگهش میدارد.
یک یافته چه شکلی است
یک یافتهی ارزیابی WAF شکاف را نام میبرد، اثباتش میکند، و دقیقاً میگوید چه باید تغییر کند؛ نه فقط «WAF را تنظیم کنید». یک نمونهی پرشده:
| بخش | محتوا |
|---|---|
| عنوان | قاعدهی تزریق SQL روی شکلِ دوبار-بازگشاییشدهی فیلد password اعمال نمیشود |
| کاوش | ' OR 1=1 --، دوبار-URL-کدشده در بدنهی درخواست ورود |
| محل ورود | POST /api/login، پارامتر password، روی نسخهی آزمایشی |
| مسدود شد؟ | خام: بله (۴۰۳، قاعدهی ۹۴۲۱۰۰). کدشده: نه؛ در ۱۰ تلاش از ۱۰ رد شد |
| اثر | دور زدن احراز هویت روی مسیری که WAF قرار بود از آن محافظت کند |
| علت ریشهای | قاعده فقط شکل خام را میگیرد؛ اپلیکیشن مقدار را دو بار URL-بازگشایی میکند |
| اصلاح | پیش از تطبیق روی این مسیر مقدار را نرمالسازی (URL-بازگشایی) کن، یا قاعدهای برای شکل بازگشاییشدهی password اضافه کن؛ و دوبار-بازگشایی را در اپلیکیشن اصلاح کن |
چطور سنجش را انجام میدهیم
- علیه یک هدف کنترلشدهی آزمایشی با دادهی واقعینما که مجاز به آزمودنش هستید تست کنید؛ هرگز روی سامانهی زندهی شخص ثالث.
- تشخیص را کلاسبهکلاس بسنجید، با گونههای فراوان برای هر کلاس، و هر کلاس را جدا گزارش کنید، نه به صورت یک درصدِ سرتیتر.
- خانوادههای دور زدن را آزمون کنید (کدگذاری، تغییر نوع محتوا، آلودگی پارامتر، بدنهی بزرگ) در برابر کلاسهایی که WAF ادعای مسدودکردنشان را دارد.
- نمونهای از ترافیک واقعی را در حالت فقط-ثبت بازپخش کنید تا مثبت کاذب را پیش از هر تغییر در قاعدهها بسنجید.
- هر شکاف را بهصورت یک یافته با اصلاح دقیقش گزارش کنید، سپس تنظیم کنید، دوباره بسنجید و تکرار کنید.
نکته: WAF زمان میخرد؛ جای رفع آسیبپذیری در اپلیکیشن را نمیگیرد. آن را یک لایه از دفاع چندلایه بدانید، کنار اعتبارسنجی ورودی و کدگذاری خروجی در خودِ کد.
پرسشهای متداول
فرق ارزیابی WAF با تست نفوذ اپلیکیشن چیست؟ تست نفوذ میپرسد اپلیکیشن چه ضعفهایی دارد؛ ارزیابی WAF میپرسد لایهی دفاعیِ جلوی اپلیکیشن آن ضعفها را چقدر میگیرد. این دو مکملاند، و هر دو باید در گزارشی بیایند که هر یافته و اصلاحش را نام میبرد (نگاه کنید به گزارش تست نفوذ باید شامل چه چیزهایی باشد؟).
آیا WAF جای وصلهکردن کد را میگیرد؟ نه. WAF یک لایه است، نه راهحل نهایی؛ در چارچوب دفاع چندلایه معنا پیدا میکند، کنار اعتبارسنجی ورودی در خودِ اپلیکیشن.
در گزارش، «دور زدن» دقیقاً یعنی چه؟ یعنی باری که WAF در شکل خامش مسدود میکند، پس از بازبستهبندی (کدشده، پخششده میان چند پارامتر، یا پنهانشده پس از مرز بازرسی) یک بار به اپلیکیشن رسیده است. هر دور زدن به یک قاعدهی تنظیمنشده نگاشته میشود، پس هر کدام یک اصلاح مشخص دارد.
هر چند وقت باید سنجید؟ پس از هر تغییر مهم در اپلیکیشن یا در قاعدهها، و بهصورت دورهای فارغ از آن؛ چون هم اپلیکیشن و هم روشهای دور زدن بهمرور عوض میشوند.
آیا این کار به ترافیک تولید آسیب میزند؟ خیر؛ آزمون حمله علیه یک هدف کنترلشده انجام میشود، و سنجش مثبت کاذب روی نمونهی ترافیک در حالت فقط-ثبت، بدون اختلال در سرویس.
اگر میخواهید بدانید WAF شما واقعاً چه چیزی را میگیرد و چه چیزی را رها میکند، کار ارزیابی، تنظیم و طراحی WAF ما دقیقاً همین را انجام میدهد، و APEA پلتفرمی است که این کار را روی آن اجرا میکنیم.