Атаки и превенция, които всеки уеб разработчик трябва да знае
Сигурността почти винаги се чупи на границите: има валидация, но не на входа; има авторизация, но един endpoint я пропуска; има escaping, но в един шаблон се показва raw HTML. Добрата новина е, че повечето реални инциденти са повторяеми класове грешки — и могат да се затварят системно.
Свързани материали: Наблюдаемост и мониторинг · Бази данни под натоварване
Съдържание
- Модел на заплахите: какво пазим и от кого
- Инжекции: SQLi, command injection, template injection
- XSS: отразен, съхранен, DOM-based
- CSRF: защо „ама това е GET“ не е защита
- Автентикация и сесии: кражба на токени, фиксация, cookies
- Контрол на достъпа и IDOR: „то е само id“
- Качване на файлове и пътища: upload, path traversal, RCE наблизо
- SSRF: когато сървърът ходи „където не трябва“
- Небезопасна десериализация и подмяна на обекти
- Браузърни защити: clickjacking, CORS, заглавки
- DoS и abuse: rate limit, brute force, скъпи операции
- Чеклист за ревю и релийз
Модел на заплахите: какво пазим и от кого
Преди техниките — рамка:
- Атакуващ: анонимен потребител, логнат потребител, партньор с API ключ, „вътрешен“ във VPN, актьор с достъп до CI/CD.
- Активи: пари, лични данни, акаунти, админ панели, интеграции, тайни, достъп до вътрешна мрежа.
- Повърхност: форми и API, редиректи, webhooks, импорт/експорт, качване на файлове, интеграции (S3, SMTP, плащания).
Практично правило: всеки вход е недоверен (вкл. заглавки, cookies, webhook payload, URL параметри, съобщения от опашки).
Инжекции: SQLi, command injection, template injection
SQL Injection (SQLi)
SQLi почти винаги започва с „само едно сурово SQL“. Защитата не е „escape“, а параметризация.
Лошо (конкатенация):
$rows = DB::select("SELECT * FROM users WHERE email = '{$email}'");
По-добре (плейсхолдъри):
$rows = DB::select('SELECT * FROM users WHERE email = ?', [$email]);
Често срещан капан са динамичните имена на колони / сортиране. Параметрите не важат за идентификатори, затова ползвайте allowlist:
$allowed = ['created_at', 'email', 'id'];
$sort = in_array($request->get('sort'), $allowed, true) ? $request->get('sort') : 'created_at';
$users = User::query()->orderBy($sort, 'desc')->paginate();
Command injection
Ако викате shell, правилото е: никога не пускайте потребителски вход в команден ред. Дори escapeshellarg() е последна бариера, не дизайн.
Template injection
Опасно е да позволите потребителски „шаблон“, който се изпълнява като Blade/Twig/Smarty. Третирайте го като данни и използвайте ограничен формат (напр. Markdown с allowlist и безопасен рендер).
XSS: отразен, съхранен, DOM-based
XSS е изпълнение на JS във вашия origin. Типични източници: raw output, HTML без санитайз, innerHTML, инжектиране в <script>/атрибути без правилно кодиране.
Базови мерки:
- escaping по подразбиране (
{{ }}в Blade); - санитайз на HTML вход (allowlist);
- CSP като ограничител на щетите.
Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'
CSRF: защо „ама това е GET“ не е защита
CSRF е когато браузърът на потребителя изпраща заявка към вашия сайт с неговите cookies/сесия, защото друг сайт го тригърва.
Мерки:
- CSRF токени за state-changing заявки (Laravel го има по подразбиране);
SameSitecookies (Laxминимум;None; Secureза cross-site);- проверка Origin/Referer за критични действия;
- никакви промени на състояние през GET.
Автентикация и сесии: кражба на токени, фиксация, cookies
Минимум:
- rate limit за login/OTP/reset;
- пароли само с
password_hash()(argon2/bcrypt); HttpOnly,Secure,SameSiteза сесийни cookies;- ротация на session ID след login и повишаване на права.
Контрол на достъпа и IDOR: „то е само id“
IDOR е когато потребителят може да познае/преброи ID и да достъпи чужди ресурси.
Защита: policies/gates, scoped заявки по owner/tenant, аудит логове за чувствителни операции.
Качване на файлове и пътища: upload, path traversal, RCE наблизо
- проверявайте съдържанието, не само разширението (MIME от браузъра е недоверен);
- лимити размер/брой;
- съхранение извън web-root, случайни имена;
- забрана за изпълнение в upload директория на ниво уеб сървър;
- не приемайте пътища от потребителски вход (защитете от
../).
SSRF: когато сървърът ходи „където не трябва“
SSRF е когато приемете URL от потребителя и го fetch-нете от сървъра (preview, import, webhook tester).
Мерки: allowlist хостове, блокирайте private IP ranges, ограничете редиректи, сложете таймаути и лимити, разрешете само https.
Небезопасна десериализация и подмяна на обекти
Не десериализирайте attacker-controlled данни (cookies/hidden fields/външни payload-и). Ползвайте подпис (HMAC) за токени, JSON + схема + валидация за данни.
Браузърни защити: clickjacking, CORS, заглавки
Clickjacking:
X-Frame-Options: DENY
Content-Security-Policy: frame-ancestors 'none'
Базови заглавки:
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), microphone=(), camera=()
DoS и abuse: rate limit, brute force, скъпи операции
Най-често атаката е „евтино за тях, скъпо за вас“: тежки търсения, експорти, PDF, login/OTP без rate limit.
Мерки: rate limiting по IP/акаунт/ключ, опашки за тежки задачи, таймаути и лимити на размер/дълбочина, кеширане там където е безопасно.
Чеклист за ревю и релийз
- [ ] Валидация на входа на границата (форми/API/webhooks).
- [ ] Allowlist за сортиране/филтри/идентификатори.
- [ ] Escaping по подразбиране, без необоснован raw HTML.
- [ ] Server-side авторизация за всяко действие; няма IDOR.
- [ ] SSRF контроли (allowlist, private IP, таймаути).
- [ ] Uploads: проверки, извън web-root, забрана за изпълнение.
Извод
Един принцип: сигурността са инварианти на границата. Валидация на входа, escaping на изхода, авторизация на сървъра, подписани интеграции и rate limit за всичко скъпо.