Skip to content
PПромтбук
RUEN
04Безопасность

Валидация входных данных

Никогда не доверяй input'у. Где и как валидировать, и почему это первая защита.

Валидируй ВСЕ inputs.

Правило: Каждое значение от внешнего мира — потенциально враждебное. Включая:

  • User input
  • API requests
  • Webhook payloads
  • File uploads
  • URL parameters
  • Cookie values
  • Headers
  • ENV переменные (yes, при boot)

Где валидировать

1. На границе системы (входные точки)

Не "где-то в бизнес-логике", а на boundary.

// API endpoint
export async function POST(req) {
  const body = await req.json();

  // ВАЛИДАЦИЯ ПЕРВЫМ ДЕЛОМ
  const parsed = schema.safeParse(body);
  if (!parsed.success) {
    return Response.json(
      { error: 'validation_failed', fields: parsed.error.flatten() },
      { status: 400 }
    );
  }

  // Теперь parsed.data type-safe
  await createUser(parsed.data);
}

2. Перед каждым downstream call

Даже если validated на entrance — переvalidate если данные пошли в:

  • SQL query (использовать параметры, не concat)
  • Shell command
  • HTML render (escape!)
  • File path (path traversal)

Чем валидировать

Schema-based (Zod, Yup, Joi, Pydantic, etc.):

const userSchema = z.object({
  email: z.string().email(),
  age: z.number().int().min(13).max(150),
  username: z.string().regex(/^[a-z0-9_]{3,20}$/),
  role: z.enum(['user', 'admin']),
  bio: z.string().max(500).optional(),
});

type User = z.infer<typeof userSchema>;

Преимущества:

  • Type-safe (TS вывод типа)
  • Декларативно
  • Composable

Категории проверок

1. Type

  • Строка / число / boolean
  • Объект с правильной shape

2. Format

  • Email regex
  • URL valid
  • Date format
  • UUID format

3. Range

  • Min / max length
  • Min / max value
  • Item count в массиве

4. Set membership

  • Enum значений
  • Whitelist (только эти ID)

5. Business rules

  • "Email уникален"
  • "Дата в будущем"
  • "Сумма не превышает лимит"

Business rules — после parse, перед save.

Защита от классических уязвимостей

SQL injection

// плохо
db.query(`SELECT * FROM users WHERE email = '${email}'`);

// хорошо
db.query('SELECT * FROM users WHERE email = $1', [email]);

ORM с параметрами — нативно безопасны.

XSS

// плохо
<div dangerouslySetInnerHTML={{ __html: userInput }} />

// хорошо
<div>{userInput}</div>  // React auto-escapes

Если HTML нужен — sanitize через DOMPurify:

const clean = DOMPurify.sanitize(dirty);

Command injection

// плохо
exec(`ls ${userInput}`);

// хорошо
execFile('ls', [userInput]);  // arguments separately

Path traversal

// плохо
const filePath = path.join('/uploads', userFilename);
// userFilename = "../../etc/passwd" → /etc/passwd

// хорошо
const safe = path.basename(userFilename);  // strip directory parts
const filePath = path.join('/uploads', safe);

File upload

  • Whitelist расширения / MIME type
  • Магические байты check (не доверяй extension)
  • Лимит размера
  • Изоляция от исполняемых директорий
  • Скан антивирусом для критичных систем

HTTPS only

  • Все API только HTTPS
  • HSTS header
  • Strict-Transport-Security: max-age=...

Размер

  • Max body size
  • Max field length
  • Max array length
  • Защита от DoS через огромные payloads

Анти-паттерны

  • ❌ "Валидируем на фронте" — фронт можно обойти curlом
  • ❌ Trust внутренние API без validation
  • ❌ Custom validation regex без тестов
  • ❌ Validate в нескольких местах с разной логикой
  • ❌ Тихо обрезать длинный input вместо ошибки

Чек-лист

  • Все endpoints имеют schema validation
  • Body size limit
  • SQL через параметризованные queries
  • User HTML — sanitized
  • File upload — type / size / path checks
  • Errors не leak'ают internals в response
К подразделу «Безопасность»
Похожие промты