Kod wygenerowany przez AI często działa, ale nie jest przez to bezpieczny: według raportu Veracode z 2026 roku około 44% zadań programistycznych kończyło się kodem z co najmniej jedną znaną podatnością. Najczęściej zawodzi ochrona przed XSS, wstrzykiwaniem do logów, brak autoryzacji oraz sekrety zapisane w kodzie. Dlatego każdą zmianę od AI traktuj jak kod od nieznanego autora: przeczytaj diff, uruchom testy, przeskanuj sekrety i zależności, a klucz, który trafił do repozytorium, wymień, a nie tylko usuń.
Kiedy agent AI dopisuje formularz logowania, endpoint albo zapytanie do bazy, pierwsze pytanie brzmi „czy działa?”. Drugie, rzadziej zadawane, brzmi „czy jest bezpieczne?”. Odpowiedź na pierwsze widać od razu po uruchomieniu. Na drugie trzeba odpowiedzieć samemu, bo narzędzie AI nie dopisze ostrzeżenia, że właśnie zostawiło lukę. Ten artykuł pokazuje, jakie luki pojawiają się w kodzie z AI najczęściej i jak je wychwycić, zanim trafią do repozytorium. Ogólne omówienie ryzyk znajdziesz w artykule co to jest vibe coding.
Czy kod wygenerowany przez AI jest mniej bezpieczny?
Dane są niewygodne. Firma Veracode, która sprzedaje narzędzia do analizy bezpieczeństwa, regularnie sprawdza, jak bezpieczny jest kod, który modele AI piszą w odpowiedzi na zadania programistyczne. W raporcie z lipca 2026 roku średni wynik bezpieczeństwa wyniósł 56% (w pierwszym raporcie było 55%), czyli około 44% zadań wprowadzało znaną podatność. Wyniki zależały od typu luki:
| Typ luki | Odsetek zadań bez podatności |
|---|---|
| Algorytmy kryptograficzne | 87% |
| SQL injection | 83% |
| Cross-site scripting (XSS) | 15% |
| Wstrzykiwanie do logów (log injection) | 12% |
Dwa wnioski. Po pierwsze, modele radzą sobie z dobrze znanymi wzorcami (parametryzowane zapytania SQL), a gubią się tam, gdzie bezpieczeństwo zależy od kontekstu: co z tym tekstem robi dalej przeglądarka albo log. Po drugie, większe modele były tylko nieznacznie lepsze od mniejszych (średnio 53% wobec 51%), więc „nowszy, większy model” nie zastępuje sprawdzania. Pamiętaj, że to badanie producenta narzędzi bezpieczeństwa, ale zbieżne wyniki pokazywał już jego pierwszy raport z 2025 roku.
Jakie luki najczęściej pojawiają się w kodzie z AI?
| Luka | Jak wygląda w kodzie | Jak ją wychwycić | Gdzie dowiesz się więcej |
|---|---|---|---|
| XSS | innerHTML = daneUzytkownika, wstawianie surowego HTML | Szukaj innerHTML, dangerouslySetInnerHTML, v-html | XSS w JavaScript |
| SQL injection | Zapytanie sklejone przez f-string lub + | Szukaj SQL w f-stringach i konkatenacji | SQL injection |
| Sekrety w kodzie | Klucz API wpisany na sztywno albo w kodzie frontendu | Szukaj sk-, api_key =, password =, plików .env w commicie | sekcja niżej |
| Brak autoryzacji | Endpoint zwraca dane po samym id, bez sprawdzenia właściciela | Pytaj: kto może to wywołać i czyje dane zobaczy? | autoryzacja w FastAPI |
| Wykonanie cudzego kodu | eval(...), shell=True, os.system(...) z danymi z wejścia | Szukaj eval, exec, shell=True | command injection i eval |
| Brak ochrony CSRF | Formularz zmieniający dane bez tokenu lub SameSite | Sprawdź, czy akcje zmieniające stan są chronione | CSRF |
| Upload bez walidacji | Zapis pliku pod nazwą od użytkownika, bez sprawdzenia typu i rozmiaru | Szukaj zapisu file.filename wprost na dysk | upload plików |
| Dane wrażliwe w logach | print(request.body), logowanie całego obiektu użytkownika | Szukaj logowania całych obiektów i haseł | wykrywanie danych wrażliwych |
Tabela nie jest kompletna, ale pokrywa większość przypadków, które widać w kodzie początkujących i w propozycjach agentów.
Jak wygląda niebezpieczne zapytanie SQL od AI w praktyce?
Zobaczmy to na minimalnym przykładzie. Ta sama funkcja w dwóch wersjach, uruchomiona na bazie w pamięci z dwoma użytkownikami:
import sqlite3
db = sqlite3.connect(":memory:")
db.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT, role TEXT)")
db.executemany("INSERT INTO users (email, role) VALUES (?, ?)", [
("anna@example.com", "user"),
("admin@example.com", "admin"),
])
def find_user_unsafe(email):
# wersja sklejona z tekstu od użytkownika
return db.execute(f"SELECT email, role FROM users WHERE email = '{email}'").fetchall()
def find_user_safe(email):
# wersja poprawiona: parametr przekazany osobno
return db.execute("SELECT email, role FROM users WHERE email = ?", (email,)).fetchall()
payload = "x' OR '1'='1"
print("unsafe:", find_user_unsafe(payload))
print("safe: ", find_user_safe(payload))
Wynik uruchomienia:
unsafe: [('anna@example.com', 'user'), ('admin@example.com', 'admin')]
safe: []
Dla spreparowanego „adresu e-mail” pierwsza wersja zwróciła wszystkich użytkowników, w tym administratora, bo tekst zmienił sam warunek zapytania. Druga potraktowała ten sam tekst jak zwykłą wartość i nic nie znalazła. Różnica w kodzie to jeden znak zapytania zamiast f-stringa, a w skutkach to wyciek bazy. Pełne omówienie, razem z innymi bazami i ORM-ami, jest w artykule o SQL injection.
Co zrobić, gdy klucz API trafił do repozytorium?
Agent chętnie wpisze klucz wprost w kod, bo „tak działa od razu”. Jeśli taki plik trafi do commita, samo usunięcie linii nie wystarcza: klucz zostaje w historii Gita, a w publicznym repozytorium boty wyszukują takie wycieki. Kolejność działań:
- Unieważnij i wymień klucz w panelu dostawcy (OpenAI, Stripe, baza danych). To jedyny krok, który naprawdę zamyka wyciek.
- Przenieś sekrety do zmiennych środowiskowych albo pliku
.envi dopisz go do.gitignore, zanim zrobisz pierwszy commit. - Dopiero potem czyść historię Gita, jeśli chcesz, ale pamiętaj, że to uzupełnienie, a nie zamiennik wymiany klucza.
- Włącz skanowanie sekretów w repozytorium (GitHub ma takie funkcje, zależnie od planu i typu repozytorium) i przejrzyj
git diff --stagedprzed każdym commitem.
W aplikacjach webowych uważaj jeszcze na jedną pułapkę: wszystko, co trafia do kodu frontendu (np. zmienne z prefiksem NEXT_PUBLIC_ w Next.js), jest widoczne dla każdego odwiedzającego. Klucz do płatnego API nie może tam trafić, tylko do backendu.
Czy AI może zaproponować pakiet, który nie istnieje?
Tak, i bywa to niebezpieczne. Model potrafi wymyślić wiarygodnie brzmiącą nazwę biblioteki, a ktoś złośliwy może zarejestrować pakiet o dokładnie takiej nazwie w rejestrze (npm, PyPI). Technika nazywa się slopsquatting. Zanim wpiszesz pip install lub npm install z polecenia, które zaproponowała AI, sprawdź nazwę pakietu w oficjalnym rejestrze: liczba pobrań, data ostatniej wersji i strona projektu powiedzą ci więcej niż sama nazwa. Szerzej o tym, jak AI zmyśla biblioteki i metody, opisuję w artykule o halucynacjach AI w kodzie.
Jak poprosić AI o bezpieczniejszy kod?
Można i warto, ale to pomaga tylko częściowo. Dopisz do polecenia konkretne wymagania („parametryzuj zapytania SQL, nie wstawiaj sekretów do kodu, waliduj dane wejściowe, sprawdzaj, czy użytkownik ma dostęp do tego zasobu”) i trzymaj je w stałym pliku instrukcji projektu, jak AGENTS.md lub CLAUDE.md. Więcej o plikach instrukcji jest w artykule o narzędziach AI dla programistów. Takie wymagania zmniejszają liczbę prostych błędów, ale nie dają gwarancji: wyniki z raportu Veracode pokazują, że luki typu XSS pojawiają się także wtedy, gdy model „wie”, jak wygląda dobra praktyka.
Jak sprawdzić kod z AI przed commitem?
Prosta lista, którą możesz przejść w kilka minut:
- Przeczytaj cały diff (
git diff), nie tylko plik, który chciałeś zmienić. Agenci potrafią dotknąć kilku miejsc naraz. Jak robić to sprawnie, opisuję w artykule o code review. - Uruchom testy i dopisz jeden, który sprawdza przypadek brzegowy lub atak (np. spreparowany tekst z przykładu wyżej).
- Poszukaj sekretów w zmianach: klucze, hasła, tokeny, pliki
.env. - Sprawdź zależności: czy każdy nowy pakiet istnieje i jest znany, a potem uruchom skaner znanych podatności (
npm auditw projektach Node,pip-auditw projektach Pythona). - Użyj analizatora statycznego, np.
banditw Pythonie, który wskazuje m.in.eval,shell=Truei słabe użycie kryptografii. - Zapytaj siebie o trzy rzeczy: kto może to wywołać, jakie dane przyjmuje i co z nimi robi dalej.
Pamiętaj też, że samo uruchomienie obcego kodu bywa ryzykowne: skrypt albo agent działa z twoimi uprawnieniami, z dostępem do plików i kluczy, a agent może wykonać instrukcję ukrytą w pliku, na stronie lub w zgłoszeniu (prompt injection). Obcy kod uruchamiaj w kontenerze lub maszynie wirtualnej, bez sekretów, i czytaj polecenia, które zatwierdzasz. Zasady opisuję w sekcji „Czy uruchamianie kodu od AI jest bezpieczne?” w artykule o halucynacjach AI w kodzie, a o zatwierdzaniu akcji we własnym agencie w tekście o uprawnieniach i zatwierdzaniu akcji agenta AI.
Czy mogę poprosić AI o przegląd bezpieczeństwa własnego kodu?
Tak, ale zrób to osobno od pisania. Po skończeniu zadania otwórz nową rozmowę i poproś o przegląd samego diffa, np.: „Przejrzyj ten diff pod kątem bezpieczeństwa: wypisz miejsca z danymi od użytkownika, sekrety i brak autoryzacji. Nie zmieniaj kodu.” Drugi, osobny przegląd wychwytuje inne rzeczy niż ta sama rozmowa, w której model pisał kod i „broni” własnego rozwiązania. To uzupełnienie twojej listy kontrolnej, a nie jej zamiennik: o tym, czy zgłoszone uwagi są trafne, decydujesz ty.
Podsumowanie: jak bezpiecznie korzystać z kodu od AI?
- Kod, który działa, nie jest przez to bezpieczny: około 44% zadań w raporcie Veracode wprowadzało znaną podatność.
- Najsłabiej wypadają XSS i logowanie danych od użytkownika, najlepiej sprawdzone wzorce (np. parametry w SQL) wychodzą lepiej, ale nie zawsze.
- Klucz, który trafił do repozytorium, trzeba wymienić, samo usunięcie nie wystarcza.
- Sprawdzaj nazwy pakietów, zanim je zainstalujesz.
- Dopisz wymagania bezpieczeństwa do pliku instrukcji projektu, ale nie zastępuj nimi przeglądu diffa.
- Ucz się podstaw bezpieczeństwa: bez nich nie rozpoznasz luki, której agent nie zgłosi.


