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

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

چرا تزریق پرامپت اتفاق می‌افتد؟#

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

در برنامه‌نویسی معمولی این مرز وجود دارد: کوئری آماده (prepared statement) به دیتابیس می‌گوید کدام بخش دستور است و کدام مقدار. برای همین تزریق پرامپت را نمی‌شود مثل تزریق SQL با یک تابع escape «درست» کرد. می‌شود احتمالش را کم کرد، ولی باید سیستم را با این فرض ساخت که گاهی موفق می‌شود.

دو راه ورود: مستقیم و غیرمستقیم#

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

نوع غیرمستقیم خطرناک‌تر است، چون کسی که آسیب می‌بیند کاربری است که هیچ کار مشکوکی نکرده.

نمودار دو راه ورود تزریق پرامپت به چت‌بات و دو دروازه‌ی دفاعی در کد سرور

مهاجم از چت‌بات سایت چه می‌خواهد؟#

معمولاً یکی از سه چیز: اطلاعاتی که نباید ببیند، وعده‌ای که نباید داده شود، یا کاری که نباید انجام شود.

هدف مهاجمنمونه در یک سایت فروشگاهیمیزان خسارت
فاش‌کردن پرامپت سیستمیکپی‌کردن دستورها و قواعد داخلی شماکم تا متوسط
گرفتن وعده‌ی جعلی«ربات خودتان گفت ارسال رایگان است»متوسط
دیدن داده‌ی کاربر دیگرپرسیدن وضعیت سفارشی که مال او نیستبالا
صدا زدن ابزار حساسساختن کد تخفیف یا فرستادن ایمیلبالا
نشت داده از راه لینکلینکی حاوی اطلاعات گفتگو به دامنه‌ی دیگربالا
هزینه‌تراشیهزاران پیام بلند برای بالا بردن هزینه‌ی APIمتوسط

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

چرا فیلتر کلمه و پرامپت سیستمی کافی نیستند؟#

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

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

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

شش لایه‌ی دفاع: چطور خسارت را محدود کنیم؟#

هدف این است که حتی اگر مدل فریب خورد، کد بیرون از مدل جلوی خسارت را بگیرد.

۱. رازی در پرامپت نگذارید#

کلید API، رمز یا کد تخفیف مخصوص همکاران در پرامپت سیستمی جایی ندارد. فرض کنید پرامپت روزی عیناً منتشر می‌شود.

۲. دسترسی را در کد بررسی کنید، نه در مدل#

مدل نباید تصمیم بگیرد کاربر به کدام سفارش دسترسی دارد. شناسه‌ی کاربر را از نشست وردپرس بگیرید، نه از چیزی که مدل فرستاده:

PHP
function di9_tool_order_status( int $order_id ): array {
    $user_id = get_current_user_id();
    $order   = wc_get_order( $order_id );

    if ( ! $user_id || ! $order || $order->get_customer_id() !== $user_id ) {
        return [ 'error' => 'not_found' ];
    }

    return [ 'status' => wc_get_order_status_name( $order->get_status() ) ];
}

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

۳. ابزار حساس را از چت‌بات عمومی دور نگه دارید#

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

۴. خروجی مدل را مثل ورودی کاربر پاک‌سازی کنید#

پاسخ مدل را هرگز خام در HTML نگذارید، تصویر Markdown را نمایش ندهید و لینک را فقط به دامنه‌های خودتان مجاز کنید:

PHP
function di9_clean_bot_reply( string $reply ): string {
    $allowed_hosts = [ 'example.com', 'www.example.com' ];

    // تصویر Markdown هرگز نمایش داده نمی‌شود
    $reply = preg_replace( '~!\[[^\]]*\]\([^)]*\)~u', '', $reply ) ?? '';

    // لینک فقط به دامنه‌های مجاز
    $reply = preg_replace_callback(
        '~https?://[^\s)\]]+~iu',
        static function ( array $m ) use ( $allowed_hosts ): string {
            $host = strtolower( (string) wp_parse_url( $m[0], PHP_URL_HOST ) );
            return in_array( $host, $allowed_hosts, true ) ? $m[0] : '[لینک حذف شد]';
        },
        $reply
    ) ?? '';

    return esc_html( $reply );
}

در مرورگر هم پاسخ را با textContent در صفحه بگذارید، نه با innerHTML.

۵. محتوای خوانده‌شده را برچسب بزنید#

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

۶. سقف بگذارید#

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

چطور حمله را تشخیص دهیم و خودمان آزمایش کنیم؟#

با لاگ و با آزمایش پیش از انتشار. پیام‌ها، فراخوانی ابزارها و پاسخ‌ها را بدون داده‌ی حساس نگه دارید و هر هفته دنبال این نشانه‌ها بگردید:

  • پرسش درباره‌ی «دستورها»، «پرامپت» یا «نقش» خود چت‌بات
  • پاسخ‌هایی که لینکشان حذف شده یا به دامنه‌ی ناشناس اشاره می‌کنند
  • فراخوانی ابزار که پشت‌سرهم با ورودی نامعتبر رد شده
  • پاسخی که تخفیف، قیمت یا تعهدی خارج از منابع شما دارد

قبل از هر تغییر مهم، روی نسخه‌ی آزمایشی سایت این سناریوها را امتحان کنید:

  1. از چت‌بات بخواهید دستورهای داخلی‌اش را بگوید.
  2. وانمود کنید مدیر سایت هستید و دسترسی بیشتری بخواهید.
  3. وضعیت سفارشی را بپرسید که مال حساب آزمایشی شما نیست.
  4. در توضیح یک محصول آزمایشی جمله‌ای دستوری بگذارید و رفتار چت‌بات را ببینید.

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

این موضوع به کجا می‌رود#

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

برای آمادگی:

  • فهرستی از همه‌ی ابزارها و داده‌هایی که چت‌بات به آن‌ها دسترسی دارد نگه دارید.
  • پیش از اضافه‌کردن هر ابزار تازه بپرسید: «اگر مدل فریب بخورد، بدترین کار با این ابزار چیست؟»
  • آزمون سناریوهای حمله را بخشی ثابت از انتشار هر نسخه کنید.

خلاصه#

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