برای دههها، آزمون ایستای امنیت برنامهها (SAST) یکی از مؤثرترین روشها برای مقیاسپذیر کردن بازبینی کد توسط تیمهای امنیتی بوده است.
اما زمانی که Codex Security را ساختیم، یک تصمیم طراحی آگاهانه گرفتیم: کار را با وارد کردن گزارش تحلیل ایستا و سپردن بررسی و اولویتبندی آن به عامل شروع نکردیم. ما سیستم را طوری طراحی کردیم که کار را از خود محل نگهداری کد—یعنی معماری آن، مرزهای اعتماد و رفتار مورد انتظار—آغاز کند و پیش از آنکه از یک انسان بخواهد زمانی صرف کند، یافتههایش را اعتبارسنجی کند.
دلیل ساده است: سختترین آسیبپذیریها معمولاً مشکلات جریان داده نیستند. این اتفاق زمانی رخ میدهد که کد به نظر میرسد یک بررسی امنیتی را اعمال میکند، اما آن بررسی در واقع ویژگیای را که سیستم به آن متکی است تضمین نمیکند. به عبارت دیگر، چالش فقط دنبال کردن نحوهٔ جریان داده در یک برنامه نیست—بلکه این است که مشخص شود آیا سازوکارهای دفاعیِ موجود در کد واقعاً بهدرستی عمل میکنند یا نه.
SAST معمولاً بهصورت یک فرایند خطی و شفاف توصیف میشود: شناسایی منبع ورودیِ غیرقابلاعتماد، دنبال کردن جریان داده در برنامه، و علامتگذاری مواردی که این داده بدون پاکسازی به یک مقصد حساس میرسد. این مدلی زیباست و بسیاری از باگهای واقعی را پوشش میدهد.
در عمل، SAST برای آنکه در مقیاس بزرگ قابل مدیریت و اجرا باقی بماند، ناچار است از تقریبها استفاده کند—بهویژه در پایگاههای کد واقعی که با ارجاعهای غیرمستقیم، ارسال پویا، توابع بازگشتی، بازتاب و جریانهای کنترلیِ وابسته به چارچوبها سروکار دارند. این تقریبها نقص SAST محسوب نمیشوند؛ بلکه واقعیتِ تلاش برای تحلیل و استدلال دربارهٔ کد بدون اجرای آن هستند.
این، بهتنهایی، دلیل این نیست که Codex Security با یک گزارش SAST شروع نمیکند.
مسئلهٔ عمیقتر این است که پس از آنکه با موفقیت مسیر یک منبع را تا یک مقصد دنبال میکنید، چه اتفاقی میافتد.
حتی زمانی که تحلیل ایستا بتواند ورودی را در میان چندین تابع و لایه بهدرستی ردیابی کند، باز هم باید به پرسشی پاسخ دهد که در نهایت تعیین میکند آیا یک آسیبپذیری وجود دارد یا خیر:
یک الگوی رایج را در نظر بگیرید: کد پیش از نمایش محتوای غیرقابلاعتماد، تابعی مانند sanitize_html() را فراخوانی میکند. یک تحلیلگر ایستا میتواند تشخیص دهد که تابع پاکسازی اجرا شده است. آنچه معمولاً نمیتواند تعیین کند این است که آیا آن تابع پاکسازی واقعاً برای بافتِ نمایشِ خاص، موتور قالب، رفتار کدگذاری و تبدیلهای بعدیِ درگیر، کافی و مناسب است یا خیر.
اینجاست که کار پیچیده میشود. مشکل فقط این نیست که آیا داده به یک مقصد میرسد. بلکه اینکه آیا بررسیها در کد واقعاً مقدار را به همان شکلی محدود میکنند که سامانه فرض میکند یا خیر.
به عبارت دیگر: تفاوت بزرگی بین «کد یک تابع پاکسازی را فراخوانی میکند» و «سیستم امن است.»وجود دارد.
این یک الگو است که در سیستمهای واقعی، دائماً دیده میشود.
یک برنامهٔ وب یک payload از نوع JSON دریافت میکند، مقدار redirect_url را استخراج میکند، آن را در برابر یک عبارت منظمِ فهرست مجاز اعتبارسنجی میکند، آن را URL-decode میکند و سپس نتیجه را به تابع هدایت ارسال میکند.
یک گزارش کلاسیک منبع به مقصد میتواند جریان را توصیف کند:
ورودی غیرقابلاعتماد → بررسی regex → رمزگشایی URL → تغییر مسیر
اما پرسش واقعی این نیست که آیا آن بررسی وجود دارد یا خیر. بلکه این است آیا آن بررسی پس از تبدیلهایی که در پی میآیند، همچنان مقدار را محدود میکند یا خیر.
اگر regex قبل از رمزگشایی اجرا شود، آیا واقعاً URL رمزگشاییشده را به همان شکلی که گردانندهٔ تغییرمسیر آن را تفسیر میکند محدود میکند؟
پاسخ دادن به این یعنی استدلال دربارهٔ کل زنجیرهٔ تبدیل: اینکه regex چه چیزهایی را مجاز میداند، رمزگشایی و نرمالسازی چگونه رفتار میکنند، تجزیهٔ URL با حالتهای مرزی چگونه برخورد میکند، و منطق تغییرمسیر طرحها و مقامات را چگونه حلوفصل میکند.
بسیاری از آسیبپذیریهایی که در عمل اهمیت دارند به این شکل هستند: اشتباهات در ترتیب عملیات، نرمالسازی ناقص، ابهامهای تجزیه و عدم تطابق بین اعتبارسنجی و تفسیر. جریان داده قابل مشاهده است. ضعف در نحوهٔ انتقال محدودیتها در طول زنجیرهٔ تبدیل است—یا در جایی که این محدودیتها منتقل نمیشوند.
این فقط یک الگوی نظری نیست. Express در CVE-2024-29041(در یک پنجره جدید باز میشود)، تحت تأثیر یک مشکل تغییر مسیر باز قرار گرفت که در آن URLs بدشکل میتوانستند پیادهسازیهای رایج فهرست مجاز را به دلیل نحوه کدگذاری و سپس تفسیر اهداف تغییر مسیر دور بزنند. جریان داده ساده بود. پرسش دشوارتر—و همان پرسشی که تعیین میکرد آیا باگ وجود داشت—این بود که آیا اعتبارسنجی پس از زنجیرهٔ تبدیل همچنان برقرار میماند یا خیر.
Codex Security حول یک هدف ساده طراحی شده است: کاهش بارِ اولویت بندی از طریق برجستهسازی مشکلاتی که با شواهد قویتری همراه هستند. در محصول، این یعنی استفاده از زمینهٔ مخصوص مخزن (از جمله یک مدل تهدید) و اعتبارسنجی مسائل پُرسیگنال در یک محیط ایزوله پیش از آنکه آنها را مطرح کنیم.
وقتی Codex Security به نقطهای میرسد که به نظر میرسد «اعتبارسنجی» یا «پاکسازی» انجام شده، آن را صرفاً بهعنوان یک تیکزدن صوری در نظر نمیگیرد. سعی میکند بفهمد کد در تلاش است چه چیزی را تضمین کند—و سپس تلاش میکند آن تضمین را نقض کند.
در عمل، معمولاً به صورت ترکیبی از موارد زیر است:
- خواندن مسیر کد مرتبط با زمینه کامل محل نگهداری، به شیوهای که یک پژوهشگر امنیتی انجام میدهد، و جستوجوی عدمتطابقها بین قصد و پیادهسازی. این شامل نظرات هم میشود، اما مدل لزوماً نظرات را باور نمیکند، بنابراین اضافه کردن //Halvar says: this is not a bug بالای کدتان اگر واقعاً یک باگ وجود داشته باشد، آن را گیج نمیکند.
- کاهش دادن مسئله به کوچکترین بخش قابلآزمایش (برای مثال، روند تبدیل پیرامون یک ورودیِ واحد)، تا بتوان بدون اینکه بقیهٔ سیستم مزاحم شود، دربارهاش استدلال کرد. از این نظر، Codex Security برشهای کوچک کد را استخراج میکند و سپس برای آنها میکروفازر مینویسد.
- استدلال درباره محدودیتها در سراسر تبدیلها، بهجای اینکه هر بررسی را بهطور مستقل تلقی کنید. در موارد مقتضی، این میتواند شامل صورتبندی بهصورت یک سوالِ ارضاپذیری باشد. به عبارت دیگر، ما به مدل دسترسی به یک محیط Python با z3-solver میدهیم و این مدل در صورت نیاز در استفاده از آن خوب است، درست همانطور که یک انسان هنگام پاسخ دادن به یک مسئلهٔ محدودیتِ ورودیِ بهویژه پیچیده مجبور است این کار را انجام دهد. این بهویژه برای بررسی سرریز عدد صحیح یا باگهای مشابه روی معماریهای غیر استاندارد مفید است.
- اجرای فرضیهها در صورت امکان، در یک محیط اعتبارسنجیِ سندباکسشده، تا «این میتواند یک مشکل باشد» را از «این یک مشکل است» متمایز کند. هیچ اثباتی بهتر از یک PoC کامل از ابتدا تا انتها با کدی که در حالت دیباگ کامپایل شده باشد، نیست.
این تغییرِ کلیدی است: بهجای اینکه صرفاً به «وجود یک بررسی» بسنده کند، سیستم به سمت این میرود که «آیا آن ویژگیِ پایدار برقرار است (یا نه)، و شواهد آن چیست». و مدل بهترین ابزار را برای آن کار انتخاب میکند.
یک واکنش منطقی این است: چرا هر دو را انجام ندهیم؟ با یک گزارش SAST شروع کنید، سپس از عامل برای تحلیل عمیقتر استفاده کنید.
مواردی وجود دارد که یافتههای از پیش محاسبهشده مفید هستند—بهویژه برای کلاسهای محدود و شناختهشدهٔ باگ. اما برای عاملی که برای کشف و اعتبارسنجی آسیبپذیریها در زمینه طراحی شده است، شروع از یک گزارش SAST سه حالت شکست قابل پیشبینی ایجاد میکند.
نخست، میتواند موجب محدود کردن زودهنگام شود. فهرست یافتهها نقشهای است از اینکه یک ابزار از قبل کجا را بررسی کرده است. اگر آن را بهعنوان نقطه شروع در نظر بگیرید، میتوانید سیستم را به سمت صرف کردن تلاشی نامتناسب در همان نواحی، استفاده از همان انتزاعها، و از دست دادن دستههایی از مسائل که با جهانبینی ابزار جور درنمیآیند، سوگیری دهید.
دوم، میتواند قضاوتهای ضمنیای ایجاد کند که باز کردن آنها دشوار باشد. بسیاری از یافتههای SAST فرضیات مربوط به پاکسازی، اعتبارسنجی یا مرزهای اعتماد را کدگذاری میکنند. اگر آن فرضها اشتباه باشند—یا صرفاً ناقص باشند—وارد کردنشان به حلقه استدلال میتواند عامل را از «بررسی» به «تأیید یا رد» سوق دهد، که این چیزی نیست که میخواهیم عامل انجام دهد.
سوم، این میتواند ارزیابی سیستم استدلال را دشوارتر کند. اگر پایپلاین با خروجی SAST شروع شود، جدا کردن اینکه عامل چه چیزی را از طریق تحلیل خودش کشف کرده است از آنچه از یک ابزار دیگر به ارث برده است دشوار میشود. این جداسازی مهم است اگر میخواهید توانمندیهای سیستم را بهطور دقیق بسنجید، که برای بهبود یافتن سیستم در گذر زمان لازم است.
پس Codex Security را ساختیم تا از جایی شروع کنیم که تحقیقات امنیتی شروع میشود: از کد و قصد سیستم، با اعتبارسنجیای که برای بالا بردن سطح اطمینان پیش از آنکه یک انسان را متوقف کنیم به کار میرود.
ابزارهای SAST میتوانند در کاری که برای آن طراحی شدهاند بسیار عالی عمل کنند: اعمال استانداردهای کدنویسی امن، شناسایی مشکلات ساده «از منبع تا مقصد»، و تشخیص الگوهای شناختهشده در مقیاس با مصالحههای پیشبینیشده. آنها میتوانند بخشی قدرتمند از دفاع چندلایه باشند.
دامنهٔ این نوشته محدودتر است: توضیح میدهد چرا ایجنتی که برای استدلال دربارهٔ رفتار و اعتبارسنجیِ یافتهها طراحی شده است، نباید کارش را با تکیه بر یک فهرستِ ایستای یافتهها آغاز کند.
همچنین شایسته است به یک محدودیت مرتبطِ تفکرِ صرفاً منبعبهنقطهٔ مصرف اشاره کنیم: همهٔ آسیبپذیریها یک مشکل جریانِ داده نیستند. بسیاری از شکستهای واقعی مسائل مربوط به وضعیت و ناوردایی هستند—دور زدن جریان کاری، شکافهای مجوزدهی، و باگهای «سیستم در وضعیت اشتباه است». برای این نوع باگها، یک مقدار آلوده حتی به یک «نقطهٔ مصرف خطرناک» نمیرسد. خطر در این است که برنامه فرض میکند چه چیزی همیشه درست خواهد بود.
ما انتظار داریم که اکوسیستم ابزارهای امنیتی همچنان به پیشرفت ادامه دهد: تحلیل ایستا، آزمون فازی، محافظهای زمان اجرا و جریانهای کاری عاملمحور همگی نقش مهمی خواهند داشت.
آنچه میخواهیم Codex Security در آن خوب باشد، بخشی است که برای تیمهای امنیتی بیشترین هزینه را دارد: تبدیل «این مشکوک به نظر میرسد» به «این واقعی است، اینطور از کار میافتد، و این هم یک راهکار که با قصد سیستم همراستا است.»
اگر میخواهید درباره اینکه Codex Security چگونه محل نگهداری را اسکن میکند، یافتهها را اعتبارسنجی میکند و اصلاحها را پیشنهاد میدهد بیشتر بدانید، مستندات ما(در یک پنجره جدید باز میشود) را مشاهده کنید.


