На 25-ом уроке мы переходим к одной из важных тем в веб-разработке, это безопасность. Если до этого мы учились создавать функциональные приложения, то сейчас научимся защищать их от злоумышленников. Безопасность это не «опциональная фича», а обязательный слой вашего кода. Даже небольшая ошибка может привести к утечке данных, взлому сайта или потере доверия пользователей. В этом уроке разберем основные принципы безопасности, а также научимся защищаться от XSS и CSRF-атак.
Основные принципы безопасности в PHP
Перед тем как углубляться в конкретные уязвимости, давайте обсудим общие правила, которые должен соблюдать каждый разработчик.
1. Валидация и санитизация данных
Валидация это проверка, что данные соответствуют ожидаемому формату (например, email содержит символ @
). Санитизация это очистка данных от опасных символов или преобразование их в безопасный формат.
Пример:
// Валидация email $email = $_POST['email']; if (!filter_var($email, FILTER_VALIDATE_EMAIL)) { die("Некорректный email!"); } // Санитизация строки $username = $_POST['username']; $clean_username = htmlspecialchars($username, ENT_QUOTES, 'UTF-8');
Правило:
- Все данные от пользователя (GET, POST, COOKIE) считаются недоверенными, пока не доказано обратное.
- Используйте встроенные функции PHP:
filter_var()
,htmlspecialchars()
,strip_tags()
.
2. Защита от SQL-инъекций
SQL-инъекции это атаки, когда злоумышленник внедряет вредоносный SQL-код через поля ввода (например, формы логина).
Как защищаться:
- Используйте подготовленные запросы (prepared statements) с PDO или MySQLi.
Пример с PDO:
$pdo = new PDO('mysql:host=localhost;dbname=test', 'user', 'password'); $stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email'); $stmt->execute(['email' => $email]); $user = $stmt->fetch();
Никогда не делайте так:
// Уязвимый код! $sql = "SELECT * FROM users WHERE email = '$email'"; $result = mysqli_query($conn, $sql);
3. Используйте HTTPS
Все современные сайты должны работать по протоколу HTTPS. Он шифрует данные между клиентом и сервером, защищая их от перехвата.
Как внедрить:
- Купите SSL-сертификат для домена.
- Настройте редирект с HTTP на HTTPS в файле
.htaccess
:
RewriteEngine On RewriteCond %{HTTPS} off RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
4. Работа с паролями
Хранить пароли в открытом виде — преступление. Используйте функции хеширования:
// Хеширование пароля $password = 'user_password_123'; $hash = password_hash($password, PASSWORD_DEFAULT); // Проверка пароля if (password_verify($password, $hash)) { echo 'Пароль верный!'; }
5. Обновляйте ПО
Устаревшие версии PHP, библиотек или CMS (например, WordPress) содержат уязвимости. Регулярно обновляйте все компоненты вашего проекта.
6. Принцип минимальных привилегий
Настройте права доступа к файлам и базам данных так, чтобы у пользователей и скриптов были только необходимые привилегии. Например, скрипт для выборки данных из БД не должен иметь прав на удаление таблиц.
Защита от XSS (Cross-Site Scripting)
XSS это уязвимость, позволяющая злоумышленнику внедрить вредоносный JavaScript-код на страницу вашего сайта. Это может привести к краже куки-файлов, перенаправлению пользователей на фишинговые сайты или подмене контента.
Пример уязвимого кода:
// Уязвимый код: выводим данные без обработки echo $_GET['search_query'];
Если злоумышленник передаст в search_query
строку <script>alert('XSS!');</script>
, на странице выполнится JavaScript.
Типы XSS-атак
- Reflected XSS — вредоносный скрипт встраивается в URL и выполняется сразу после перехода по ссылке.
- Stored XSS — скрипт сохраняется на сервере (например, в комментариях) и выполняется у всех пользователей.
- DOM-based XSS — атака происходит на стороне клиента через манипуляции с DOM.
Как защититься от XSS?
1. Санитизация выводимых данных
Все данные, выводимые в HTML, должны быть обработаны функцией htmlspecialchars()
.
Пример:
$user_comment = $_POST['comment']; // Очищаем данные перед выводом echo htmlspecialchars($user_comment, ENT_QUOTES, 'UTF-8');
2. Использование заголовков Content Security Policy (CSP)
CSP позволяет ограничить источники скриптов, стилей и других ресурсов.
Пример заголовка:
header("Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline'");
3. Фильтрация вводимых данных
Удаляйте опасные теги и атрибуты с помощью strip_tags()
или библиотек вроде HTML Purifier.
Пример:
$user_bio = $_POST['bio']; // Разрешаем только теги <b>, <i>, <p> $clean_bio = strip_tags($user_bio, '<b><i><p>');
Практическая задача: Защита формы комментариев
Условие:
Создайте форму для комментариев, где пользователь вводит имя и текст. Защитите форму от XSS.
Решение:
<?php if ($_SERVER['REQUEST_METHOD'] === 'POST') { $name = htmlspecialchars($_POST['name'], ENT_QUOTES, 'UTF-8'); $comment = htmlspecialchars($_POST['comment'], ENT_QUOTES, 'UTF-8'); // Сохраняем в базу данных или выводим echo "<div class='comment'><strong>$name</strong>: $comment</div>"; } ?> <form method="POST"> <input type="text" name="name" placeholder="Ваше имя"> <textarea name="comment" placeholder="Ваш комментарий"></textarea> <button type="submit">Отправить</button> </form>
Защита от CSRF (Cross-Site Request Forgery)
CSRF это атака, когда злоумышленник заставляет пользователя выполнить нежелательное действие на сайте, где он авторизован. Например, изменить пароль или сделать перевод денег.
Сценарий атаки:
- Пользователь авторизуется на сайте
example.com
. - Злоумышленник отправляет ему ссылку на свой сайт.
- На сайте злоумышленника есть форма, которая автоматически отправляет запрос на
example.com/change-password
. - Если сайт
example.com
не защищен от CSRF, запрос выполнится от имени пользователя.
Как защититься от CSRF?
1. CSRF-токены
Идея: генерировать уникальный токен для каждой формы и проверять его при отправке.
Шаги:
- При открытии формы генерируем токен и сохраняем его в сессии.
- Добавляем скрытое поле с токеном в форму.
- При отправке формы сравниваем токен из формы с токеном в сессии.
Пример:
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'] ?>"> <!-- Остальные поля --> </form> // Проверка токена if ($_POST['csrf_token'] !== $_SESSION['csrf_token']) { die('Неверный CSRF-токен!'); }
2. Заголовок Referer Check
Проверяйте, что запрос пришел с вашего домена:
if (!isset($_SERVER['HTTP_REFERER']) || parse_url($_SERVER['HTTP_REFERER'], PHP_URL_HOST) !== 'yourdomain.com') { die('Недействительный источник запроса!'); }
Но учтите: Заголовок Referer может отсутствовать или быть подделанным. Используйте этот метод только как дополнение к CSRF-токенам.
Практическая задача: Защита формы изменения пароля
Условие:
Создайте форму для изменения пароля с CSRF-защитой.
Решение:
<?php session_start(); // Генерация токена if (empty($_SESSION['csrf_token'])) { $_SESSION['csrf_token'] = bin2hex(random_bytes(32)); } if ($_SERVER['REQUEST_METHOD'] === 'POST') { // Проверка токена if (!isset($_POST['csrf_token']) || $_POST['csrf_token'] !== $_SESSION['csrf_token']) { die('Ошибка CSRF!'); } // Обработка данных $new_password = $_POST['new_password']; // ... смена пароля ... echo 'Пароль успешно изменен!'; } ?> <form method="POST"> <input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>"> <input type="password" name="new_password" placeholder="Новый пароль"> <button type="submit">Сменить пароль</button> </form>
Сегодня мы разобрали ключевые аспекты безопасности в PHP:
- Основные принципы: валидация, санитизация, защита от SQL-инъекций, HTTPS.
- Защита от XSS: экранирование вывода, CSP, фильтрация ввода.
- Защита от CSRF: использование токенов, проверка Referer.
Безопасность это процесс, а не разовое действие. Всегда тестируйте свой код, используйте инструменты вроде OWASP ZAP для поиска уязвимостей и следите за обновлениями.
Хотите узнать больше? Переходите к следующим урокам: полный курс по PHP для начинающих. Там мы разберем работу с базами данных, авторизацию и создание REST API!