امنیت برنامههای وب تقریباً همیشه از یک فهرست آشنا شروع میشود: فهرست دهگانهٔ پروژهٔ امنیت برنامههای وب باز (OWASP). مشکل از جایی شروع میشود که این فهرست را مثل چکلیست باگ بخوانیم و بکوشیم «هر ده مورد» را کمی بپوشانیم. OWASP فهرست رتبهبندیشدهٔ ریسک است، نه فهرست آسیبپذیری؛ و درست خواندنش یک تصمیم معماری میسازد، نه چند وصلهٔ پراکنده روی کد.
OWASP چه چیزی را رتبهبندی میکند؟
هر ردیف این فهرست یک دستهٔ ریسک است، نه یک خطای مشخص. «کنترل دسترسی شکسته (Broken Access Control)» میتواند دهها اشتباه کوچک و متفاوت را در خود جا دهد: نبود بررسی مالکیت منبع، اعتماد به شناسهای که از سمت کاربر میآید، یا مجوزدهی فقط در رابط کاربری. به همین دلیل، پوشاندن نامها فایدهای ندارد؛ باید ریشهٔ مشترک این ضعفها را هدف گرفت.
همین ویژگی یک مزیت عملی هم دارد: وقتی بهجای «رفع باگ» به «کاهش یک دستهٔ ریسک» فکر میکنید، معیار تصمیمگیری روشن میشود. دیگر نمیپرسید «آیا این تکه کد آسیبپذیر است؟»؛ میپرسید «اگر این لایه را باور نکنیم چه اتفاقی میافتد؟»
ریشهٔ مشترک: مرز اعتماد را نمیشناسیم
هر برنامهٔ وب چند مرز اعتماد (Trust Boundary) دارد: مرورگر کاربر، سرور برنامه، دیتابیس، سرویسهای جانبی و پنل مدیریت. هرجا داده از یک طرف مرز به طرف دیگر میرود، باید دوباره اعتبارسنجی و مجوزدهی شود. بیشتر آسیبپذیریهای واقعی هم از همین نقطه میآیند: یک لایه فرض میکند لایهٔ دیگر کارش را کرده است. فرانتاند دکمه را پنهان کرده، پس بکاند بررسی نمیکند؛ فایروال ترافیک را فیلتر کرده، پس خود برنامه هر ورودی را باور میکند.
نگاه از مرز اعتماد بهجای مرز شبکه کمک میکند جای واقعی این بررسیها را پیدا کنید و بفهمید چرا «امن بودن شبکه» با «امن بودن برنامه» یکی نیست.
یک مثال عملی: فاکتور شمارهٔ 1042
کاربری پس از ورود، فاکتورهای خود را از آدرسی مانند /invoices/1042 میبیند. سرور برای پاسخ، توکن نشست را میخواند تا بفهمد چه کسی درخواست داده، اما شمارهٔ فاکتور را بدون بررسی مالکیت از آدرس برمیدارد. کافی است همان کاربر عدد آدرس را به 1043 تغییر دهد تا فاکتور مشتری دیگری را ببیند.
ظاهر برنامه سالم است، تستهای عملکردی پاس میشوند و خطایی هم در لاگها ثبت نمیشود؛ اما دادهٔ مشتریان دیگر لو رفته است. این خانوادهٔ خطا نشان میدهد چرا احراز هویت کافی نیست: برنامه میداند «شما کی هستید»، ولی نپرسیده «آیا اجازه دارید این منبع را ببینید؟».
راهحل پیچیده نیست: هر درخواست باید در سمت سرور، مالکیت یا مجوز دسترسی به منبع را بررسی کند و مدل مجوزدهی در یک نقطه متمرکز شود، نه پراکنده در هر کنترلر. پنهانکردن لینک در صفحه یا غیرفعالکردن دکمه در مرورگر، کنترل امنیتی نیست؛ فقط کار را برای کاربر عادی سختتر میکند.
سه خانوادهای که بیشترین هزینه را میسازند
- کنترل دسترسی شکسته: بررسی مجوز روی هر منبع، نه فقط روی صفحهها. پنلهای مدیریتی و نقاط پایانی (Endpoint) که فقط با پنهانبودن در رابط کاربری محافظت شدهاند، پرخطرترین بخشها هستند.
- تزریق (Injection): پرسوجوی پارامتریک (Parameterized Query) بهجای چسباندن رشتهها؛ برای تزریق اسکریپت در مرورگر (XSS)، کدگذاری خروجی بر اساس بافت همان صفحه. اعتبارسنجی ورودی خوب است، اما جایگزین پارامتریککردن نمیشود.
- احراز هویت و مدیریت نشست: نشستی که پس از خروج باطل نمیشود، توکنهای بسیار بلندمدت، و نبود محدودیت نرخ (Rate Limiting) روی ورود و بازیابی رمز.
در کنار اینها، پیکربندی نادرست معمولاً نتیجهٔ بیدقتی در استقرار است، نه ضعف در کد: صفحهٔ خطای پرجزئیات، فایل پشتیبان در دسترس عموم، حساب پیشفرض باقیمانده، یا محیط آزمایشی که به اینترنت باز است.
از فهرست تا فرایند: ترتیب سرمایهگذاری
- فهرست دادههای حساس و مسیرهای رسیدن به آنها را بنویسید؛ بدون این نقشه، اولویتبندی ممکن نیست.
- برای هر مسیر مشخص کنید چه نقشهایی مجازند و این تصمیم را در یک لایهٔ مرکزی نگه دارید.
- پارامتریککردن پرسوجو و کدگذاری خروجی را به پیشفرض چارچوب تبدیل کنید تا خطای فردی گران تمام نشود.
- برای هر نقطهٔ پایانی، آزمون خودکار دو نقشی بنویسید: یک کاربر عادی و یک کاربر دیگر. اگر پاسخ هر دو یکی بود، همانجا مشکل دارید.
- بازبینی کد با نگاه امنیتی را به فرایند همیشگی تبدیل کنید؛ کد امنیتی که هیچوقت بازبینی نمیشود، بهسرعت قدیمی میشود.
- لاگ و پایش را جدی بگیرید. بدون ثبت درخواستهای ناموفق و الگوهای تکراری، نمیفهمید کسی در حال شناسایی برنامهٔ شماست.
برای کسبوکارهای کوچک، هدف سخت بودن است نه بزرگ بودن
مهاجم دنبال سختترین هدف نیست؛ دنبال کممقاومتترین است. سه کار کمهزینه بیشترین اثر را دارند: بهروز نگهداشتن مؤلفهها و کتابخانهها، دادن کمترین دسترسی لازم به هر کاربر و سرویس، و پشتیبانگیری که واقعاً یک بار بازیابی شده باشد. هدف سخت، نه هدف بزرگ دقیقاً همین منطق است.
جمعبندی
فهرست OWASP اگر بهعنوان چکلیست خوانده شود، به مجموعهای از کارهای نیمهتمام تبدیل میشود؛ اگر بهعنوان فهرست ریسک خوانده شود، جای سرمایهگذاری را نشان میدهد. در عمل، تمرکز روی مرزهای اعتماد، متمرکزکردن مجوزدهی، پارامتریککردن پرسوجوها و آزمونهای خودکار دسترسی، بخش بزرگی از دردسرها را پیش از رسیدن به محیط عملیاتی حذف میکند. امنیت برنامههای وب یک بار مصرف نیست؛ عادتی است که در هر تغییر کد تکرار میشود.