SQL injection у Prisma – практичний посібник для інженерів Node.js

Дізнайтеся, як захистити Prisma від SQL injection у застосунках Node.js, використовуючи TypeScript та кращі практики OWASP.

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

SQL injection у Prisma – це проблема, яка може виникнути навіть при сучасних ORM, якщо не застосувати належні практики. У цій статті ми розглядаємо модель загрози, механізми роботи Prisma, порівняння вразливого та виправленого коду та чек‑лист мітигації, заснований на OWASP Top 10, CWE та принципі найменших привілеїв.

Модель загрози – як виникає SQL injection

Атака SQL injection полягає у впровадженні неавторизованого фрагмента мови SQL у запит, що генерується застосунком. У контексті Prisma це найчастіше трапляється, коли розробник користується так званими raw запитами або інтерполює змінні у рядках без валідації. Механізм Prisma сам по собі санітизує параметри, передані як об’єкти, але не захищає від небезпечного використання prisma.$queryRaw чи prisma.$executeRaw з неправильним форматуванням.

Як Prisma і TypeScript допомагають усунути ризик

Prisma пропонує два рівні захисту: статичний (типи TypeScript) та runtime (параметризовані запити). Типи гарантують, що поля, передані у запити, існують у схемі, а runtime автоматично перетворює значення у прив’язані параметри, що усуває можливість ін’єкції SQL‑коду. Ключове – послідовно використовувати методи findUnique, findMany, create тощо, замість ручного формування запитів.

Приклад: вразливий vs. виправлений код

Нижче наведено два фрагменти – перший порушує правила безпеки, другий використовує повний потенціал Prisma і TypeScript.

// вразливий код – використання $queryRaw з інтерполяцією рядка
const userId = req.query.id; // отримане від користувача
const result = await prisma.$queryRaw(`SELECT * FROM "User" WHERE id = ${userId}`);

У цьому прикладі значення userId вставляється безпосередньо у запит, що дозволяє атакуючому підмінити його, наприклад, на 1; DROP TABLE "User";. Правильний спосіб – використання прив’язаних параметрів:

// виправлений код – прив’язаний параметр
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 автоматично замінює ${userId} на плейсхолдер $1 і передає значення окремо, що усуває можливість ін’єкції.

Checklist мітигації – що зробити, щоб не помилитися

  • Використовуйте лише методи ORM (find*, create*, update*, delete*) замість $queryRaw і $executeRaw.
  • Якщо треба застосувати raw‑query, завжди використовуйте параметризацію (prisma.$queryRaw`...${value}`).
  • Валідуйте і конвертуйте всі вхідні дані (наприклад, Number(), zod, class‑validator).
  • Увімкніть у базі мінімальні привілеї (least‑privilege) – обліковий запис Prisma не повинен мати прав DDL.
  • Регулярно скануйте код на предмет CWE‑89 (SQL Injection) за допомогою SAST.

Посилання на стандарти – OWASP, CWE та принцип least‑privilege

OWASP Top 10 класифікує SQL injection як A03 – «Injection». CWE‑89 детально описує техніку ін’єкції та рекомендовані заходи, включаючи використання підготовлених виразів і уникання динамічного формування запитів. Принцип least‑privilege вимагає надати базі мінімальні права – на практиці обліковий запис Prisma має мати лише SELECT, INSERT, UPDATE, DELETE на потрібних таблицях.

«Безпека не є одноразовим тестом, а постійним процесом – кожен новий фрагмент коду це потенційний вектор атаки.»

Типові помилки та компроміси

Найпоширеніша помилка – залежність від «магічних» рядків у бізнес‑логіці та їх подальше вставлення у raw‑queries. Інша проблема – відсутність валідації типів при конвертації вхідних даних, що призводить до неочікуваних NaN і, як наслідок, до fallback‑у на рядки. Компромісом може бути використання prisma.$queryRawUnsafe у випадках, коли альтернативи немає, але тоді треба вручну забезпечити санітизацію і обмежити доступ до такої функції одним добре протестованим модулем.

Практичний чек‑лист впровадження

  • Аудит коду на використання $queryRaw і $executeRaw.
  • Забезпечте типізацію всіх параметрів у TypeScript (наприклад, type UserId = number).
  • Налаштуйте Prisma schema з відповідними ролями – @@map і @@id не повинні змінюватися у runtime.
  • Впровадьте інтеграційні тести, які намагаються ін’єктувати шкідливі рядки у ендпоінти.
  • Встановіть правила CI/CD, які блокують merge при виявленні CWE‑89 у результатах SAST.

Застосовуючи вищеперелічені практики, ви мінімізуєте ризик SQL injection у застосунках Node.js, що працюють з Prisma і TypeScript, і одночасно збережете високу продуктивність і читабельність коду.

Якщо потрібна допомога у зміцненні шару даних, аудиті безпеки або впровадженні CI/CD за стандартами OWASP, звертайтеся до Coderia.it – ми допоможемо створити надійні та безпечні бекенд‑рішення.

Почнімо

Маєте проєкт на думці?

Опишіть його кількома реченнями. Відповім протягом 24 годин із безкоштовною оцінкою та пропозицією стеку.