Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Безопасность

Сайт доступен всему интернету, и рано или поздно кто-нибудь попробует его взломать. Большинство взломов используют одну и ту же ошибку: программа доверяет данным, которые прислал пользователь. Главное правило — любые данные извне считаются опасными, пока не проверены.

XSS и экранирование

Если вывести на страницу текст от пользователя как есть, в него можно вставить скрипт, который выполнится у других посетителей. Это называется XSS. Функция htmlspecialchars() превращает опасные символы в безвредные:

<?php
 
$comment = '<script>alert("Взлом")</script>';
 
echo htmlspecialchars($comment);

Браузер покажет текст скрипта буквами, но не выполнит его. Экранируйте всё, что выводите в HTML.

SQL-инъекции

Если склеить запрос к базе из строк, пользователь сможет изменить его смысл. Защита — подготовленные запросы из урока о базе данных:

<?php
 
$pdo = new PDO('sqlite::memory:');
$pdo->exec('CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT)');
$pdo->exec("INSERT INTO users (email) VALUES ('anna@example.com')");
 
$email = "' OR '1'='1";
 
$statement = $pdo->prepare('SELECT COUNT(*) FROM users WHERE email = ?');
$statement->execute([$email]);
 
echo $statement->fetchColumn(); // 0

Значение передаётся отдельно от запроса, поэтому попытка взлома ищется как обычная строка и ничего не находит.

Хеширование паролей

Пароли нельзя хранить в открытом виде: при утечке базы они достанутся злоумышленнику. Хранят хеш — необратимый отпечаток пароля:

<?php
 
$hash = password_hash('секретный-пароль', PASSWORD_DEFAULT);
 
var_dump(password_verify('секретный-пароль', $hash)); // bool(true)
var_dump(password_verify('другой-пароль', $hash));    // bool(false)

password_hash() каждый раз даёт разный результат для одного и того же пароля — так и должно быть. Проверяет пароль только password_verify().

CSRF-токен

Чужой сайт может незаметно отправить форму от имени вошедшего пользователя. Чтобы отличить свою форму от чужой, в неё добавляют случайный токен и сверяют его при обработке:

<?php
 
session_start();
 
$_SESSION['csrf'] ??= bin2hex(random_bytes(32));
 
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    if (!hash_equals($_SESSION['csrf'], $_POST['csrf'] ?? '')) {
        http_response_code(419);
        exit('Неверный токен');
    }
}

В форму токен кладут скрытым полем: <input type="hidden" name="csrf" value="...">.

Проверка входных данных

Проверяйте тип и формат всего, что приходит от пользователя. Для этого есть функция filter_var():

<?php
 
var_dump(filter_var('anna@example.com', FILTER_VALIDATE_EMAIL) !== false); // bool(true)
var_dump(filter_var('42', FILTER_VALIDATE_INT));                           // int(42)
var_dump(filter_var('сорок два', FILTER_VALIDATE_INT));                    // bool(false)

Чек-лист

УгрозаЗащита
XSShtmlspecialchars() при выводе
SQL-инъекцияПодготовленные запросы
Кража паролейpassword_hash() и password_verify()
CSRFТокен в каждой форме
Перехват данныхHTTPS на всём сайте

Частая ошибка

Проверка в браузере — атрибут required, JavaScript — нужна для удобства, а не для защиты. Запрос можно отправить в обход формы, поэтому все проверки обязательно повторяют на сервере.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.