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
Похожие промты
site / security
Content Security Policy и security headers
CSP, HSTS, X-Frame, Permissions-Policy — закрыть основные классы атак за один проход.
securitycspheaders
Открыть
Продвинутый30-60 мин
site / security
Управление секретами
Где хранить, как ротировать, как обнаружить утечку.
securitysecrets
Открыть
Средний30-60 мин
site / security
Аутентификация и rate limiting
Защита логина, реги, восстановления пароля от brute force.
securityauthrate-limit
Открыть
Продвинутый30-60 мин