Безопасность PHP приложений: топ-10 уязвимостей для начинающих
PHP остаётся одним из самых популярных языков для веб-разработки, но его гибкость часто становится причиной серьёзных ошибок безопасности. Для новичка легко пропустить критическую уязвимость, которая может привести к утечке данных или полному взлому сервера. В этой статье мы разберём 10 самых распространённых уязвимостей PHP-приложений, покажем примеры опасного кода и научим вас правильно защищаться. Материал подготовлен специально для начинающих разработчиков, которые хотят писать безопасный код с первого дня.
1. SQL-инъекции (SQL Injection)
Это самая известная и опасная уязвимость. Она возникает, когда пользовательский ввод напрямую подставляется в SQL-запрос без экранирования. Злоумышленник может получить доступ к базе данных, изменить или удалить данные.
Опасный код:
<?php
$id = $_GET['id'];
$query = "SELECT * FROM users WHERE id = $id";
$result = mysqli_query($conn, $query);
?>
Как защититься: Используйте подготовленные запросы (prepared statements) с PDO или MySQLi.
<?php
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id");
$stmt->execute(['id' => $_GET['id']]);
?>
2. XSS (Межсайтовый скриптинг)
XSS позволяет злоумышленнику внедрить вредоносный JavaScript-код на страницу, которая показывается другим пользователям. Это может привести к краже сессионных cookie или перенаправлению на фишинговые сайты.
Опасный код:
<?php
echo "Привет, " . $_GET['name'];
?>
Как защититься: Всегда экранируйте вывод с помощью htmlspecialchars().
<?php
echo "Привет, " . htmlspecialchars($_GET['name'], ENT_QUOTES, 'UTF-8');
?>
3. CSRF (Межсайтовая подделка запроса)
CSRF-атака заставляет авторизованного пользователя выполнить нежелательное действие на сайте (например, сменить пароль или перевести деньги). Атака происходит, когда пользователь переходит по ссылке на стороннем сайте.
Как защититься: Используйте CSRF-токены для всех форм и запросов, изменяющих состояние.
<?php
session_start();
if (empty($_SESSION['csrf_token'])) {
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
}
?>
<form method="POST">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<button type="submit">Удалить аккаунт</button>
</form>
4. Небезопасная работа с файлами
Позволяет злоумышленнику читать или записывать произвольные файлы на сервере. Например, через path traversal или загрузку PHP-шеллов.
Опасный код:
<?php
include($_GET['page'] . '.php');
?>
Как защититься: Никогда не используйте пользовательский ввод в путях к файлам. Используйте белые списки допустимых значений.
<?php
$allowed_pages = ['home', 'about', 'contact'];
$page = $_GET['page'] ?? 'home';
if (in_array($page, $allowed_pages)) {
include($page . '.php');
} else {
include('404.php');
}
?>
5. Недостатки аутентификации и управления сессиями
Слабые пароли, отсутствие защиты от перебора, фиксация сессии — всё это открывает путь к захвату учётных записей.
Рекомендации:
- Храните пароли только в хешированном виде через
password_hash(). - Используйте HTTPS для всех страниц с авторизацией.
- Регенерируйте ID сессии после входа:
session_regenerate_id(true). - Устанавливайте короткое время жизни сессии.
6. Небезопасное прямое указание объекта (IDOR)
IDOR возникает, когда пользователь может получить доступ к чужим данным, просто изменив ID в URL. Например, /profile.php?user_id=5.
Как защититься: Всегда проверяйте права доступа на сервере, а не только на клиенте.
<?php
$user_id = $_GET['user_id'];
if ($_SESSION['user_id'] !== $user_id && $_SESSION['role'] !== 'admin') {
http_response_code(403);
die('Доступ запрещён');
}
?>
7. Уязвимости при загрузке файлов
Позволяет загрузить вредоносный скрипт (например, PHP-шелл) вместо изображения. Это одна из самых частых причин взлома серверов.
Как защититься:
- Проверяйте MIME-тип файла на сервере (не полагайтесь на расширение).
- Переименовывайте файлы при сохранении.
- Храните загруженные файлы вне корня веб-сервера.
<?php
$allowed_types = ['image/jpeg', 'image/png'];
if (!in_array($_FILES['file']['type'], $allowed_types)) {
die('Недопустимый тип файла');
}
$new_name = bin2hex(random_bytes(16)) . '.jpg';
move_uploaded_file($_FILES['file']['tmp_name'], '/uploads/' . $new_name);
?>
8. Небезопасные десериализация объектов
Если ваше приложение использует unserialize() с пользовательскими данными, злоумышленник может выполнить произвольный код на сервере.
Как защититься: Никогда не передавайте непроверенные данные в unserialize(). Используйте JSON для обмена данными, где это возможно.
<?php