یک ابزار خودکارسازی که بر پایه Safari MCP ساخته شده بود، در حالی که تب داشبورد توسعهدهنده در حال خوانده شدن بود، آن را بست. این حادثه نقص پنهانی را در «نگهبان» (guard) آشکار کرد که وظیفه داشت مانع از دسترسی عوامل (agents) مبتنی بر هوش مصنوعی به تبهایی شود که متعلق به آنها نبودند؛ این اتفاق نشان میدهد که چرا دستهبندیهای «امنیت پیشفرض» (safe-by-default) میتوانند تبدیل به یک نقطه ضعف شوند.
نگهبانی که کار میکرد—تا زمانی که دیگر نکرد
این ابزار هر تب ایجاد شده را با یک شناسه داخلی نشانهگذاری میکند. قبل از اینکه عامل (agent) هر دستوری را صادر کند، نگهبان آن نشانگر را بررسی میکند؛ اگر نشانگر وجود نداشته باشد، نگهبان از انجام عملیات خودداری میکند. در عمل، نگهبان مانع از خواندن صفحهای توسط عامل شد که خودش آن را باز نکرده بود—دقیقاً همان کاری که برای انجامش طراحی شده بود.
هنگام پر کردن یک فرم، صفحه به دامنه دیگری تغییر مسیر داد (redirect). این تغییر مسیر، نشانگر را حذف کرد و تب را بدون برچسب باقی گذاشت. نگهبان متوجه نبود نشانگر شد و گزارش داد: «نمیتوانم مالکیت را تأیید کنم، بنابراین این تب را نخواهم خواند.» در آن مرحله، بررسی امنیتی دقیقاً طبق انتظار عمل کرد.
کد پاکسازی که از حد گذشت
مرحله بعد، یک روال پاکسازی دستی بود که هدف آن بستن تبهای یتیم (orphaned tabs)—یعنی تبهای بدون نشانگر—بود. این روال از ابزار خواست تا بدون تأیید مالکیت، یک تب را «ببندد» (close a tab). از آنجایی که نگهبان نتوانست ثابت کند تب متعلق به خود ابزار است، ابزار به یک اقدام پیشفرض بازگشت: «بستن تب فعلی» (close the current tab). تب فعلی، همان داشبوردی بود که توسعهدهنده در حال مطالعه آن بود، نه یک تب یتیم.
نتیجه، یک عملیات مخرب بود که توسط یک مسیر امنیتی فعال شد که باید به یک بنبست ختم میشد.
سه لایهای که «عدم مالکیت» را به معنای اجازه تلقی کردند
- دستهبندی دستورات – فهرستی که دستورات را گروهبندی میکرد،
close_tabرا زیر یک دسته کلی به نام «مدیریت تب» (tab management) قرار داده بود. توسعهدهنده تصور میکرد هر چیزی در آن دسته بیخطر است، زیرا سایر دستورات (مانندlist tabs) فقط اطلاعات را میخواندند. هیچ یادداشت صریحیclose_tabرا به عنوان یک دستور مخرب علامتگذاری نکرده بود، بنابراین این دستور، امنیتِ ادراکشدهی همسایگان خود را به ارث برد. - سیاست در سطح افزونه – افزونه Safari که میانجی تمام اقدامات مرورگر بود، زمانی که نشست (session) مالکیت هیچ چیزی را نداشت، اجازه هر عملیاتی را میداد. این قانون برای اقدامات «فقط خواندنی» (read-only) کار میکند، اما راه را برای اجرای
close_tabبدون بررسی منشأ (provenance check) نیز باز کرد. - عدم تطابق منطقی – روال پاکسازی، پرچم مالکیت را روی یک تب بررسی کرد، اما سپس تابع بستن را روی تبی که مرورگر به عنوان «فعلی» (current) گزارش کرده بود، فراخوانی کرد. این عدم تطابق باعث شد که ناتوانی نگهبان در یافتن نشانگر، دستور بستن را دور بزند و آن را به هدف اشتباه هدایت کند.
هر لایه فرض میکرد که «عدم ثبت مالکیت» به معنای «امن بودن برای اقدام» است و در مجموع، آنها دستوری برای بستن تب ایجاد کردند که بدون هیچ مدرکی بر مشروعیت، اجرا شد.
راه حل: اثبات مالکیت برای اقدامات مخرب الزامی است
منطق اصلاحشده، مسیرهای «فقط خواندنی» را از مسیرهای «مخرب» جدا میکند. اکنون، قبل از اینکه دستور close_tab اجرا شود، ابزار باید یک نشانگر معتبر برای تب هدف ارائه دهد. اگر نشانگر وجود نداشته باشد، دستور به جای بازگشت به حالت پیشفرض (تب فعلی)، یک خطا صادر میکند. نگهبان دیگر به یک شاخه عمومیِ «انجام دادنِ کاری» باز نمیگردد.
این تغییر، وضعیت مبهمی را که در آن نبودِ نشانگر میتوانست هم به معنای «کاری برای انجام دادن نیست» و هم به معنای «ادامه بده و اقدام کن» تفسیر شود، از بین میبرد. با اجبار به بروز یک خطای صریح، ابزار از کار کاربر در برابر از دست رفتن تصادفی محافظت میکند.
آنچه توسعهدهندگان باید مراقب آن باشند
- اجازه ندهید نام یک دسته، امنیت را تعیین کند – برچسبی مانند «مدیریت تب» هیچچیز درباره تأثیر هر دستور در داخل آن نمیگوید. هزینه هر عملیات (خواندن در مقابل تخریب) را در کنار خودِ دستور ثبت کنید.
- شرایط نگهبان باید با شدت اقدام مطابقت داشته باشد – بررسیای که برای یک درخواست خواندن کافی است، برای دستوری که میتواند دادهها را حذف کند، کافی نیست. برای هر کلاس از تأثیرات، خط لولههای اعتبارسنجی مجزایی بسازید.
- از بازگشتهای ضمنی (implicit fallbacks) اجتناب کنید – وقتی نگهبان نمیتواند مالکیت را تأیید کند، امنترین پاسخ لغو کردن (abort) است، نه انتخاب یک هدف پیشفرض. اقدامات پیشفرض منبع رایج باگهای ارتقای سطح دسترسی (privilege-escalation) هستند.
- فرضهای مربوط به مجاورت را بازبینی کنید – هر لیست یا منویی را که در آن دستورات در کنار هم قرار دارند، بررسی کنید. اگر کد به طور صریح امنیت را دوباره ارزیابی نکند، یک دستور بیخطر میتواند اعتمادی را که به همسایگانش گذاشته شده، به ارث ببرد.
نکته کلیدی
نبودِ نگهبانِ مالکیت یک باگ نیست؛ بلکه یک شکاف طراحی است. با هر دستور مخرب به عنوان یک حوزه امنیتی مجزا برخورد کنید که نیازمند اثبات صریح صلاحیت است، و هرگز اجازه ندهید «نبود نشانگر» به معنای «ادامه بده» تفسیر شود. تنها در این صورت است که ابزارهای خودکارسازی میتوانند از همان تبهایی محافظت کنند که قرار است آنها را مدیریت کنند.
