Атаки й захист на прикладах з PHP
Подивіться, як атака працює, напишіть захищений код і дізнайтеся, де Laravel і Symfony вас не прикриють.
- 01Натисніть на приклад під полем «Логін».
- 02Подивіться, де введене виривається з лапок.
- 03Увімкніть підготовлений запит.
Помаранчевий блок вирвався з лапок і став частиною SQL. Решту запиту, перевірку пароля, вимкнув коментар.
Умова OR '1'='1' правдива для кожного рядка, а коментар відрізав перевірку пароля. Код входу бере перший рядок, а це часто адміністратор.
Так виглядає старий код входу, де хеш пароля порівнюють прямо в SQL. Як зберігати паролі правильно, розповімо в темі «Паролі й хешування».
Де саме бʼє кожна атака
Запит іде від браузера до бази й назад. На кожній ділянці свої атаки. Натисніть на шпильку, щоб відкрити тему.
Каталог атак
Введення стає частиною запиту до бази.
Чужий скрипт виконується на вашій сторінці від імені користувача.
Інший сайт відправляє форму від імені залогіненої людини.
Користувач дописує в запит поле is_admin, і модель його приймає.
Змінили id в адресі й побачили чуже резюме.
md5, sha1, власна «сіль» і чому password_hash достатньо.
avatar.php замість avatar.png і виконання коду на сервері.
Сервер іде за URL від користувача і стукає у вашу внутрішню мережу.
.env у публічній теці, ключі в репозиторії й у логах.
Фіксація сесії, відсутні Secure, HttpOnly і SameSite.
Як часто в PHP-коді: оцінка редакції PHP Ukraine з досвіду ревʼю й аудитів, а не статистика. OWASP: категорія в OWASP Top 10, редакція 2025 року.
Реальні захисти цього сайту
resources/views/profile/show.blade.php
«Про себе» розробника друкується лише через {{ }}. TextBlocks ділить текст на заголовки, списки й абзаци, а Blade екранує кожен шматок, тож тег у профілі стає буквами.
src/Content/Infrastructure/Markdown/CommonMarkRenderer.php
Markdown статей, які пишуть читачі, рендериться з html_input: strip і allow_unsafe_links: false. Сирий HTML вирізається, посилання javascript: не стають посиланнями.
src/Projects/Domain/ProjectLink.php
Посилання на сайт і репозиторій проєкту приймаються лише зі схемою http або https і справжнім доменом, тож у href не потрапить javascript:.
src/Hiring/Infrastructure/Catalog/EloquentJobPageQuery.php
Навіть сирий фрагмент SQL для сортування схожих вакансій отримує значення через ? і масив bindings, а не склеюванням рядка.
bootstrap/app.php
Кожна форма групи web проходить перевірку CSRF. Винятки лише для launch/subscribe і events, де сесії ще немає; ці адреси обмежує throttle.
routes/web.php
Вхід, скидання пароля й відгук на вакансію мають ліміт спроб через throttle: 10, 5 і 5 на хвилину.
app/Models/User.php
Пароль має cast hashed: Laravel хешує його bcrypt ще до запису, у базі немає відкритого тексту.
app/Auth/AccountSecurity.php
Зміна пошти чи пароля просить підтвердити, хто за клавіатурою: поточний пароль або свіжий вхід через провайдера, з яким повʼязано акаунт. Новий пароль завершує всі інші сесії.
app/Http/Controllers/ProfileController.php
Резюме приймається лише як PDF до 5 МБ, лягає на приватний диск під випадковою назвою й віддається тільки через маршрут, що перевіряє, хто питає.
app/Http/Middleware/SecurityHeaders.php
Кожна відповідь несе nosniff, X-Frame-Options: SAMEORIGIN, Referrer-Policy і Permissions-Policy, а через https ще й HSTS.