Twój asystent oparty na AI stosuje się do instrukcji w około 99% przypadków, ale to brakujące 1% jest miejscem, w które uderzają atakujący. Poprzez podanie sformułowanego w określony sposób promptu, złośliwy użytkownik może zmusić model do wywołania funkcji, których nie powinien uruchamiać, kradnąc dane lub wykonując uprzywilejowane działania. Rozwiązaniem nie jest bardziej uprzejmy język – lecz potraktowanie tej wady jako problemu z autoryzacją i usunięcie niebezpiecznych narzędzi z zasięgu modelu.
Dlaczego prompt injection to nie tylko problem doboru słów
Deweloperzy często próbują wzmocnić agentów za pomocą ostrzeżeń pisanych wielkimi literami, numerowanych reguł lub klauzul typu „nie wywołuj funkcji admina”. Takie zabezpieczenia zakładają, że model posłucha zdania brzmiącego „nie rób X”. W praktyce model można skłonić do zignorowania instrukcji poprzez zmianę sformułowania prośby, odgrywanie innej roli lub po prostu dodanie dodatkowego kontekstu. Granica językowa jest negocjowalna; prompt atakującego jest nieograniczony i nie kosztuje go nic, by go przetestować.
Prawdziwa podatność tkwi w liście narzędzi, którą otrzymuje agent. Gdy schemat promptu zawiera funkcję nadającą uprawnienia administratora, model otrzymuje mapę do tej władzy. Nawet jeśli prompt mówi „nie używaj jej dla klientów”, model wciąż może zostać przekonany do jej wywołania, ponieważ funkcja istnieje w jego środowisku wykonawczym. Problem polega zatem na luce w autoryzacji: system udostępnia uprzywilejowane możliwości wywołującemu, który nie ma do nich prawa.
Zabezpieczanie agentów poprzez ograniczanie ekspozycji
Najprostszym sposobem na zamknięcie tej luki jest zaprzestanie dawania modelowi dostępu do narzędzi, których nie jest uprawniony używać. Potraktuj listę narzędzi jak klucz API: jeśli klucza nie ma, wywołanie nie może nastąpić. Żadne sprytne sformułowanie nie przywoła funkcji, której nie ma w bieżącym kontekście.
Błędny sposób
Prompt: “You are an assistant. Do not use the adminDeleteUser function for regular customers.”
Model wciąż widzi adminDeleteUser w swoim zestawie narzędzi i można go oszukać, by go wywołał.
Właściwy sposób
Prompt schema for a regular customer: { “functions”: [ “searchCatalog”, “placeOrder” ] }
adminDeleteUser nigdy się nie pojawia, więc model nie ma ścieżki, aby go wywołać.
Trzy praktyczne zasady dla deweloperów
- Twórz listy narzędzi na żądanie – Generuj katalog funkcji dynamicznie, w oparciu o uprawnienia uwierzytelnionego wywołującego. Klient widzi tylko te funkcje, których potrzebuje; administrator widzi pełny zestaw.
- Stosuj zasadę „fail closed” – Jeśli tożsamość użytkownika nie może zostać zweryfikowana, zwróć pustą listę zamiast ogólnego rozwiązania typu „wszystkie narzędzia dostępne”. Gwarantuje to, że nieuwierzytelnione żądanie nigdy nie zyska nieoczekiwanej mocy.
- Unikaj współdzielonego stanu – Podczas buforowania definicji narzędzi nigdy nie zapisuj danych specyficznych dla użytkownika w obiekcie współdzielonym. Używaj mechanizmu copy-on-write lub kopii na sesję, aby uprawnienia jednego użytkownika nie przeniknęły do żądania innego.
Jeśli schemat przedstawiony zwykłemu użytkownikowi wygląda identycznie jak ten pokazany administratorowi, granicą bezpieczeństwa pozostaje tekst promptu, a prompty nie są niezawodnym mechanizmem bezpieczeństwa.
Co nas do tego doprowadziło
Prompt injection pojawił się, gdy deweloperzy zaczęli integrować duże modele językowe (LLM) z procesami produkcyjnymi, które wymagały od modelu wywoływania zewnętrznych API, uruchamiania kodu lub modyfikowania baz danych. „Rozumowanie” modelu jest kierowane przez prompt, który zawiera również listę dostępnych narzędzi. Wczesne prototypy zakładały, że model będzie przestrzegał reguły w języku naturalnym, takiej jak „nie usuwaj rekordów dla osób niebędących administratorami”. Atakujący szybko udowodnili, że kilka dodatkowych zdań może ominąć te reguły, zmuszając model do wywołania tej samej funkcji usuwania.
Pierwszą reakcją społeczności było uszczelnienie języka promptów, dodawanie klauzul „nigdy nie rób X” lub wdrażanie filtrów regex, które usuwają podejrzane tokeny. Środki te zmniejszyły przypadkowe nadużycia, ale nie powstrzymały zdeterminowanego przeciwnika, który mógł po prostu zmienić sformułowanie prośby. Podstawowa przyczyna – udostępnianie uprzywilejowanych funkcji nieautoryzowanemu wywołującemu – pozostała bez zmian.
Kto wygrywa, a kto przegrywa
Przedsiębiorstwa, które wdrażają ograniczanie zakresu narzędzi na żądanie, zyskują jasną, egzekwowalną granicę. Ich agenci mogą być wdrażani na dużą skalę bez obawy, że pojedynczy błędnie sformułowany prompt odblokuje uprawnienia administratora. Zespoły ds. zgodności (compliance) również doceniają ścieżkę audytu: lista funkcji wysłanych do modelu jest konkretnym artefaktem, który można logować i przeglądać.
Deweloperzy polegający wyłącznie na zabezpieczeniach w promptach wciąż mierzą się ze zmiennym celem. Ich agenci mogą wydawać się funkcjonalni podczas testów, ale mogą zostać przejęci w rzeczywistym środowisku, co prowadzi do wycieków danych, nieautoryzowanych transakcji lub naruszeń zgodności. Koszt naruszenia znacznie przewyższa wysiłek włożony w budowę dynamicznej listy narzędzi.
Kontrargument: „Lepsze prompty wystarczą”
Niektórzy twierdzą, że dzięki odpowiedniej inżynierii instrukcji — warstwowym promptom, wiadomościom systemowym i uczeniu ze wzmocnieniem na podstawie informacji zwrotnej od ludzi (RLHF) — model można zmusić do przestrzegania klauzul „nie rób”. Rzeczywistość jest jednak taka, że modele językowe to generatory probabilistyczne; ważą one najbardziej prawdopodobne kontynuacje, a nie sztywne reguły bezpieczeństwa. Nawet przy precyzyjnie dostrojonych barierach ochronnych (guardrails), nowatorskie sformułowanie może się prześlizgnąć, zwłaszcza gdy atakujący może iterować w nieskończoność bez żadnych kosztów. Bariery ochronne są przydatne do redukcji szumu, ale nie powinny stanowić jedynej linii obrony.
Na co warto zwrócić uwagę w następnej kolejności
- Frameworki udostępniające scoping narzędzi jako API pierwszej klasy – Należy spodziewać się nowych bibliotek, które pozwolą deklarować możliwości dla poszczególnych użytkowników i automatycznie ograniczać listę funkcji przed zbudowaniem promptu.
- Ustandaryzowane „manifesty funkcji” – Grupy branżowe mogą zdefiniować schemat JSON, który oddziela funkcje publiczne od uprzywilejowanych, co ułatwi generowanie manifestów specyficznych dla danego żądania.
- Wymuszanie w czasie wykonywania (Runtime enforcement) – Niektóre platformy eksperymentują z uruchamianiem w piaskownicy (sandboxed execution), które sprawdza token wywołującego względem wywoływanej funkcji, dodając drugą warstwę ochrony poza scopingiem promptu.
Wniosek jest jasny: traktuj prompt injection jako błąd autoryzacji. Usuwając nieautoryzowane narzędzia z zestawu narzędzi modelu, eliminujesz powierzchnię ataku, którą próbuje wykorzystać sprytnie sformułowany prompt. Prompty mogą kierować zachowaniem, ale nie mogą zastąpić właściwej kontroli dostępu.
