EvolCode
треки/Сервер и продакшн·08 / 08
8 мин чтения

Безопасность — OWASP top 5 для вайбкодеров

OWASP — некоммерческая организация которая ведёт top-10 самых частых уязвимостей. Разберём пять которые ИИ совершает регулярно — это закроет 80% реальных рисков пет-проекта.

1. SQL-инъекции

Если ты собираешь SQL-запрос склеиванием строк, типа

$ "SELECT * FROM users WHERE name = ZZZ" + name + "ZZZ"

(где ZZZ — кавычки), то пользователь введёт что-то вроде

$ ZZZ; DROP TABLE users--

и привет всем данным. Защита: ВСЕГДА параметризованные запросы. В Prisma и других ORM — автоматически:

$ db.user.findMany({where: {name}})

Если используешь raw SQL — Prisma db.$queryRaw с template literal: переменная подставится как параметр, а не как часть SQL.

2. XSS — Cross-Site Scripting

Пользователь оставил коммент:

$ <script>alert(localStorage)</script>

Если ты вставишь этот текст в HTML напрямую — скрипт выполнится у каждого, кто откроет страницу. React автоматически экранирует через {userInput} — БЕЗОПАСНО. Опасность только когда используешь

$ dangerouslySetInnerHTML={{__html: userInput}}

никогда без санитайзера типа DOMPurify. Также: в API-ответах ставь Content-Type: application/json — иначе браузер может «угадать» как HTML и выполнить.

3. CSRF — действия от твоего имени

Пользователь залогинен на твоём сайте, открывает злую страницу со скрытой формой

$ <form action="https://твой-сайт.com/api/delete-account" method="POST">

+ автоотправка. Cookie сессии браузер пошлёт автоматически — действие выполнится от имени пользователя. Защита: sameSite=lax/strict у cookie (NextAuth ставит сам), CSRF-токены для форм, double-submit pattern, и вообще — destructive endpoints не делать GET-методом.

4. Утёкшие секреты

Самый частый баг: запушил .env в гит. Боты сканируют GitHub в реальном времени — секунды от пуша до использования. Защита: .gitignore сразу при создании репо, проверка git status перед коммитом, github secrets scanner (включён по умолчанию). Если случилось — отозвать все ключи в дашбордах сервисов и поменять, удаление файла из истории не помогает.

5. IDOR — доступ к чужому

Endpoint /api/orders/42 — заказ. Если ты не проверяешь что ЭТОТ заказ принадлежит ТЕКУЩЕМУ пользователю — кто угодно прочитает чужие данные просто меняя цифру в URL. Защита: в каждом query —

$ where: { id, userId: session.user.id }

ИИ ОЧЕНЬ часто забывает про userId — это лучший на свете способ слить данные. Проверяй сам.

примерtypescript
// ╔═════════════════════════════════════╗
// ║  1. SQL-INJECTION                    ║
// ╚═════════════════════════════════════╝

// ❌ ПЛОХО — конкатенация
const sql = `SELECT * FROM user WHERE name = '${name}'`;
// Если name = "'; DROP TABLE user--" — катастрофа

// ✅ ХОРОШО — параметризованный запрос (Prisma)
const users = await db.user.findMany({ where: { name } });

// ✅ Если raw SQL — template literal с Prisma
const users = await db.$queryRaw`SELECT * FROM "user" WHERE name = ${name}`;
// Prisma экранирует автоматически


// ╔═════════════════════════════════════╗
// ║  2. XSS                              ║
// ╚═════════════════════════════════════╝

// ✅ React автоматически экранирует
<div>{userComment}</div>     // безопасно — < и > превратятся в &lt; &gt;

// ❌ dangerouslySetInnerHTML без санитайзера
<div dangerouslySetInnerHTML={{__html: userComment}} />
// Если userComment = "<script>steal()</script>" — выполнится

// ✅ С санитайзером
import DOMPurify from 'isomorphic-dompurify';
<div dangerouslySetInnerHTML={{__html: DOMPurify.sanitize(userComment)}} />


// ╔═════════════════════════════════════╗
// ║  3. CSRF                             ║
// ╚═════════════════════════════════════╝

// ✅ Cookie с правильными флагами (NextAuth ставит сам)
{
  httpOnly: true,
  secure: true,           // только HTTPS
  sameSite: 'lax',        // не шлёт cookie с других сайтов на твой
}

// ❌ Destructive операции через GET
// app/api/delete-account/route.ts
export async function GET() { /* удаление */ }    // ПЛОХО

// ✅ Через POST/DELETE с проверкой Origin
export async function POST(req: Request) {
  const origin = req.headers.get('origin');
  if (origin !== 'https://example.com') {
    return new Response('Forbidden', { status: 403 });
  }
  // ... удаление
}


// ╔═════════════════════════════════════╗
// ║  4. УТЁКШИЕ СЕКРЕТЫ                  ║
// ╚═════════════════════════════════════╝

// ❌ Хардкод в коде
const API_KEY = 'sk-proj-abc123...';

// ✅ В .env, .env в .gitignore
const API_KEY = process.env.OPENAI_API_KEY;

// ✅ Перед каждым commit:
// git diff --cached | grep -i "secret\|api_key\|password"
// (или git-secrets / pre-commit hook)

// ❌ Лог секрета
console.log('Got user data:', user);   // если в user есть passwordHash — логи в Sentry это ловят

// ✅ Явно убирай чувствительные поля
console.log('User:', { id: user.id, email: user.email });


// ╔═════════════════════════════════════╗
// ║  5. IDOR — доступ к чужому           ║
// ╚═════════════════════════════════════╝

// ❌ ПЛОХО — нет проверки владельца
// app/api/orders/[id]/route.ts
export async function GET(req: Request, { params }) {
  const order = await db.order.findUnique({ where: { id: params.id } });
  return Response.json(order);
}
// Любой залогиненный юзер видит ЛЮБОЙ заказ просто меняя URL

// ✅ ХОРОШО — проверяем что заказ принадлежит этому юзеру
export async function GET(req: Request, { params }) {
  const session = await getServerSession(authOptions);
  if (!session) return new Response('Unauthorized', { status: 401 });

  const order = await db.order.findUnique({
    where: {
      id: params.id,
      userId: session.user.id,    // ← КРИТИЧНО
    },
  });

  if (!order) return new Response('Not Found', { status: 404 });
  return Response.json(order);
}

// ✅ Ещё лучше — middleware который автоматом фильтрует по userId
// (это уже архитектура — Row-level security в Postgres, например)

Антипаттерны и правильные варианты для каждой уязвимости

главное
  • 01SQL-инъекции: всегда параметризованные запросы или ORM — никогда строковая склейка
  • 02XSS: React защищает {userInput}; dangerouslySetInnerHTML только с DOMPurify
  • 03CSRF: sameSite cookie + destructive операции только POST/DELETE с проверкой Origin
  • 04Утёкшие секреты: .env в .gitignore СРАЗУ, никогда console.log полного user
  • 05IDOR — главный баг ИИ: каждый запрос к БД должен иметь where { id, userId } — без userId не пиши
  • 06Если случилась утечка ключа — отозвать в дашборде, не пытаться очистить историю
  • 07Тестируй: меняй цифры в URL, вводи фигню в формы, попробуй CSRF — это и есть pentest
проверь себя
01Какая уязвимость ИИ оставляет ЧАЩЕ всего?
02Безопасно ли вставлять userInput в `<div>{userInput}</div>` в React?
03Запушили .env с ключом в публичный репо. Лучшее действие?
← к треку