پروژه‌ای را به یاد می‌آورم که سه بار در شش ماه پاک‌سازی شد و هر بار، مدیرش می‌گفت «الان امن است». مشکل این نبود که نمی‌خواست امنیت جدی بگیرد؛ مشکل این بود که هیچ‌وقت نپرسیده بود اصلاً از کجا وارد شده‌اند. وقتی بالاخره لاگ‌ها را خواندیم، مسیر حمله واضح شد: یک افزونه‌ی فراموش‌شده با نسخه‌ای قدیمی، یک آسیب‌پذیری شناخته‌شده داشت که چند ماه قبل از آن منتشر شده بود. اگر آن افزونه یک بار در چرخه بازبینی‌های معمول دیده شده بود، هر سه پاک‌سازی لازم نمی‌شد. آسیب‌پذیری وب (Web Vulnerability) یعنی ضعفی در طراحی، پیاده‌سازی یا پیکربندی یک برنامه که مهاجم می‌تواند از آن سوءاستفاده کند. در این نوشته، تمرکزم روی بخشی است که نیمی از پاک‌سازی‌های اجباری از نداشتنش می‌آید: شناسایی درست، قبل از اینکه روز شلوغ فرا برسد.

آسیب‌پذیری با باگ چه تفاوتی دارد

در گفتگوهای روزمره، این دو را به‌جای هم به کار می‌برند، اما تفاوتشان تصمیم‌ساز است. باگ یعنی رفتاری که با خواست توسعه‌دهنده فرق دارد و اثرش معمولاً عملکردی است — مثلاً صفحه‌بندی اشتباه، فرمی که ذخیره نمی‌کند، یا محاسبه قیمتی که غلط درمی‌آید. آسیب‌پذیری یعنی همان رفتار اشتباه، از دید یک مهاجم قابل سوءاستفاده است. هر آسیب‌پذیری یک باگ هست، اما هر باگی آسیب‌پذیری نیست. یک صفحه‌بندی اشتباه معمولاً فقط UX را خراب می‌کند؛ اما یک فیلد اعتبارسنجی‌نشده می‌تواند پل ورود به دیتابیس شود. همین تفکیک، اولین کاری است که در جلسه با کارفرما انجام می‌دهم: تشخیص اینکه کدام مورد روی جدول اولویت قرار می‌گیرد و کدام در بک‌لاگ توسعه می‌ماند. برای درک جایگاه این بحث در تصویر بزرگ، مقاله امنیت وب چیست نقطه شروع خوبی است.

نقشه OWASP و دسته‌بندی واقعی آسیب‌پذیری‌ها

مرجعی که همه متخصصان روی آن توافق دارند، لیست OWASP (Open Web Application Security Project) است. هر چند سال یک بار به‌روز می‌شود و ده دسته اصلی آسیب‌پذیری را کنار هم می‌گذارد. تمرکز این لیست روی «چگونگی سوءاستفاده» نیست، روی «نوع ضعف» است. سه دسته بالای این لیست، به تجربه من، بیشترین سهم را در حوادث واقعی دارند:

  • کنترل دسترسی معیوب (Broken Access Control): کاربری که نباید به منبعی دسترسی داشته باشد، به آن می‌رسد. شامل IDOR (Insecure Direct Object Reference) که در آسیب‌پذیری IDOR جدا توضیح داده‌ام.
  • ایرادهای رمزنگاری (Cryptographic Failures): اطلاعات حساس بدون رمزنگاری درست ذخیره یا منتقل می‌شوند. تا وقتی روی HTTP کار می‌کنید، این مورد روی میز است؛ گام درست در نصب SSL آمده.
  • تزریق (Injection): داده‌ای که به‌عنوان ورودی وارد می‌شود، به‌عنوان دستور اجرا می‌شود. معروف‌ترینش SQL Injection است که در جلوگیری از SQL Injection مفصل نوشته‌ام، اما انواع XSS و Command Injection هم در همین دسته‌اند.

سه دسته بعدی — پیکربندی نادرست، اجزای آسیب‌پذیر و شناخته‌نشده، و نقص در احراز هویت — بیشتر در پروژه‌های واقعی خودشان را نشان می‌دهند. یعنی روی کاغذ دسته‌های بعدی‌اند، اما در آمار پاک‌سازی‌های من جزو اولین‌ها هستند.

آسیب‌پذیری فقط از کد نمی‌آید. نیمی از حفره‌هایی که در پروژه‌های واقعی دیده‌ام، از پیکربندی، فراموشی و افزونه‌های قدیمی آمده‌اند؛ نه از منطق برنامه.

سه دسته‌ای که بیشترین آسیب را می‌زنند

اگر بخواهم تمرکزم را روی سه چیز بگذارم که در پروژه‌هایم پرتکرارند، این‌ها هستند:

یک: آسیب‌پذیری‌های احراز هویت (Authentication). از ضعف رمز عبور تا نبود 2FA (Two-Factor Authentication) تا خطای منطقی در بازیابی رمز. جایی که یک مهاجم می‌تواند وارد شود و به‌عنوان یک کاربر عادی، رفتار کند. برای پیشگیری در وردپرس، مسیر دقیق در فعال‌سازی 2FA برای کاربران وردپرس آمده.

دو: آسیب‌پذیری‌های کنترل دسترسی. مثلاً کاربر عادی بتواند با تغییر یک عدد در آدرس، به فایل کاربر دیگری دسترسی پیدا کند. این گروه اغلب در بازبینی دستی دیده می‌شود نه با اسکنر؛ چون به معنای استاندارد، رفتار برنامه خطای واضحی نشان نمی‌دهد.

سه: اجزای آسیب‌پذیر. این همان دسته‌ای است که در داستان ابتدای مقاله، سه بار پاک‌سازی را تحمیل کرد. قالب و افزونه‌های به‌روزرسانی‌نشده، بیشترین سهم را در نفوذ به سایت‌های وردپرسی دارند. نشانه‌های ریسک در همین زمینه در آسیب‌پذیری افزونه‌های وردپرس جدا نوشته شده.

نشانه‌های اولیه: کجا به آسیب‌پذیری مشکوک شویم

قبل از هر ابزاری، یک نگاه تحلیلی به کد و پیکربندی سایت، نیمی از راه است. این نشانه‌ها هستند که به من می‌گویند کدام پروژه باید اول برود:

  • فرم‌هایی که مقدارشان در دیتابیس ذخیره می‌شود اما لایه اعتبارسنجی ندارند.
  • URLهایی با شناسه عددی مستقیم (مثلاً ?user_id=42) که بدون بررسی مالکیت، محتوا برمی‌گردانند.
  • فایل‌های .env، wp-config.php.bak یا نسخه‌های بکاپ که از بیرون قابل خواندن‌اند.
  • لاگ‌های سرور که خطاهای SQL یا مسیر فایل واقعی را برمی‌گردانند؛ خود این خطاها هم نشانه‌اند هم سیگنال خطر.
  • قالب یا افزونه‌ای که بیش از شش ماه آپدیت نشده و در مخزن رسمی هم علامت‌گذاری شده.

این لیست، جایگزین ابزار نیست؛ فیلتر اولیه است. اما در پروژه‌های کوچک و متوسط، همین پنج مورد، حجم کاری ممیزی را نصف می‌کند.

شناسایی خودکار: اسکنرها و مرزهایشان

ابزارهای خودکار، اولین قدم در شناسایی‌اند چون سرعت و پوشش‌شان از انسان بیشتر است. سه دسته اصلی وجود دارد:

  • اسکنرهای آسیب‌پذیری وب (Web Vulnerability Scanner): طیف وسیعی از آسیب‌پذیری‌های شناخته‌شده را روی یک دامنه بررسی می‌کنند. فهرستی از این ابزارها را در اسکنرهای آسیب‌پذیری وب جمع کرده‌ام.
  • اسکنرهای اختصاصی CMS: روی وردپرس تمرکز می‌کنند و نسخه هسته، قالب، افزونه‌ها و کانفیگ را با پایگاه داده آسیب‌پذیری‌های شناخته‌شده تطبیق می‌دهند.
  • اسکنرهای پیکربندی و SSL: روی لایه پایین‌تر کار می‌کنند؛ وجود هدرهای امنیتی، درستی زنجیره گواهی، و پیکربندی سرور را چک می‌کنند.

اما مرزهایشان را هم بشناسید: اسکنرها فقط آسیب‌پذیری شناخته‌شده را می‌بینند. اگر منطق برنامه‌ی شما شرط دسترسی را اشتباه پیاده کرده باشد اما این شرط در هیچ پایگاه داده شناخته‌شده‌ای نباشد، اسکنر خطای واضحی نمی‌دهد. همچنین اسکنرها در برابر آسیب‌پذیری‌های منطقی کسب‌وکار، مثل تخفیف‌های چندباره یا مسیر پرداخت اشتباه، عملاً کورند. به همین دلیل است که همیشه پس از اسکن خودکار، فاز بازبینی دستی می‌گذارم.

شناسایی دستی: کاری که هیچ اسکنری انجام نمی‌دهد

بازبینی دستی، کندتر است اما عمیق‌تر. من روی سه بخش تمرکز می‌کنم:

اول، مسیرهای اعتبارسنجی ورودی. هرجایی که داده کاربر ذخیره، بازیابی یا روی صفحه رندر می‌شود را دنبال می‌کنم. فرض اولیه‌ام این است که هر ورودی، تا زمانی که خلافش ثابت نشود، مشکوک است. اگر خروجی در HTML بدون پاک‌سازی چاپ می‌شود، اینجا بستر XSS است که مسیر دفاعش در جلوگیری از XSS آمده.

دوم، چرخه احراز هویت و نشست. آیا خروج کاربر، نشست را واقعاً باطل می‌کند؟ آیا توکن CSRF روی فرم‌های تغییر داده چک می‌شود؟ مسیر دفاعی CSRF در پیشگیری از CSRF است و برای APIها، امن‌سازی API وب راهنمای جدا دارد.

سوم، مسیرهای نادیده‌گرفته‌شده. فایل‌های بکاپ، مسیرهای ادمین بدون احراز هویت، پوشه uploads با PHP اجرایی، و ریسک RFI و LFI (Remote و Local File Inclusion) که در تفاوت RFI و LFI باز کرده‌ام. این‌ها اغلب قاتلان خاموشند چون در ظاهر بی‌خطرند.

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

پروتکل گام‌به‌گام شناسایی در یک سایت وردپرسی

چیزی که برای مشتریان تازه‌کار اجرا می‌کنم، این ترتیب است:

  1. فهرست موجودی: همه افزونه‌ها، قالب‌ها، نسخه هسته، کاربران و نقش‌هایشان را در یک فایل لیست می‌کنم.
  2. مقایسه با نسخه‌های فعلی: هر افزونه‌ای که بیش از شش ماه آپدیت نشده را علامت می‌زنم.
  3. اسکن خودکار سبک: یک ابزار بیرونی روی دامنه؛ نه برای پوشش کامل، برای برداشت سریع سیگنال. اگر گواهی SSL یا هدرهای امنیتی ناقص بودند، همان‌جا اضافه می‌شود.
  4. بازبینی فایل‌های حساس: دسترسی بیرونی به wp-config.php، .htaccess، پوشه uploads و بکاپ‌ها را چک می‌کنم؛ راهنمای عملی در امن‌سازی wp-config آمده.
  5. مرور کاربران و نقش‌ها: کاربران مدیر قدیمی، حساب‌های ناشناس و نقش‌های اضافی را حذف می‌کنم.
  6. بررسی لاگ‌ها: لاگ‌های سرور و افزونه امنیتی را برای الگوهای حمله یا رفتارهای مشکوک مرور می‌کنم.
  7. ثبت یافته‌ها در یک نقشه: هر یافته با شدت، احتمال سوءاستفاده و اثر واقعی ثبت می‌شود؛ نه فقط یک لیست ترسناک.

اگر بخواهید همین ترتیب را برای بقیه بخش‌ها هم داشته باشید، راهنمای تست امنیت وب‌سایت و پیدا کردن آسیب‌پذیری سایت همان گام‌ها را در سطح جامع‌تر تکرار می‌کنند.

تفاوت شناسایی در وردپرس و اپلیکیشن اختصاصی

دو بستر متفاوت‌اند و ابزار مشترک کافی نیست. در وردپرس، بخش بزرگی از ریسک از طرف اکوسیستم می‌آید: هسته، قالب، افزونه‌ها. این یعنی فهرست موجودی و مقایسه نسخه‌ها بخش مهمی از کار است و ابزارهایی مثل Wordfence یا Sucuri می‌توانند این لایه را پوشش بدهند؛ راهنمای انتخابشان در بهترین افزونه‌های امنیتی وردپرس و پایه‌های دستی در امنیت وردپرس برای مبتدیان آمده.

در اپلیکیشن اختصاصی، ریسک اصلی در منطق خودتان است. اینجا مسیر تحلیل کد، بازبینی مدل‌های دسترسی، درستی مصرف APIها و امنیت نشست مهم‌تر از اسکنر می‌شود. ترکیب درست، اسکن خودکار برای گرفتن خطاهای شناخته‌شده + بازبینی منطق برای یافتن نقاط کور است. بسته به معماری، همیشه بخشی از آسیب‌پذیری‌ها را فقط با چشم انسانی می‌بینید.

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

یافتن آسیب‌پذیری‌ها پایان کار نیست؛ آغازی برای کار سخت‌تر است. اگر لیست را همان روز بخواهید کامل رفع کنید، احتمالاً فقط بخش سطحی را انجام می‌دهید و بخش عمیق پنهان می‌ماند. رویکردی که در پروژه‌ها اجرا می‌کنم، سه لایه دارد:

  1. کاهش سطح حمله: هر افزونه یا سرویسی که استفاده نمی‌شود حذف شود، دسترسی‌های اضافی بسته شود، و مسیرهای حساس از بیرون بسته شوند. این کار، لیست آسیب‌پذیری‌ها را کوتاه می‌کند بدون کدنویسی.
  2. رفع موارد بحرانی: نقاطی که احتمال سوءاستفاده فعال دارند — آسیب‌پذیری شناخته‌شده در افزونه، تزریق در فرم‌ها، دسترسی باز به فایل حساس.
  3. رفع موارد ساختاری: منطق کنترل دسترسی، مدل احراز هویت، بازبینی کد جدید. این بخش زمان می‌برد اما ریسک بلندمدت را پایین می‌آورد.

ترتیب دقیق اولویت‌بندی را در اولویت‌بندی و رفع آسیب‌پذیری‌ها نوشته‌ام و مرجع طبقه‌بندی بین‌المللی‌شان، CVE (Common Vulnerabilities and Exposures)، در CVE چیست توضیح داده شده است. آنچه اشتباهات رایج این مرحله را پوشش می‌دهد، در اشتباهات مدیریت آسیب‌پذیری و اشتباهات رایج امنیت وب آورده‌ام.

پرسش‌های پرتکرار درباره شناسایی آسیب‌پذیری وب

هر چند وقت باید اسکن کنم؟ برای سایت وردپرسی فعال، ماهانه یک اسکن خودکار و در هر آپدیت بزرگ، بازبینی دستی کوتاه. اگر سرویس بحرانی دارید، اسکن را در چرخه استقرار بگذارید؛ یعنی هر انتشار، یک اسکن سبک.

آیا اسکن خودکار می‌تواند به سایت آسیب بزند؟ بله، بعضی اسکنرها در حالت تهاجمی ترافیک سنگینی می‌فرستند یا فرم‌ها را با داده تست پر می‌کنند. همیشه اول روی محیط استجینگ یا روی سایت آفلاین اجرا کنید. اگر استجینگ ندارید، این را در اولویت ساخت بگذارید.

Zero Day چیست و چگونه شناسایی می‌شود؟ آسیب‌پذیری صفر-روز (Zero-Day) یعنی حفره‌ای که هنوز وصله‌اش منتشر نشده یا کسی از آن باخبر نیست. این‌ها را نمی‌شود از روی امضا شناسایی کرد. دفاع در برابرشان عمدتاً روی سخت‌سازی سراسری، مانیتورینگ رفتار و کاهش سطح حمله سوار است؛ توضیح کامل در آسیب‌پذیری Zero Day.

بدون بودجه، چطور شروع کنم؟ با سه کار بدون هزینه: بستن دسترسی بیرونی به فایل‌های حساس، حذف افزونه‌های بی‌مصرف و به‌روز‌رسانی منظم هسته و پوسته. بیشترین اثر را همین سه کار در پروژه‌های کوچک می‌گذارد.

آیا فقط کدنویسی کافی است؟ نه. پیکربندی سرور، سیاست رمز عبور، نحوه بکاپ‌گیری و حتی آموزش کاربران، همه بخشی از امنیت‌اند. سیاهه کارهای پیشگیرانه در بهترین روش‌های امنیت وب جمع شده.

شناسایی، آغازی برای یک چرخه است

آسیب‌پذیری وب یک صفت ثابت نیست؛ یک وضعیت پویاست که با هر آپدیت، هر افزونه جدید و هر کد تازه بازتعریف می‌شود. برای همین، پیدا‌کردن یک بار آن کافی نیست. آن‌چه از تجربه پاک‌سازی‌های مکرر یاد گرفته‌ام این است که تنها راه خروج از چرخه حوادث، پذیرفتن شناسایی به‌عنوان یک عادت دوره‌ای است، نه یک پروژه سررسیدشده. اگر امروز فقط یک کار می‌کنید، فهرست افزونه‌ها و کاربران فعلی سایتتان را بردارید و کنار هر مورد بنویسید آخرین بار کِی بازبینی شده. هر موردی که جوابش می‌شود چند ماه پیش یا هرگز، نقطه شروع ممیزی شماست. و اگر در یکی از پروژه‌های خودتان، آسیب‌پذیری‌ای پیدا کرده‌اید که مدتی نادیده مانده بود، همین‌جا بنویسید؛ همان تجربه‌های واقعی، برای خواننده بعدی ارزشمندتر از هر چک‌لیست آماده است. 🔐