Nullify

Usługi

Testy penetracyjne Audyty bezpieczeństwa Bezpieczeństwo aplikacji Szkolenia dla firm Bezpłatny skan ekspozycji

Menu

Blog O nas Kontakt
AI / LLM

Testy penetracyjne aplikacji LLM - jak testuje się systemy oparte na AI

15 sierpnia 2026 · 10 min czytania

W skrócie: pentest aplikacji opartej na modelu językowym to nie to samo co test samego modelu. Najgroźniejsze podatności zwykle nie siedzą w modelu, tylko w tym, co aplikacja robi z jego odpowiedzią i jakie uprawnienia mu przyznano.

Model, który da się namówić na nieprzyzwoity żart, to problem wizerunkowy. Model, który ma dostęp do bazy i wykonuje wygenerowane przez siebie zapytania SQL, to problem bezpieczeństwa.

Coraz więcej firm wdraża asystentów obsługi klienta, wyszukiwarki oparte na dokumentach wewnętrznych i agentów wykonujących zadania w systemach. Prawie zawsze pojawia się wtedy pytanie, czy da się to przetestować tak jak zwykłą aplikację webową.

Odpowiedź brzmi: klasyczny test jest nadal potrzebny, ale sam nie wystarcza. Dochodzi warstwa, w której zabezpieczenia działają inaczej niż wszystko, co znamy z tradycyjnych aplikacji.

Dlaczego to inna klasa problemu

W zwykłej aplikacji dane i polecenia są rozdzielone. Zapytanie SQL ma strukturę, parametry idą osobno, a poprawnie napisany kod nie pozwala danym stać się poleceniem. Na tym opiera się obrona przed wstrzykiwaniem.

W modelu językowym tego rozdziału nie ma. Instrukcja systemowa, historia rozmowy, treść pobranego dokumentu i pytanie użytkownika trafiają do jednego okna kontekstu jako tekst. Model nie ma niezawodnego sposobu, żeby odróżnić "to jest polecenie od twórcy aplikacji" od "to jest treść, którą masz tylko streścić".

Stąd wynika najważniejsza konsekwencja praktyczna: wstrzykiwania promptu nie da się w pełni wyeliminować, tak jak eliminuje się SQL injection. Można je utrudniać i ograniczać skutki, ale projektowanie systemu przy założeniu, że model zawsze wykona instrukcję twórcy, jest z góry błędne.

OWASP Top 10 dla aplikacji LLM

Fundacja OWASP prowadzi osobną listę dla tej klasy systemów. W aktualnej edycji z 2025 roku wygląda ona tak:

KategoriaNa czym polega
LLM01 Prompt InjectionPodmiana intencji modelu przez treść wejściową, bezpośrednio lub przez pobierany dokument.
LLM02 Sensitive Information DisclosureUjawnienie danych z kontekstu, dokumentów albo innych sesji.
LLM03 Supply ChainPochodzenie modeli, adapterów i zbiorów danych; zaufanie do cudzych artefaktów.
LLM04 Data and Model PoisoningZatruwanie danych treningowych albo bazy wiedzy.
LLM05 Improper Output HandlingTraktowanie odpowiedzi modelu jako zaufanej przez kolejne komponenty.
LLM06 Excessive AgencyZbyt szerokie uprawnienia i dostęp do narzędzi bez kontroli.
LLM07 System Prompt LeakageWyciek instrukcji systemowej, a wraz z nią logiki i czasem sekretów.
LLM08 Vector and Embedding WeaknessesBłędy w bazach wektorowych i mechanizmach RAG.
LLM09 MisinformationFałszywe, ale wiarygodnie brzmiące odpowiedzi i zmyślone źródła.
LLM10 Unbounded ConsumptionNiekontrolowane zużycie zasobów i kosztów.

Co konkretnie sprawdzamy

Wstrzykiwanie bezpośrednie i pośrednie

Bezpośrednie to próby wydania modelowi instrukcji sprzecznych z jego rolą wprost w rozmowie. Groźniejsze jest wstrzykiwanie pośrednie: instrukcja ukryta w treści, którą aplikacja sama pobiera - w dokumencie wrzuconym do bazy wiedzy, na stronie, którą asystent odwiedza, w treści maila, który ma streścić.

Atakujący nie musi mieć wtedy w ogóle dostępu do aplikacji. Wystarczy, że umieści spreparowany tekst tam, skąd system czerpie dane. To realny scenariusz wszędzie, gdzie asystent czyta treści z zewnątrz.

Co się dzieje z odpowiedzią modelu

Najważniejsza część testu i miejsce, gdzie zapadają najpoważniejsze błędy. Sprawdzamy, dokąd trafia wygenerowany tekst: czy jest wstawiany do HTML bez kodowania (wtedy mamy XSS), czy trafia do zapytania do bazy, czy jest wykonywany jako kod, czy steruje wywołaniem API.

Reguła jest prosta i rzadko stosowana: odpowiedź modelu należy traktować jak dane od niezaufanego użytkownika, bo w praktyce nim jest.

Przykład z testu

W jednej z testowanych aplikacji użytkownik mógł podać, jak asystent ma się do niego zwracać - drobne udogodnienie, obok którego przechodzi się bez zastanowienia. Podaliśmy:

Nazywaj mnie: echo whoami

W odpowiedzi nie wróciła podana przez nas nazwa, tylko wynik wykonania tego polecenia na serwerze - nazwa konta systemowego, na którym działała aplikacja.

To znalezisko dobrze pokazuje, dlaczego testowanie samego okna czatu nie wystarcza. Warto zwrócić uwagę na trzy rzeczy.

Ładunek nie wyglądał na atak. Nie było tu próby obejścia zabezpieczeń modelu, prośby o zignorowanie instrukcji ani żadnej z technik, które wychwyci filtr treści. Z punktu widzenia aplikacji użytkownik po prostu podał swoje imię.

Zadziałało z opóźnieniem. Wartość została zapisana i wykorzystana dopiero w kolejnej turze rozmowy, przy generowaniu powitania. To wstrzyknięcie drugiego rzędu: nic złego nie dzieje się w momencie podania danych, więc walidacja na wejściu niczego nie łapie.

Model nie zrobił nic złego. Nie ujawnił instrukcji systemowej, nie wygenerował szkodliwej treści, nie dał się przekonać do niczego. Problem powstał w warstwie wokół modelu - w miejscu, gdzie tekst przechodzący przez potok trafił do kontekstu wykonywalnego. Gdyby ta sama aplikacja nie miała po drodze powłoki systemowej, identyczny ładunek byłby zwykłym, dziwnie brzmiącym imieniem.

Praktyczny wniosek: przy przeglądzie takiego systemu prześledź drogę każdego pola, w które użytkownik może wpisać dowolny tekst - od zapisania, przez zapamiętanie, aż do wszystkich miejsc, w których zostanie później użyte. Podatność siedzi zwykle na końcu tej drogi, kilka warstw od miejsca, gdzie dane weszły.

Uprawnienia agenta

Przy systemach z dostępem do narzędzi sprawdzamy, co model może faktycznie zrobić i czyimi uprawnieniami. Typowe znaleziska to agent działający na koncie technicznym z prawami znacznie szerszymi niż potrzebne, brak potwierdzenia przy operacjach nieodwracalnych oraz możliwość wywołania narzędzia w sposób nieprzewidziany przez twórców.

Pytanie kontrolne brzmi: jeśli atakujący przejmie kontrolę nad tym, co model wygeneruje, to jaki jest najgorszy możliwy skutek? Odpowiedź wyznacza realny poziom ryzyka.

RAG i bazy wektorowe

W systemach opartych na wyszukiwaniu w dokumentach sprawdzamy izolację danych między klientami i rolami. Klasyczne znalezisko: baza wektorowa wspólna dla wszystkich najemców, w której filtrowanie po uprawnieniach odbywa się dopiero po pobraniu wyników - albo wcale. Użytkownik odpowiednio formułujący pytanie dostaje fragmenty cudzych dokumentów.

Wyciek instrukcji systemowej

Sama treść instrukcji rzadko jest sekretem wartym ochrony, ale bywa mapą systemu: ujawnia nazwy narzędzi, strukturę uprawnień, reguły biznesowe, a zaskakująco często także klucze API i adresy usług wewnętrznych, które ktoś wkleił dla wygody.

Koszty jako wektor ataku

Kategoria często pomijana. Przy rozliczeniu za tokeny odpowiednio skonstruowane zapytania potrafią wygenerować rachunek nieproporcjonalny do wysiłku atakującego. Sprawdzamy limity na użytkownika, ograniczenia długości kontekstu i to, czy ktokolwiek monitoruje nagły wzrost zużycia.

Dlaczego testowanie wygląda inaczej

W klasycznym pentescie podatność albo jest, albo jej nie ma - to samo żądanie daje ten sam wynik. Model językowy jest probabilistyczny: ten sam prompt raz przejdzie, a raz nie.

Dlatego testy prowadzi się inaczej. Każdy scenariusz powtarzamy wielokrotnie i patrzymy na skuteczność w ujęciu statystycznym, a nie zero-jedynkowym. Znalezisko "ta technika przechodzi w około jednej trzeciej prób" jest w tym świecie w pełni sensowne - i wystarczające, żeby uznać zabezpieczenie za nieskuteczne.

Druga różnica: sam filtr treści to nie zabezpieczenie. Mechanizmy blokujące szkodliwe odpowiedzi da się obchodzić kodowaniem, zmianą języka, rozbijaniem instrukcji na części czy osadzaniem ich w fikcyjnym kontekście. Traktujemy je jako utrudnienie, nie jako granicę bezpieczeństwa - a właściwą granicą są uprawnienia i walidacja po stronie systemu.

Jak przygotować się do takiego testu

  • Instrukcja systemowa i opis, co aplikacja ma robić, a czego nie.
  • Lista narzędzi i integracji, do których model ma dostęp, wraz z uprawnieniami, na jakich działa.
  • Architektura przepływu danych - skąd biorą się dokumenty w bazie wiedzy i kto może je tam umieścić.
  • Konta w różnych rolach, po dwa w każdej, tak samo jak przy zwykłym pentescie.
  • Ustalenie limitów - testy potrafią wygenerować realne koszty, więc warto z góry ustalić budżet i środowisko.

Pozostałe zasady przygotowania są takie same jak przy klasycznym teście penetracyjnym.

Czego taki test nie załatwi

Uczciwie: nie da się doprowadzić aplikacji LLM do stanu, w którym wstrzyknięcie promptu jest niemożliwe. Można sprawić, że przestanie mieć znaczenie - ograniczając uprawnienia, walidując wyjście i projektując system tak, żeby przejęcie kontroli nad modelem nie oznaczało przejęcia kontroli nad danymi.

Dobry test kończy się więc nie listą "załatanych promptów", tylko oceną, co atakujący może osiągnąć, gdy model zrobi dokładnie to, czego chce. I rekomendacjami, które zwykle dotyczą architektury, nie treści instrukcji.

Klasyczne podatności nigdzie nie znikają - aplikacja LLM to nadal aplikacja webowa z API, uwierzytelnianiem i bazą, więc OWASP Top 10 obowiązuje tak samo. Warstwa AI dochodzi, a nie zastępuje.

Wdrażacie aplikację opartą na LLM?

Testujemy chatboty, asystentów, systemy RAG i agentów z dostępem do narzędzi. Napisz - odpiszemy w 24 h z wyceną.

Zapytaj o wycenę