SQL injection w Prisma – praktyczny przewodnik dla inżynierów Node.js

Dowiedz się, jak zabezpieczyć Prisma przed SQL injection w aplikacjach Node.js przy użyciu TypeScript i najlepszych praktyk OWASP.

SQL injection w Prisma – praktyczny przewodnik dla inżynierów Node.js

SQL injection w Prisma to problem, który może pojawić się nawet przy nowoczesnych ORM‑ach, jeśli nie zastosujemy odpowiednich praktyk. W tym artykule przedstawiamy model zagrożenia, mechanizmy działania Prisma, porównanie podatnego i poprawionego kodu oraz checklistę mitygacji opartą o OWASP Top 10, CWE i zasadę least‑privilege.

Model zagrożenia – jak powstaje SQL injection

Atak SQL injection polega na wstrzyknięciu nieautoryzowanego fragmentu języka SQL do zapytania generowanego przez aplikację. W kontekście Prisma najczęściej dochodzi do tego, gdy programista korzysta z tzw. raw queries lub interpoluje zmienne w stringach bez walidacji. Mechanizm Prisma sam w sobie sanitizuje parametry przekazywane jako obiekty, ale nie chroni przed niebezpiecznym użyciu prisma.$queryRaw czy prisma.$executeRaw z niewłaściwym formatowaniem.

Jak Prisma i TypeScript pomagają w eliminacji ryzyka

Prisma oferuje dwa poziomy ochrony: statyczny (typy TypeScript) oraz runtime (parametryzowane zapytania). Typy zapewniają, że pola przekazywane do zapytań istnieją w schemacie, a runtime automatycznie zamienia wartości na parametry bindowane, co eliminuje możliwość wstrzyknięcia kodu SQL. Kluczowe jest jednak konsekwentne używanie metod findUnique, findMany, create etc., zamiast ręcznego budowania zapytań.

Przykład: podatny vs. poprawiony kod

Poniżej prezentujemy dwa fragmenty – pierwszy narusza zasady bezpieczeństwa, drugi wykorzystuje pełny potencjał Prisma i TypeScript.

// podatny kod – użycie $queryRaw z interpolacją stringa
const userId = req.query.id; // pochodzące od użytkownika
const result = await prisma.$queryRaw(`SELECT * FROM "User" WHERE id = ${userId}`);

W powyższym przykładzie wartość userId jest wstawiana bezpośrednio do zapytania, co umożliwia atakującemu podmianę na 1; DROP TABLE "User";. Poprawny sposób to użycie parametrów bindowanych:

// poprawiony kod – parametr bindowany
const userId = Number(req.query.id);
if (Number.isNaN(userId)) {
  throw new Error('Invalid user id');
}
const result = await prisma.$queryRaw`
  SELECT * FROM "User" WHERE id = ${userId}`;

Prisma automatycznie zamienia ${userId} na placeholder $1 i przesyła wartość osobno, co eliminuje możliwość wstrzyknięcia.

Checklist mitygacji – co zrobić, by nie popełnić błędu

  • Używaj wyłącznie metod ORM (find*, create*, update*, delete*) zamiast $queryRaw i $executeRaw.
  • Gdy musisz użyć raw query, zawsze stosuj parametryzację (prisma.$queryRaw`...${value}`).
  • Waliduj i konwertuj wszystkie dane wejściowe (np. Number(), zod, class‑validator).
  • Włącz w bazie minimalne uprawnienia (least‑privilege) – konto Prisma nie powinno mieć uprawnień DDL.
  • Regularnie skanuj kod pod kątem CWE‑89 (SQL Injection) przy pomocy SAST.

Odwołania do standardów – OWASP, CWE i zasada least‑privilege

OWASP Top 10 klasyfikuje SQL injection jako A03 – „Injection”. CWE‑89 opisuje dokładnie technikę wstrzykiwania oraz rekomendowane środki zaradcze, w tym użycie przygotowanych instrukcji i unikanie dynamicznego budowania zapytań. Zasada least‑privilege wymusza przydzielenie kontu bazy minimalnych uprawnień – w praktyce konto Prisma powinno mieć jedynie SELECT, INSERT, UPDATE, DELETE na potrzebnych tabelach.

„Bezpieczeństwo nie jest jednorazowym testem, a ciągłym procesem – każdy nowy fragment kodu to potencjalny wektor ataku.”

Typowe błędy i kompromisy

Najczęstszy błąd to uzależnienie się od „magicznych” stringów w logice biznesowej i późniejsze ich wklejanie do raw queries. Inny problem – brak walidacji typów przy konwersji danych wejściowych, co prowadzi do nieoczekiwanych NaN i w konsekwencji do fallbacku na stringi. Kompromisem może być użycie prisma.$queryRawUnsafe w sytuacjach, gdy nie ma alternatywy, ale wtedy należy ręcznie zapewnić sanitację i ograniczyć dostęp do takiej funkcji do jednego, dobrze przetestowanego modułu.

Praktyczna checklista wdrożeniowa

  • Audyt kodu pod kątem użycia $queryRaw i $executeRaw.
  • Wymuś typowanie wszystkich parametrów w TypeScript (np. type UserId = number).
  • Skonfiguruj Prisma schema z odpowiednimi rolami – @@map i @@id nie muszą być modyfikowane w runtime.
  • Wdroż testy integracyjne, które próbują wstrzyknąć złośliwe ciągi do endpointów.
  • Ustaw reguły CI/CD, które blokują merge przy wykryciu CWE‑89 w wynikach SAST.

Stosując powyższe praktyki, zminimalizujesz ryzyko SQL injection w aplikacjach Node.js korzystających z Prisma i TypeScript, a jednocześnie utrzymasz wysoką wydajność i czytelność kodu.

Jeśli potrzebujesz wsparcia przy hardeningu warstwy danych, audycie bezpieczeństwa lub wdrożeniu CI/CD zgodnego z OWASP, skontaktuj się z Coderia.it – pomożemy Ci zbudować solidne i bezpieczne rozwiązania backendowe.

Zacznijmy

Masz projekt na oku?

Opisz go w kilku zdaniach. Odpiszę w ciągu 24 godzin z bezpłatną wyceną i propozycją stacku.