SSD Nodes Learn 🎉 VPS از $5.50/ماه
راهنماها Matt Connorتوسط Matt Connor

حمله Prompt Injection به عامل‌های کدنویسی و روش مقابله

در این مقاله بررسی می‌کنیم که چگونه مهاجمان با تزریق دستور به فایل‌ها یا نتایج ابزارها، عامل‌های کدنویسی را فریب می‌دهند. مدل تهدید و راهکارهای عملی برای محدودسازی آسیب‌ها را بیاموزید.

تزریق دستور (Prompt Injection) علیه یک عامل کدنویسی چیست

تزریق دستور علیه یک عامل (agent) کدنویسی را می‌توان به سادگی تعریف کرد: متنی که عامل می‌خواند، به عنوان دستوری تلقی می‌شود که عامل از آن پیروی می‌کند. عامل فایلی را باز می‌کند، یک کامنت در pull request، یک صفحه وب یا نتیجهٔ فراخوانی یک ابزار را می‌خواند. تمام این موارد به همان شکلی به دست عامل می‌رسند که درخواست خود شما. اگر مهاجم هر بخشی از این متن را کنترل کند، در واقع در حال نوشتن در نشست (session) شماست.

تمام محصولات مبتنی بر عامل که امروزه وجود دارند، این ویژگی را دارا هستند. مدل یک توالی از توکن‌ها را دریافت می‌کند. درخواست شما، دستورات سیستمی (system prompt)، محتوای فایل‌ها و نتایج ابزارها همگی به هم متصل می‌شوند و مدل پیش‌بینی می‌کند که در ادامه چه چیزی باید بیاید. هیچ بیتِ سطح دسترسی (privilege bit) برای توکن‌ها وجود ندارد. در ساختار داده‌ها هیچ بخشی مشخص نمی‌کند که کدام قسمت را شما تأیید کرده‌اید و کدام قسمت از فایل README یک غریبه آمده است.

این صفحه مدل تهدید را شرح می‌دهد: اینکه متنِ تحت کنترل مهاجم چگونه به عاملی که روی سرور در حال اجراست می‌رسد، مهاجم در هر مرحله چه چیزی به دست می‌آورد و کدام دفاع‌ها ارزش صرف انرژی را دارند. توصیه‌های مربوط به محدودسازی (containment) در سایر راهنماهای ما، تنها زمانی معنا پیدا می‌کنند که بدانید عامل را از چه چیزی محدود می‌کنید.

چرا مدل نمی‌تواند محتوا را از دستورالعمل‌ها تفکیک کند

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

پروژه OWASP GenAI این موضوع را تحت عنوان LLM01:2025 Prompt Injection دنبال می‌کند و آن را به دو دسته تقسیم کرده است. تزریق مستقیم (Direct injection)، زمانی است که پرامپت خودِ کاربر رفتار مدل را تغییر می‌دهد. تزریق غیرمستقیم (Indirect injection)، زمانی است که محتوای خارجی، مانند یک وب‌سایت یا یک فایل، هنگام پردازش توسط مدل، رفتار آن را تغییر می‌دهد. تزریق غیرمستقیم همان موردی است که در سرور اهمیت دارد، زیرا یک عامل (agent) بسیار بیشتر از آنچه شما تایپ می‌کنید، متن می‌خواند.

اولین مطالعهٔ سیستماتیک در این زمینه توسط Greshake و همکارانش با عنوان Not what you've signed up for (2023) انجام شده است. نتیجه‌گیری آن‌ها جمله‌ای است که باید به خاطر سپرد: هنگامی که یک برنامه، متن بازیابی‌شده را به مدلی می‌دهد که قابلیت فراخوانی ابزارها (tools) را دارد، پردازش آن متن به اجرای کد دلخواه (arbitrary code execution) بسیار نزدیک است.

شرایطی که خواندن داده را به یک نفوذ امنیتی تبدیل می‌کند

خواندن متن مخرب به‌تنهایی آسیب‌زا نیست. آسیب زمانی رخ می‌دهد که راهی برای خروج داده از ماشین وجود داشته باشد.

Simon Willison در ژوئن 2025 این ترکیب را مثلث مرگبار نامید. عاملی (agent) که به داده‌های خصوصی دسترسی دارد، در معرض محتوای غیرقابل‌اعتماد قرار می‌گیرد و می‌تواند داده‌ها را به بیرون ارسال کند، ممکن است فریب بخورد تا داده‌های خصوصی را از طریق مسیر سوم به بیرون منتقل کند.

یک عامل کدنویسی روی VPS شما از همان روز اول تمام این شرایط را دارد. داده‌های خصوصی شامل سورس‌کد، فایل .env، کلیدهای SSH و تاریخچه shell شماست. محتوای غیرقابل‌اعتماد شامل تمام مخازن، صفحات و نتایج ابزارهایی است که عامل می‌خواند. راه خروج نیز git push، curl، npm publish، بدنه یک pull request یا لینکی است که در ترمینال شما چاپ شده و شما روی آن کلیک می‌کنید.

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

جایی که متن‌های غیرقابل‌اعتماد به یک عامل کدنویسی روی سرور می‌رسند

مخزنی که عامل در آن کار می‌کند

هر فایلی در checkout، ورودی محسوب می‌شود. کامنت‌های سورس، README.md، لاگ‌های تغییرات، فیکسچرهای تست، کدهای vendored و خودِ فایل‌های دستورالعمل عامل: CLAUDE.md، AGENTS.md و موارد مشابه. عاملی که از او خواسته شده یک codebase را درک کند، آن‌ها را می‌خواند، زیرا این همان چیزی است که شما درخواست کرده‌اید.

آنچه مهاجم در اینجا به دست می‌آورد، دسترسی به تمام کسانی است که مخزن را clone کرده و عاملی را به سمت آن هدایت می‌کنند. فایل‌های دستورالعمل مستقیم‌ترین مسیر هستند، زیرا ماهیت آن‌ها خوانده شدن به عنوان دستورالعمل است. یک pull request که چهار خط مفید به CLAUDE.md اضافه می‌کند و یک خط که عامل را منحرف می‌کند، تغییری است که یک بازبین انسانی به‌سادگی از کنار آن می‌گذرد.

ایشوها، pull requestها و کامنت‌های بازبینی کد

هر چیزی که یک غریبه بتواند در سیستم ردیابی شما تایپ کند، لحظه‌ای که از عامل بخواهید آن را دسته‌بندی (triage) کند، به دست او می‌رسد. در مه 2025، Invariant Labs یک یافته GitHub MCP با دقیقاً همین ساختار منتشر کرد. عامل یک توسعه‌دهنده به یک مخزن عمومی و چندین مخزن خصوصی دسترسی داشت. یک مهاجم در مخزن عمومی یک issue ثبت کرد. وقتی توسعه‌دهنده از عامل خواست ایشوهای باز را بررسی کند، عامل محتویات مخزن خصوصی را خواند و آن‌ها را در یک pull request در سمت عمومی نوشت.

آن گزارش بیش از آنکه یک نقص کد در سرور MCP باشد، یک مشکل معماری را توصیف می‌کند. عامل یک توکن دسترسی گسترده داشت، از یک صندوق ورودی عمومی می‌خواند و مجوز نوشتن داشت. هیچ‌چیز به معنای معمول کلمه بد پیکربندی نشده بود، و به همین دلیل است که پاسخ در محدودسازی (scoping) نهفته است، نه در وصله‌کردن (patching).

صفحات وبی که عامل واکشی می‌کند

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

مهاجم در لحظه‌ای که عامل کمترین نظارت را دارد، کنترل را به دست می‌گیرد. هیچ‌کس متن کامل صفحه‌ای را که عامل هنگام جستجوی چیزی واکشی کرده است، نمی‌خواند.

خروجی ابزار MCP

MCP (پروتکل زمینه مدل) روش رایجی است که عامل‌ها از طریق آن به ابزارهای خارجی متصل می‌شوند. نتایج به صورت متن بازمی‌گردند و مستقیماً وارد پنجره زمینه (context window) می‌شوند. در اینجا دو سطح وجود دارد، نه یکی. داده‌هایی که یک ابزار برمی‌گرداند، سطح آشکار است. نام و توضیحات خودِ ابزار که مدل برای تصمیم‌گیری در مورد زمان فراخوانی آن می‌خواند، سطح دیگر است و سروری که شما کنترل نمی‌کنید، می‌تواند هر کدام از این‌ها را بین فراخوانی‌ها تغییر دهد.

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

لاگ‌های CI، خروجی بیلد و متادیتای وابستگی‌ها

npm install متن‌هایی را از بسته‌هایی که شما ننوشته‌اید چاپ می‌کند. یک خطای تست، پیام assertion را از یک کتابخانه چاپ می‌کند. لاگ یک job در سیستم یکپارچه‌سازی مداوم (CI)، هزاران خط خروجی شخص ثالث است. از یک عامل بخواهید یک بیلد ناموفق را اصلاح کند و او تمام آن را می‌خواند.

در اینجا مهاجم ماشین بیلد را به دست می‌آورد که معمولاً حاوی اعتبارنامه‌های استقرار (deploy credentials) و توکن‌های رجیستری است و نسبت به یک لپ‌تاپ، توجه کمتری به آن می‌شود.

مهاجم واقعاً به چه چیزی دست می‌یابد

چهار پیامد ارزش برنامه‌ریزی دارند.

سرقت اعتبارنامه‌ها. هر چیزی که پردازش agent بتواند بخواند در محدوده دسترسی است: متغیرهای محیطی، ~/.aws/credentials، ~/.ssh، یک توکن gh، یا فایل پیکربندی Docker. ارسال این موارد به خارج از سرور نیازی به curl ندارد. یک commit به یک branch، توضیحات یک pull request، بسته‌ای که در یک registry منتشر می‌شود، یا یک DNS lookup برای نامی که تحت کنترل مهاجم است، همگی داده‌ها را از سرور خارج می‌کنند.

تغییرات کدی که شما تأیید می‌کنید. نوشتن کد کاری است که agent انجام می‌دهد، بنابراین وادار کردن آن به نوشتن یک خط کد که به‌طور نامحسوس اشتباه است، ارزان‌ترین نتیجه برای رسیدن به هدف است. یک dependency اضافه شده، یا یک فراخوانی logging که توکنی را به لاگی می‌برد که شما آن را به جای دیگری ارسال می‌کنید.

ماندگاری (Persistence). فایلی که یک بار نوشته شود بدون نیاز به هیچ مدلی به کار خود ادامه می‌دهد: یک hook در .git/hooks، یک اسکریپت postinstall در package.json، خطی که به فایل startup شل اضافه شده، یا یک خط اضافی در CLAUDE.md. دستور بعدی آن را اجرا می‌کند.

حرکت در شبکه شما. این agent در جایی اجرا می‌شود که شما آن را قرار داده‌اید. اگر آن سرور به یک دیتابیس روی loopback، یک سرویس مدیریتی داخلی، سرویس metadata ارائه‌دهنده ابری شما، یا میزبان دیگری در شبکه خصوصی دسترسی داشته باشد، هر چیزی که agent را هدایت کند نیز به آن دسترسی خواهد داشت.

حالت‌های تأیید خودکار، آخرین لایه نظارتی را حذف می‌کنند

در حالت پیش‌فرض، Claude Code پیش از اجرای هر دستور یا ویرایش فایل، از کاربر اجازه می‌گیرد. این پرسش، همان لایه نظارت انسانی است که میان تمامی سطوح عملیاتی و اجرای واقعی دستور قرار دارد. حالت‌هایی که این پرسش را حذف می‌کنند، در واقع لایه نظارتی را از بین می‌برند.

مستندات در مورد bypassPermissions صریح هستند: از آن فقط در محیط‌های ایزوله مانند containerها یا VMها استفاده کنید که در آن‌ها Claude Code امکان ایجاد خسارت ندارد. حالت auto ملایم‌تر است و فراخوانی ابزارها را با بررسی‌های امنیتی پس‌زمینه تأیید می‌کند تا اطمینان حاصل شود که اقدامات با درخواست شما همسو هستند. این بررسی‌ها بسیاری از موارد را شناسایی می‌کنند. با این حال، آن‌ها همچنان قضاوت مدل درباره خروجی خودِ مدل هستند؛ بنابراین به آن‌ها به چشم یک فیلتر نگاه کنید، نه یک مرز امنیتی قطعی.

یک مدیر سیستم می‌تواند هر دو حالت را حذف کند. کافی است permissions.disableBypassPermissionsMode یا permissions.disableAutoMode را در یک فایل تنظیمات روی "disable" قرار دهید و آن فایل را در بخش تنظیمات مدیریت‌شده (managed settings) بگذارید تا پروژه‌های checkout شده نتوانند آن را بازنویسی کنند. راهنمای ما در قوانین حالت خودکار و دسترسی‌های Claude Code توضیح می‌دهد که هر قانون در کجا اعمال می‌شود.

دفاع‌ها، رتبه‌بندی‌شده بر اساس دستاوردهای آن‌ها

هیچ‌کدام از این موارد یک راه‌حل قطعی نیستند. هر یک از آن‌ها یا دامنه دسترسی عامل (agent) را محدود می‌کنند و یا توانایی‌های آن را کاهش می‌دهند.

  1. ماشینی که بتوانید آن را نابود و دوباره بازسازی کنید، تا هزینه یک نفوذ به جای یک حادثه امنیتی، تنها یک ساعت زمان باشد.
  2. اعتبارنامه‌هایی که از اعتبارنامه‌های شخصی شما جدا، محدود به یک مخزن (repository) و دارای عمر کوتاه باشند.
  3. عدم وجود اسرار (secrets) با عمر طولانی در محیطی که دستورات عامل از آن ارث‌بری می‌کنند.
  4. اعمال محدودیت‌های سیستم‌عامل بر خروجی شبکه و دسترسی به فایل‌ها، که برای هر فرآیندی که عامل آغاز می‌کند، اعمال شود.
  5. فعال نگه داشتن درخواست‌های تأیید برای عملیات نوشتن و فراخوانی‌های شبکه.
  6. استفاده از هوک‌ها (hooks) به عنوان یک سد قطعی برای اقدامات خاصی که می‌توانید نام ببرید.
  7. خواندن تغییرات (diff) پیش از ادغام (merge) آن‌ها.

ترتیب اهمیت دارد. موارد 1 تا 4 حتی زمانی که مدل کاملاً تحت کنترل مهاجم باشد، برقرار هستند. موارد 5 تا 7 به توجه انسان وابسته است، که دقیقاً همان چیزی است که در اجرای طولانی‌مدت عامل، متوقف می‌شود.

عامل را روی ماشینی قرار دهید که قابل دور انداختن باشد

یک VPS که حاوی یک checkout و یک توکن محدود است، هدف بسیار کوچک‌تری نسبت به لپ‌تاپی است که کلیدهای شما روی آن قرار دارد. عامل را به عنوان کاربر بدون دسترسی ویژه (unprivileged) خود اجرا کنید، نه به عنوان حساب کاربری ورود به سیستم و نه به عنوان root. راهنماهای ما در یک ماشین مجازی یک‌بارمصرف برای عامل‌های برنامه‌نویسی و کاربران با حداقل دسترسی روی VPS این تنظیمات را پوشش می‌دهند و اجرای ایمن Claude Code روی VPS شکل روزمره آن را بررسی می‌کند.

اسرار را از محیط خارج کنید

یک متغیر محیطی توسط هر فرآیند فرزند قابل خواندن است، که یعنی هر دستوری که عامل اجرا می‌کند به آن دسترسی دارد. sandbox برنامه Claude Code می‌تواند متغیرهای نام‌گذاری‌شده را پیش از هر دستور sandboxed حذف (unset) کند. در لینوکس، sandbox ابتدا به دو بسته نیاز دارد:

sudo apt-get install bubblewrap socat

سپس در ~/.claude/settings.json:

{
  "sandbox": {
    "enabled": true,
    "network": {
      "allowedDomains": ["github.com", "*.npmjs.org"]
    },
    "credentials": {
      "envVars": [
        { "name": "GITHUB_TOKEN", "mode": "deny" },
        { "name": "NPM_TOKEN", "mode": "deny" }
      ]
    }
  }
}

یک ورودی deny آن متغیر را پیش از اجرای هر دستور sandboxed حذف می‌کند و allowedDomains دستورات sandboxed را به میزبان‌هایی که لیست می‌کنید محدود می‌سازد. بلوک credentials به Claude Code نسخه v2.1.187 یا بالاتر نیاز دارد که در اوت 2026 بررسی شده است. دستور /sandbox را در یک نشست اجرا کنید تا ببینید کدام لایه‌ها فعال هستند و کدام وابستگی‌ها مفقود شده‌اند. تصمیم‌گیری در مورد اینکه اصلاً کدام اسرار باید روی آن ماشین وجود داشته باشند، بخش بزرگ‌تر کار است و دور نگه داشتن اسرار از دسترس عامل هوش مصنوعی به این موضوع می‌پردازد.

خروجی شبکه را در سطح سیستم‌عامل قطع کنید

یک قانون فایروال به تصمیمات مدل اهمیتی نمی‌دهد. عامل را به عنوان یک کاربر اختصاصی agent اجرا کنید، سپس آنچه آن کاربر ارسال می‌کند را مسدود (drop) کنید:

table inet agentcage {
  chain output {
    type filter hook output priority filter; policy accept;
    meta skuid "agent" ct state established,related accept
    meta skuid "agent" oif lo accept
    meta skuid "agent" counter drop
  }
}

این کار کاربر agent را فقط به loopback محدود می‌کند، بنابراین ترافیک آن باید از طریق پروکسی که روی همان ماشین اجرا می‌کنید عبور کند و پروکسی لیست مجاز نام‌های میزبان را نگه می‌دارد. با تنظیم https_proxy به سمت آن پروکسی، کلاینت یک درخواست CONNECT ارسال می‌کند و پروکسی جستجوی نام را انجام می‌دهد، بنابراین عامل نیازی به DNS (سیستم نام دامنه) خروجی برای خود ندارد. کار خود را با sudo nft list ruleset بررسی کنید و افزایش شمارنده قانون drop را در حالی که عامل سعی می‌کند به چیز جدیدی دسترسی پیدا کند، مشاهده کنید.

هنگام اعمال تغییرات فایروال، یک نشست SSH دوم باز نگه دارید. بررسی کنید که runtime کانتینر شما با این قوانین چه می‌کند: Docker زنجیره‌های خاص خود را می‌نویسد و پورت‌های منتشر شده Docker از ufw عبور می‌کنند، غافلگیری ناشی از این موضوع را شرح می‌دهد.

هوک‌ها: بررسی‌هایی که مدل نمی‌تواند از آن‌ها عبور کند

قوانین دسترسی و هوک‌ها توسط Claude Code اعمال می‌شوند، نه توسط مدل. مستندات به صراحت بیان می‌کنند: دستورالعمل‌های موجود در prompt یا CLAUDE.md شما، تلاش‌های Claude را شکل می‌دهند و آنچه Claude Code اجازه می‌دهد را تغییر نمی‌دهند. این تمایز، تمام ارزش کار است. یک خط در CLAUDE.md که می‌گوید "هرگز curl را اجرا نکن"، پیشنهادی است که یک پاراگراف تزریق‌شده می‌تواند با آن مخالفت کند. یک هوک فرآیندی است که یک کد خروج (exit code) برمی‌گرداند.

یک هوک PreToolUse را در .claude/settings.json ثبت کنید:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/no-egress.sh"
          }
        ]
      }
    ]
  }
}

هوک، فراخوانی ابزار را به صورت JSON در ورودی استاندارد دریافت می‌کند. کد خروج 2 فراخوانی را مسدود کرده و دلیل آن را از خروجی خطای استاندارد به Claude نشان می‌دهد. کد خروج 0 اجازه می‌دهد فراخوانی از جریان عادی مجوزها عبور کند.

#!/usr/bin/env bash
# PreToolUse: stdin holds the tool call, exit 2 blocks it.
cmd=$(jq -r '.tool_input.command // ""')
if printf '%s' "$cmd" | grep -qE '(^|[;&|]|\s)(curl|wget|nc|ncat)(\s|$)'; then
  echo "Blocked: this repository does not allow outbound network commands." >&2
  exit 2
fi
exit 0

حالا بخش صادقانه ماجرا: این یک لیست سیاه (denylist) روی یک رشته shell است و لیست‌های سیاه روی رشته‌های shell نشت می‌کنند. python3 -c یک سوکت را بدون استفاده از کلمه curl باز می‌کند. یک هدف make deploy همان فراخوانی را یک سطح پایین‌تر پنهان می‌کند. برای اشتباهاتی که می‌توانید نام ببرید هوک بنویسید و مرزی که واقعاً به آن تکیه می‌کنید را در هسته (kernel) یا شبکه قرار دهید.

قوانین منع دسترسی (deny rules) دارای یک محدودیت تطبیق هستند که دانستن آن ارزشمند است. قوانین منع Read و Edit ابزارهای فایل خود Claude و دستورات فایلی که در Bash تشخیص می‌دهد، مانند cat، head، tail و sed را پوشش می‌دهند. آن‌ها یک اسکریپت Python یا Node که خود فایل را باز می‌کند، پوشش نمی‌دهند. قوانین ابتدا به صورت منع، سپس پرسش و در نهایت اجازه ارزیابی می‌شوند، بنابراین یک قانون منع نمی‌تواند استثنای لیست مجاز (allowlist) داشته باشد.

{
  "permissions": {
    "deny": [
      "Read(.env)",
      "Read(./secrets/**)",
      "Bash(git push *)"
    ]
  }
}

آنچه خارج می‌شود را زیر نظر بگیرید و تغییرات را بخوانید

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

چه مواردی هنوز حل‌نشده باقی مانده‌اند

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

فیلترها کمک می‌کنند، اما شکست هم می‌خورند. یک طبقه‌بندی‌کننده (classifier) که اکثر تلاش‌های تزریق (injection) را شناسایی می‌کند، باید همیشه درست عمل کند، در حالی که مهاجم تنها کافی است یک‌بار موفق شود. این عدم تقارن باعث می‌شود نرخ موفقیتِ اعلام‌شده برای یک سیستم دفاعی، به‌جای آنکه یک تضمین باشد، صرفاً نقطهٔ شروعی برای تلاش بعدی مهاجم باشد.

امیدوارکننده‌ترین اقدامات در سطح طراحی انجام می‌شوند، نه در سطح مدل. رویکرد CaMeL که در مقالهٔ شکست دادن تزریق پرامپت از طریق طراحی (Debenedetti و همکاران، 2025) معرفی شده، ابتدا جریان کنترل و جریان داده را از درخواستِ مورد اعتماد استخراج می‌کند تا داده‌های غیرقابل‌اعتماد نتوانند عملکرد برنامه را تغییر دهند، و سپس هنگام فراخوانی ابزارها، بررسی‌های دسترسی (capability checks) را اعمال می‌کند. ارقام ارائه‌شده در خودِ مقاله بر اساس بنچمارک AgentDojo نشان می‌دهد که این کار چه هزینه‌ای دارد.

ChartAgentDojo tasks solved, published figures from the CaMeL paper (2025)
The data behind this chart
[
  {
    "label": "Undefended agent",
    "tasks_solved_pct": 84
  },
  {
    "label": "CaMeL",
    "tasks_solved_pct": 77
  }
]

ایجنتی که هیچ دفاعی ندارد، 84 درصد از وظایف را حل کرد. CaMeL با داشتن یک تضمین امنیتی، 77 درصد از وظایف را حل کرد. این‌ها ارقام منتشرشده در مقاله برای یک بنچمارک خاص هستند و معیاری برای بار کاری شما محسوب نمی‌شوند. شکاف میان این دو عدد، تقریباً همان هزینه‌ای است که امروزه برای یک تضمین واقعی باید پرداخت.

تا زمانی که طراحی‌هایی از این دست در ابزارهایی که روزانه استفاده می‌کنید عرضه نشوند، فرض را بر این بگیرید که ایجنت شما در مقطعی دچار نفوذ (compromise) خواهد شد و سعی کنید این رویداد را به یک اتفاق عادی و بی‌اهمیت تبدیل کنید. این تمامِ استدلال برای استفاده از ماشین‌های یک‌بارمصرف (disposable)، اعتبارنامه‌های محدود (scoped credentials)، خروجی‌های کنترل‌شده و عادت به بررسی تغییرات (diff) است.

FAQ

آیا می‌توانم با دستور دادن به عامل برای نادیده گرفتن دستورات موجود در فایل‌ها، جلوی prompt injection را بگیرم؟

خیر. آن جمله متنی است که در همان context window حمله قرار دارد و با متن مهاجم در شرایطی برابر رقابت می‌کند. مستندات Claude Code مرز را به‌وضوح مشخص کرده است: دستورات موجود در prompt یا CLAUDE.md شما، تعیین‌کنندهٔ تلاش‌های عامل هستند، اما آنچه ابزار مجاز می‌داند را تغییر نمی‌دهند. یک فایل دستورالعمل را به‌عنوان بیانی از قصد و نیت در نظر بگیرید و هر چیزی که به آن متکی هستید را در قوانین دسترسی، یک hook در PreToolUse یا یک قانون فایروال قرار دهید.

آیا اگر عامل فقط به مخزن (repository) خودم دسترسی داشته باشد، prompt injection یک ریسک واقعی است؟

بله، زیرا مخزن شما پر از متونی است که خودتان ننوشته‌اید. فایل‌های README وابستگی‌ها، URLهای موجود در lockfile، داده‌های تست (test fixtures)، کدهای vendored و خروجی‌های npm install همگی در طول یک وظیفهٔ عادی وارد سیستم می‌شوند. هر چیزی که از یک issue tracker یا سایت مستندات فراخوانی شود نیز به همین ترتیب وارد می‌شود. ریسک با میزان خواندن عامل افزایش می‌یابد و یک عامل مفید، حجم زیادی از اطلاعات را می‌خواند.

آیا اجرای عامل در یک container این مشکل را حل می‌کند؟

این کار آسیب را محدود می‌کند، آن هم تنها در صورتی که اعتبارنامه‌ها (credentials) را نیز از آن بگیرید. یک container که SSH agent شما در آن forward شده، اعتبارنامه‌های ابری در محیط آن قرار دارد و دسترسی شبکهٔ نامحدود دارد، تقریباً تمام آنچه میزبان در اختیار دارد را به مهاجم می‌دهد. آنچه container واقعاً برای شما فراهم می‌کند، یک سیستم فایل قابل حذف و محیطی تمیز برای اعمال قوانین خروجی (egress) است. آن را با یک توکن که محدود به یک مخزن خاص است، ترکیب کنید.

کدام تغییر واحد، ریسک را بیش از همه کاهش می‌دهد؟

اعتبارنامه‌های با عمر طولانی را از محیطی که دستورات عامل از آن ارث‌بری می‌کنند حذف کنید و سپس برای آن ماشین یک سیاست خروجی (egress) با پیش‌فرضِ «رد کردن همه» (default-deny) در نظر بگیرید. این دو اقدام با هم، شرط سوم در مثلث مرگبار را می‌شکنند: متن همچنان می‌تواند عامل را برباید، اما داده‌هایی که به آن‌ها دسترسی پیدا می‌کند، راه خروجی مفیدی ندارند. درخواست‌های تأیید و بازبینی diff نیز کمک می‌کنند، اما آن‌ها به هوشیاری انسان در طول یک اجرای طولانی وابسته هستند؛ به همین دلیل است که اهمیت آن‌ها پایین‌تر از آن دو تغییر قرار می‌گیرد.