SQL injection w Prisma – praktyczny przewodnik hardeningu baz danych

Krok po kroku pokażemy, jak zabezpieczyć Prisma i TypeScript przed SQL injection i spełnić wymogi OWASP Top 10.

SQL injection w Prisma – praktyczny przewodnik hardeningu baz danych

SQL injection w Prisma jest jedną z najczęstszych podatności, które mogą zagrozić integralności danych i poufności aplikacji. W artykule przedstawiamy krótki model zagrożenia, mechanizmy działania Prisma, porównanie kodu podatnego i poprawionego, checklistę mitygacji oraz odwołania do uznanych standardów, takich jak OWASP Top 10, CWE i zasada least‑privilege.

Model zagrożenia – jak powstaje SQL injection

Atakujący wstrzykuje własny kod SQL w miejsce danych wejściowych, które aplikacja przekazuje do bazy bez odpowiedniej walidacji lub parametryzacji. W kontekście Prisma najczęściej dzieje się to przy użyciu metod queryRaw lub executeRaw, które pozwalają na ręczne wstawianie fragmentów zapytań. Jeśli przekazujemy do nich niezweryfikowane ciągi znaków, otwieramy drzwi do manipulacji zapytaniem.

Podstawowy scenariusz:

  • Użytkownik podaje wartość w formularzu (np. nazwa produktu).
  • Aplikacja buduje zapytanie SQL, wstawiając tę wartość bez parametryzacji.
  • Atakujący dodaje instrukcję OR 1=1 lub DROP TABLE, co prowadzi do nieautoryzowanego dostępu lub destrukcji danych.

Mechanizm ochrony w Prisma – parametryzacja i typowanie

Prisma ORM domyślnie generuje zapytania przy użyciu przygotowanych instrukcji (prepared statements). Dzięki temu wartości są przekazywane jako parametry, a nie wstawiane bezpośrednio do tekstu SQL. W TypeScript dodatkowo korzystamy z typów, które ograniczają możliwość wstrzyknięcia niepożądanego kodu.

Kluczowe mechanizmy:

  • Query Builder – metody takie jak findMany, create czy update budują zapytania w sposób bezpieczny.
  • Parametryzowane RAW – prisma.$queryRaw`SELECT * FROM User WHERE email = ${email}` używa interpolacji tagowanej, co automatycznie sanitizuje zmienną.
  • Typy Prisma – definiują strukturę danych i wymuszają poprawny format wejścia.

Przykład: kod podatny vs kod poprawiony

Poniżej porównujemy dwa fragmenty – najpierw niebezpieczny, potem bezpieczny.

// --- Kod podatny ---
const userInput = req.body.username; // dane z formularza
const raw = `SELECT * FROM "User" WHERE username = '${userInput}'`;
const result = await prisma.$queryRaw(raw);

W powyższym przykładzie userInput jest wstawiane bezpośrednio do łańcucha znaków, co umożliwia wstrzyknięcie dowolnego SQL.

// --- Kod poprawiony ---
const userInput = req.body.username;
// użycie tagowanej interpolacji zapewnia parametryzację
const result = await prisma.$queryRaw`SELECT * FROM "User" WHERE username = ${userInput}`;

Interpolacja tagowana (prisma.$queryRaw`...`) zamienia ${userInput} w parametr bindowany, eliminując ryzyko injection.

Checklista mitygacji – co wdrożyć w projekcie

  • Używaj wyłącznie metod Prisma API (findUnique, create itp.) zamiast ręcznych zapytań RAW.
  • Jeśli RAW jest niezbędny, stosuj tagowaną interpolację lub przekazuj parametry jako drugi argument (prisma.$queryRaw(sql, params)).
  • Włącz walidację schematu wejścia (np. zod lub class-validator) przed przekazaniem danych do warstwy dostępu do bazy.
  • Ogranicz uprawnienia konta DB używanego przez aplikację – zasada least‑privilege.
  • Monitoruj logi SQL i wykrywaj nieoczekiwane wzorce (np. nagłe użycie DROP).

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

OWASP Top 10 wymienia A03:2021 – Injection jako jedną z najpoważniejszych kategorii. W katalogu CWE odpowiedzialnym za ten problem jest CWE‑89: SQL Injection. Implementując powyższe praktyki, spełniamy wymogi obu standardów.

Zasada least‑privilege wymaga, aby konto bazy danych używane przez Prisma posiadało jedynie niezbędne uprawnienia (SELECT, INSERT, UPDATE, DELETE na wymaganych tabelach). Unikaj przyznawania roli SUPERUSER lub DBA aplikacji produkcyjnej.

„Bezpieczne zapytania to nie dodatek, to podstawa – kod, który nie może zostać wstrzyknięty, nie wymaga późniejszych poprawek.”

Typowe błędy i kompromisy w praktyce

Najczęstsze pułapki:

  • Używanie prisma.$executeRaw bez parametryzacji – łatwo przeoczyć, zwłaszcza przy migracjach.
  • Dynamiczne budowanie nazw tabel lub kolumn – Prisma nie pozwala na parametryzację identyfikatorów, więc trzeba je walidować w białej liście.
  • Rezygnacja z walidacji wejścia na rzecz „zaufania” do frontendu – prowadzi do nieprzewidzianych wektorów ataku.

Kompro­misy:

  • W niektórych przypadkach konieczne jest użycie RAW dla skomplikowanych raportów. Wtedy warto wydzielić tę warstwę do osobnego serwisu i otoczyć ją dodatkową warstwą sanitizacji.
  • Jeśli aplikacja musi obsługiwać zapytania ad‑hoc, rozważ użycie białej listy dozwolonych pól i operatorów zamiast swobodnego wstawiania tekstu.

Praktyczny przykład – checklistę wdrożeniową

// checklista.ts – przykładowa implementacja w TypeScript
import { PrismaClient } from '@prisma/client';
import { z } from 'zod';

const prisma = new PrismaClient();

// 1. Schemat walidacji wejścia
const userSchema = z.object({
  username: z.string().min(3).max(30),
  email: z.string().email(),
});

export async function createUser(payload: unknown) {
  // 2. Walidacja
  const data = userSchema.parse(payload);

  // 3. Bezpieczne użycie Prisma API – nie RAW
  return await prisma.user.create({
    data: {
      username: data.username,
      email: data.email,
    },
  });
}

// 4. Przykład RAW z parametryzacją
export async function findUsersByDomain(domain: string) {
  const safeDomain = domain.replace(/[^a-zA-Z0-9.-]/g, ''); // dodatkowa sanitizacja
  return await prisma.$queryRaw`
    SELECT * FROM "User" WHERE email LIKE ${'%@' + safeDomain}`;
}

Powyższy kod demonstruje, jak połączyć walidację schematu, użycie bezpiecznego API oraz parametryzowane RAW w sytuacjach, które tego wymagają.

Podsumowanie i zaproszenie do współpracy

Stosując opisane praktyki – parametryzację, typowanie, walidację i ograniczenie uprawnień – eliminujesz ryzyko SQL injection w projektach opartych na Prisma i TypeScript, jednocześnie spełniając wymogi OWASP Top 10 oraz CWE‑89. Jeśli potrzebujesz audytu bezpieczeństwa, wsparcia w hardeningu bazy danych lub pomocy przy wdrożeniu najlepszych praktyk, skontaktuj się z Coderia.it – nasz zespół specjalistów pomoże Ci zbudować solidną i bezpieczną architekturę backendową.

Zacznijmy

Masz projekt na oku?

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