Данные от посетителя - экранирование, проверка, сеанс
Принимаем данные от посетителя так, чтобы чужой скрипт не оказался на странице, в запросе к базе или в каталоге загрузок.
Решение
Экранируем при выводе:
echo htmlspecialcharsbx($item['NAME']); // в текст страницыecho '<a title="' . htmlspecialcharsbx($title) . '">'; // и в атрибут тожеecho \Bitrix\Main\Text\HtmlFilter::encode($comment); // то же самое в D7// экранируют на выводе: в базе значение остаётся таким, каким его ввелиЭкранируют при выводе, а не при сохранении. Значение в базе остаётся исходным, и его можно показать в письме, отдать в выгрузку и сравнить с приехавшим из обмена без обратного разбора.
Подставляем значения в запрос безопасно:
$rows = SyncTable::getList(['filter' => ['=CODE' => $code]])->fetchAll(); // ORM сама$id = (int)$request->get('id'); // числа приводим$helper = \Bitrix\Main\Application::getConnection()->getSqlHelper();$sql = "SELECT * FROM b_vendor WHERE CODE = '" . $helper->forSql($code) . "'";Значения подставляют средствами платформы, а не склейкой строк. Фильтр ORM готовит значение сам, а в редком случае прямого запроса его пропускают через подготовку и приводят числа к числам.
Проверяем признак сеанса у формы:
// в формеecho bitrix_sessid_post();// в обработчикеif (!check_bitrix_sessid()) { ShowError('Форма устарела, обновите страницу'); return;}Своя форма проверяет признак сеанса сама. Без него страницу с чужого сайта можно заставить отправить запрос от имени вошедшего сотрудника, и он об этом не узнает.
Проверяем загружаемый файл:
$file = $request->getFile('doc');$mime = mime_content_type($file['tmp_name']); // тип по содержимомуif (!in_array($mime, ['application/pdf', 'image/jpeg'], true) || $file['size'] > 5_000_000) { return 'Такой файл принять нельзя';}// имя файла и его расширение задаёт посетитель: доверять им нельзяТип файла определяют по содержимому, а не по имени. Расширение приходит от посетителя, и картинка с чужим скриптом внутри отличается от настоящей только содержимым.
Данные от посетителя проверяют на сервере, даже если их проверил браузер. Проверка на странице - это удобство для человека, а не защита: запрос отправляют и мимо формы.
HTML от контент-менеджера и HTML от покупателя - разные вещи. Первому его иногда разрешают, второму - никогда, и оба случая решаются на выводе, а не на входе.
Ошибку проверки стоит показывать понятной строкой. Молчаливый отказ приводит человека в поддержку, а «недопустимое значение поля» без имени поля - туда же.
Типичные проблемы
На странице появились чужие символы вместо кавычек.
Значение экранировано при сохранении и ещё раз при выводе. Экранирование делают только на выводе, и больше нигде в остальном коде проекта.
Фильтр по числу возвращает не то.
Значение пришло строкой и подставлено в запрос как есть. Числа приводят к числам сразу при чтении запроса.
Форма срабатывает с чужого сайта.
Признак сеанса в самой форме обработчиком никак и не проверяется вовсе нигде. Проверку признака сеанса ставят до всякой работы с пришедшими от посетителя данными.
В каталоге загрузок оказался скрипт.
Тип файла проверен по расширению из его имени. Имя задаёт посетитель, а тип определяют по содержимому.
Значение ломает вёрстку в атрибуте.
Вывод в атрибут сделан вообще без экранирования кавычек внутри самого этого значения. В атрибуте значение экранируют ровно так же, как и в обычном тексте.
Частые вопросы
Экранировать при сохранении или при выводе?
При выводе: в базе значение должно остаться исходным. Иначе оно испортится в письмах и выгрузках.
Что делать с HTML от контент-менеджера?
Разрешать точечно и только доверенным группам. Для покупателя HTML не разрешают вовсе.
Нужна ли проверка сеанса на запросах из браузера?
Да, для всего, что меняет данные. Штатные контроллеры делают это сами, свой файл - нет.
Достаточно ли проверки на странице?
Нет: запрос легко отправить мимо формы. Проверка на сервере обязательна всегда.
Смежное
- Защита сайта на практике - оглавление подтемы
- Проверка кода перед релизом: чек-лист безопасности проекта - короткий список проверок релиза
- Проверка входных данных атрибутами: правила, результат, контроллер - штатные правила проверки данных
- Шифрование данных в базе: ключ, поле, миграция - хранение секретов в базе
- Базовая защита сайта: обновления, доступы, проактивная защита - что закрывают на уровне сайта
- Файлы из кода: сохранение, уменьшенные копии, удаление - куда попадает принятый файл
- AJAX-запрос: контроллер, свой файл и ответ в JSON - где эта проверка нужна каждый раз
- Загрузка по ссылке от посетителя: защита от запросов во внутреннюю сеть - когда посетитель присылает не файл, а ссылку
- Безопасность - устройство темы целиком
- Отзывы о товаре: хранение, модерация, средняя оценка - типовой приём текста от посетителя
- Ошибка проверки источника запроса: разбор причин - когда проверка отвергает свой же запрос