تصور کنید در یک مرکز نظارتی هوش مصنوعی (AI Observability Hub) خصوصی، تمام تستهای خودکار وضعیت «پاس» را گزارش میکنند، اما کنترلهای امنیتی در سکوت کامل در حال شکست خوردن هستند. این واقعیت تلخ در جریان یک تست نفوذ داخلی که در هفتهی گذشته (اوت ۲۰۲۶) انجام شد، آشکار شد و ثابت کرد که استدلال دربارهی یک دفاع (Reasoning about a defense)، با مشاهدهی عملکرد آن در برابر ترافیک واقعی زمین و آسمان فرق دارد.
بسیاری از توسعهدهندگان برای تأیید زیرساخت خود به مجموعهای از چکهای «سبز» تکیه میکنند. اما این بررسی نشان داد که خطرناکترین نقصها معمولاً در کنترلهایی هستند که بهتازگی نوشته شدهاند؛ کدهایی که دربارهشان استدلال شده اما هنوز با دادههای غیرقابلپیشبینی و پراکنده دنیای واقعی تحت فشار قرار نگرفتهاند. کدهای قدیمی چون با ترافیک واقعی برخورد کردهاند و کسی را غافلگیر کردهاند، مشاهده شدهاند؛ اما دفاعی که دیروز نوشته شده، تنها در ذهن نویسندهاش «درست» است، و این احساس شباهت، یک توهم است.
این وضعیت شبیه ساختن دیواری است که چون در نقشه درست است، فرض میکنیم محکم است، اما با اولین وزش باد فرو میریزد. در دنیای تلهمتری هوش مصنوعی، این «نقشه» اغلب یک فایل پیکربندی است که درست به نظر میرسد اما نمیتواند ویژگیهای (Attributes) واقعی را که توسط کلاینتهای مدرن هوش مصنوعی ارسال میشود، مدیریت کند. این چالشها نشان میدهد که تکیه بر لایههای سطحی کافی نیست و گاهی تغییر در معماری سیستم تنها راه حل بنیادین برای رفع توهمات و خطاهای مدلهاست، نه صرفاً اصلاح دستورالعملها.
زمینه: زیرساخت مورد بررسی
هدف این ممیزی، یک هاب نظارتی کوچک بود که برای کدنویسی به کمک هوش مصنوعی ساخته شده بود. این استک از شش سرویس تشکیل شده بود که همگی در یک فایل compose تعریف شده بودند:
- یک تونل برای دسترسی خارجی.
- یک جمعکننده OpenTelemetry (OTel Collector) برای دریافت متریکها و لاگها از Claude Code.
- Prometheus برای ذخیرهسازی متریکها.
- Grafana برای بصریسازی.
- Loki برای تجمیع لاگها.
- یک API وضعیت.
سطح دسترسی عمومی تنها به سه عدد کلی (Aggregate numbers) محدود شده بود و قرار بود هر چیز دیگری خصوصی بماند. برای جلوگیری از مسموم کردن نتایج یا تحمیل هزینههای مربوط به WAF، نویسنده یک ممیزی «فقط خواندنی» از کد و پیکربندی انجام داد و آن را با یک اجرای پویا روی استکی که بهصورت محلی در Docker بالا آمده بود، ترکیب کرد.
شکست لیستهای مجاز (Allow-Lists)
هدف این بود که اطمینان حاصل شود تنها سه عدد کلی به سطح عمومی میرسند و تمام دادههای هویتی خصوصی میمانند. مرز حریم خصوصی به جای استفاده از لیست عدممجاز (Deny-list)، از یک «لیست مجاز» (Allow-list) استفاده میکرد. با این حال، در اندازهگیریها مشخص شد که Claude Code پنج ویژگی هویتی، از جمله user.email حاوی یک آدرس واقعی را ارسال میکند، در حالی که هیچ پرچمی (Flag) برای خاموش کردن آنها وجود نداشت.
در ۲۰ اوت ۲۰۲۶، نویسنده کشف کرد که ویژگیهای هویتی به برچسبهای ایندکس Loki نشت میکنند. یک فرستنده که توکن دریافت (Ingest token) را در اختیار داشت، آن را به صورت claude-code-…[email protected] نوشت و آدرس ایمیل مستقیماً به عنوان یک برچسب ایندکس وارد شد. پیکربندی مورد استفاده از دو خط تشکیل شده بود:
keep_keys(resource.attributes, ["service.name"])set(resource.attributes["service.name"], "claude-code")
خط دوم زائد نیست؛ زیرا keep_keys کلیدها را فیلتر میکند، نه مقادیر را، و service.name همان ویژگی است که در Loki به برچسب ایندکس تبدیل میشود.
نویسنده در یادداشتهای طراحی، دو سد دفاعی روی این مرز را «مستقل» توصیف کرده بود؛ یکی در Collector و دیگری در Loki. با این حال، ویژگیهای دامنه (Scope attributes) بدون تغییر از هر دو عبور کردند. برای تست ایزولاسیون، با حذف لیست Loki، مشخص شد که یک مقدار تزریق شده مانند scope.secret قابل پرسوجو است، در حالی که هویت و محتوا خارج مانده بودند. تعمیر این نقص، یک افزودنی ساده به دستورات دامنه بود: keep_keys(scope.attributes, []).
وقتی اثباتها دروغ میگویند
این ممیزی یک شکست سیستمی در ابزارهای تأیید خود پروژه را هم افشا کرد. نویسنده از «اثباتهای مبتنی بر شل» (shell-based proofs) استفاده میکرد تا از شکستهای خاموش جلوگیری کند، اما خودِ این اثباتها بهصورت خاموش شکست خورده بودند. نویسنده یک شکست خاموش را در ابزاری پیدا کرد که تنها ۶ ساعت از عمرش میگذشت.
- عدم تطابق ایمیج: دو اثبات، نسخه ایمیج OTel Collector را دقیقاً روی نسخه ۰.۱۵۸.۰ قفل کرده بودند و در کامنتی ادعا شده بود که این نسخه با محیط تولید (Production) مطابقت دارد. اما یک PR وابستگیها، فایل compose و Dockerfile مربوط به Railway را به نسخه ۰.۱۵۹.۰ ارتقا داده بود. چون Dependabot اسکریپتهای شل را نمیخواند، اثباتها همچنان ایمیج قدیمی را میکشیدند و وضعیت را «پاس» نشان میدادند. ادعای «اثبات قرارداد روی ایمیج جدید سبز است» کاملاً دروغ بود.
- دروازههای CI شکسته: یک دروازه کنترل (CI gate) که برای جلوگیری از این عدم تطابق طراحی شده بود، خودش از بدو تولد خراب بود. این دروازه تعداد اثباتهایی را که ایمیج خود را از فایل میگرفتند، با جستوجوی رشتهی
docker-compose.ymlمیشمرد. چون کامنتی که نحوه استخراج را توصیف میکرد حاوی این رشته بود، حتی اگر خط کد واقعی حذف میشد، دروازه سبز میماند. این اتفاق دقیقاً پانزده خط پایینتر از کامنتی رخ داده بود که دقیقاً همین الگو را ممنوع میکرد. - نقاط کور اسکنر: یک اسکن ایمیج مسدودکننده به این دلیل سبز شد که حذف
pipدر واقع آن را بهطور کامل پاک نمیکند. دایرکتوریensurepip/_bundled/یک کپی دوم را به صورت wheel نگه میدارد و اسکنر داخل آرشیوها را نمیخواند.
تلهی Trace ID
شکست دیگری در مسیر لاگها رخ داد، جایی که یک خط کد قرار بود شناسه ردیابی (Trace ID) را در هر رکورد قبل از ذخیرهسازی صفر کند. دستور استفاده شده این بود: # looks right, fails on every record - set(log.trace_id.string, "").
این دستور در هر رکورد شکست میخورد زیرا ParseTraceID به ۳۲ کاراکتر هگز نیاز دارد و یک رشته خالی معتبر نیست. Collector خطای «شکست در اجرای دستور» (failed to execute statement) را ثبت میکرد و به مسیرش ادامه میداد، که باعث میشد این فیلد دستنخورده به Loki برسد. در ۲۱ اوت ۲۰۲۶، در مواجهه با ترافیک واقعی، این موضوع منجر به دو هشدار در هر رکورد — تقریباً ۱۸۰ هشدار در هر جلسه — شد.
اثباتها نمیتوانستند این را ببینند زیرا دادههای مصنوعی (synthetic payload) هرگز حاوی Trace ID نبودند. آنها «سبز، و کور» بودند. این ناتوانی در شناسایی خطاهای عملیاتی در محیطهای واقعی، یادآور شکست عاملهای کدنویس پیشرفته در مواجه با آزمونهای سختافزاری است که نشان میدهد شبیهسازیهای ساده هرگز جایگزین تستهای فشار واقعی نمیشوند.
شکستهای شرطی و مقادیر تهی (Nil)
یک شکست دوم و بدتر، از نوع شرطی بود. طبق مستندات OTTL، دستور set اگر مقدار به nil تبدیل شود، هیچ کاری انجام نمیدهد. خطی که قصد داشت بدنه لاگ را به نام رویداد (event name) تبدیل کند، روی هر رکوردی که فاقد event.name بود، هیچ اثری نداشت. در نتیجه، بدنهای حاوی یک پرامپت و یک آدرس، عیناً در Loki ذخیره میشد.
هیچ اثباتی این را نگرفت چون کلاینت همیشه event.name را میفرستاد و دادههای مصنوعی برای پاس شدن یک تأییدیه (Assertion) دیگر، به این فیلد نیاز داشتند. دفاع در دقیقترین حالتی که برایش طراحی شده بود، عملاً یک عملیات تهی (no-op) بود.
ریسک تزریق مقدار
نویسنده مسیر متریکها را بهصورت محلی با اسرارهای جعلی بالا آورد و متریکی ارسال کرد که حاوی یک ایمیل در user.email و یک شناسه در organization.id و یک مقدار خصمانه در service.name بود.
نتیجه نشان داد لیست مجاز برای «نامها» کار میکند: ایمیل و شناسه سازمان حذف شدند و service.name خصمانه پین شد. اما یک لیست مجاز از نامها، «مقادیر» را محدود نمیکند. هر کسی با داشتن توکن دریافت میتواند claude_code.token.usage را با هر عددی بنویسد.
در یک استک تست، نویسنده مقدار 1e12 توکن را تزریق کرد. چون پرسوجوهای عمومی از max_over_time(…[25h]) استفاده میکنند، این جهش تزریقی تا ۲۵ ساعت در سیستم باقی میماند. این یک آسیبپذیری ساختاری است زیرا توکن، تولیدکننده مورد اعتماد را شناسایی میکند و منبع حقیقت دومی برای این اعداد وجود ندارد.
گزارش پنهان
نسخهای از این مطلب وجود دارد که تمام نقاط ضعف باقیمانده در سیستم جاری را با نام، مسیر دقیق و پنجره زمانی لیست کرده است. آن گزارش در کشو میماند. در حالی که مخزن کد عمومی است، تجمیع تمام نقصهای باز در یک جا، افشای امنیتی نیست — بلکه مجموعهای از دستورالعملها برای یک مهاجم است.
این ممیزی این فرض را تغییر میدهد که «تستهای پاس شده» برابر با «سیستمهای امن» است. در زیرساختهای هوش مصنوعی، شکاف بین یک دفاع استدلالی و یک دفاع مشاهدهشده، جایی است که خطرناکترین آسیبپذیریها زندگی میکنند.
برای کسانی که تلهمتری هوش مصنوعی را مدیریت میکنند، درس روشن است: به فایلهای پیکربندی اعتماد نکنید. اگر از یک کلاینت کدنویسی هوش مصنوعی تلهمتری میفرستید، قبل از خواندن پیکربندی، یک رکورد خام را بخوانید. دادههای هویتی در این دسته از محصولات بهصورت پیشفرض ارسال میشوند و هر لیست عدممجازی (deny-list) که امروز مینویسید، صرفاً لیستی از فیلدهایی است که امروز صبح وجود داشتند.
اگر به دو سد دفاعی تکیه کردهاید، بپرسید کدام دسته از دادهها را فقط یکی از آنها واقعاً میبیند. هر کدام را عمداً بشکنید و دیگری را بررسی کنید. و اگر اثبات دارید، بپرسید سه اجرای سبز اخیر شما واقعاً روی چه چیزی اجرا شدهاند. مال من ایمیجی را میکشیدند که محیط تولید قبلاً رها کرده بود، و آنها این را به دوستانهترین شکل ممکن به من گفتند: با سبز شدن تستها.




گفتگو