Шпаргалка по валидации входных данных¶
Введение¶
Эта статья посвящена предоставлению чётких, простых и практичных рекомендаций по реализации функций безопасности валидации входных данных в ваших приложениях.
Цели валидации входных данных¶
Валидация входных данных выполняется для обеспечения того, что только правильно сформированные данные попадают в рабочий процесс информационной системы, предотвращая сохранение неверных данных в базе данных и сбои различных downstream-компонентов. Валидация входных данных должна происходить как можно раньше в потоке данных, предпочтительно сразу же, как только данные получены от внешней стороны.
Данные из всех потенциально ненадёжных источников должны проходить валидацию входных данных, включая не только клиентов, взаимодействующих с веб-интерфейсом в интернете, но и серверные каналы через экстранет от поставщиков, партнёров, вендоров или регуляторов, каждый из которых может быть скомпрометирован и начать отправлять неверно сформированные данные.
Валидация входных данных не должна использоваться как основной метод предотвращения XSS, SQL-инъекций и других атак, описанных в соответствующих шпаргалках, однако при правильной реализации она может значительно способствовать снижению их воздействия.
Стратегии валидации входных данных¶
Валидация входных данных должна применяться как на синтаксическом, так и на семантическом уровнях:
- Синтаксическая валидация должна обеспечивать правильность синтаксиса структурированных полей (например, SSN, дата, символ валюты).
- Семантическая валидация должна обеспечивать корректность значений в конкретном бизнес-контексте (например, дата начала предшествует дате окончания, цена находится в ожидаемом диапазоне).
Всегда рекомендуется предотвращать атаки как можно раньше при обработке запроса пользователя (злоумышленника). Валидация входных данных может использоваться для обнаружения несанкционированного ввода до его обработки приложением.
Реализация валидации входных данных¶
Валидацию входных данных можно реализовать с использованием любой техники программирования, позволяющей эффективно применять синтаксическую и семантическую корректность, например:
- Валидаторы типов данных, встроенные во фреймворки веб-приложений (такие как Django Validators, Apache Commons Validators и т.д.).
- Валидация по JSON Schema и XML Schema (XSD) для входных данных в этих форматах.
- Преобразование типов (например,
Integer.parseInt()в Java,int()в Python) со строгой обработкой исключений. - Проверка диапазона минимальных и максимальных значений для числовых параметров и дат, проверка минимальной и максимальной длины для строк.
- Массив допустимых значений для небольших наборов строковых параметров (например, дни недели).
- Регулярные выражения для любых других структурированных данных, охватывающих всю входную строку
(^...$), и без использования подстановочного символа «любой символ» (такого как.или\S). - Составление списков запрещённых известных опасных паттернов может использоваться как дополнительный уровень защиты, но должно дополнять — а не заменять — список разрешённых паттернов, помогая перехватывать некоторые часто наблюдаемые атаки или паттерны без опоры на это как на основной метод валидации.
Список разрешённых vs список запрещённых¶
Распространённой ошибкой является использование валидации на основе списка запрещённых для попытки обнаружения потенциально опасных символов и паттернов, таких как апостроф ', строка 1=1 или тег <script>. Однако это принципиально ошибочный подход, поскольку злоумышленнику тривиально легко обойти такие фильтры.
Кроме того, подобные фильтры часто блокируют авторизованный ввод, например O'Brian, где символ ' является полностью законным. Подробнее об обходе XSS-фильтров см. эту страницу wiki.
Хотя список запрещённых может быть полезен как дополнительный уровень защиты для перехвата некоторых распространённых вредоносных паттернов, не следует полагаться на него как на основной метод. Список разрешённых остаётся более надёжным и безопасным подходом для предотвращения потенциально вредоносного ввода.
Валидация на основе списка разрешённых подходит для всех полей ввода, предоставляемых пользователем. Валидация на основе списка разрешённых предполагает точное определение того, что РАЗРЕШЕНО, и по определению всё остальное не разрешено.
Если данные хорошо структурированы — например, даты, номера социального страхования, почтовые индексы, адреса электронной почты — разработчик должен иметь возможность определить очень сильный паттерн валидации, обычно на основе регулярных выражений, для проверки таких входных данных.
Если поле ввода принимает значения из фиксированного набора параметров, например из выпадающего списка или переключателей, входные данные должны точно соответствовать одному из значений, предложенных пользователю. Любой сбой при проверке значения по этому дискретному списку параметров на стороне сервера является событием высокого уровня безопасности и должно быть зафиксировано как событие высокой серьёзности, поскольку указывает на то, что злоумышленник вмешивается в клиентский код.
Валидация свободного текста в Unicode¶
Свободный текст, особенно с символами Unicode, воспринимается как сложный для валидации из-за относительно большого пространства символов, которые необходимо разрешить.
Также именно свободный текст подчёркивает важность правильного контекстно-зависимого кодирования вывода и достаточно ясно демонстрирует, что валидация входных данных не является основной защитой от межсайтового скриптинга. Если ваши пользователи хотят ввести апостроф ' или знак «меньше» < в поле комментария, у них может быть вполне законная причина для этого, и задача приложения — правильно обрабатывать это на протяжении всего жизненного цикла данных.
Основные средства валидации входных данных для свободного текста должны быть:
- Нормализация: Обеспечение использования канонического кодирования по всему тексту и отсутствия недопустимых символов.
- Список разрешённых категорий символов: Unicode позволяет перечислять категории, такие как «десятичные цифры» или «буквы», которые охватывают не только латинский алфавит, но и различные другие системы письма, используемые по всему миру (например, арабский, кириллица, идеографы CJK и т.д.).
- Список разрешённых отдельных символов: Если вы разрешаете буквы и идеографы в именах и также хотите разрешить апостроф
'для ирландских имён, но не хотите разрешать всю категорию пунктуации.
Ссылки:
- Валидация свободного текста в Unicode на Python
- UAX 31: Синтаксис идентификаторов и паттернов Unicode
- UAX 15: Формы нормализации Unicode
- UAX 24: Свойство скрипта Unicode
Регулярные выражения (Regex)¶
Разработка регулярных выражений может быть сложной и выходит далеко за рамки этой шпаргалки.
В интернете есть множество ресурсов о том, как писать регулярные выражения, в том числе этот сайт и Репозиторий валидационных Regex OWASP.
При разработке регулярных выражений необходимо помнить об атаках типа ReDoS (Regex Denial of Service). Эти атаки приводят к тому, что программа, использующая плохо разработанное регулярное выражение, работает очень медленно и долго потребляет ресурсы процессора.
В итоге валидация входных данных должна:
- Применяться ко всем входным данным, как минимум.
- Определять допустимый набор символов.
- Определять минимальную и максимальную длину для данных (например,
{1,25}).
Примеры регулярных выражений для списков разрешённых¶
Валидация почтового индекса США (5 цифр плюс необязательный -4):
^\d{5}(-\d{4})?$
Валидация выбора штата США из выпадающего меню:
^(AA|AE|AP|AL|AK|AS|AZ|AR|CA|CO|CT|DE|DC|FM|FL|GA|GU|
HI|ID|IL|IN|IA|KS|KY|LA|ME|MH|MD|MA|MI|MN|MS|MO|MT|NE|
NV|NH|NJ|NM|NY|NC|ND|MP|OH|OK|OR|PW|PA|PR|RI|SC|SD|TN|
TX|UT|VT|VI|VA|WA|WV|WI|WY)$
Пример использования Java Regex:
Пример валидации параметра «zip» с использованием регулярного выражения.
private static final Pattern zipPattern = Pattern.compile("^\d{5}(-\d{4})?$");
public void doPost( HttpServletRequest request, HttpServletResponse response) {
try {
String zipCode = request.getParameter( "zip" );
if ( !zipPattern.matcher( zipCode ).matches() ) {
throw new YourValidationException( "Improper zipcode format." );
}
// делайте что нужно здесь, после валидации..
} catch(YourValidationException e ) {
response.sendError( response.SC_BAD_REQUEST, e.getMessage() );
}
}
Некоторые валидаторы на основе списков разрешённых также предопределены в различных пакетах с открытым исходным кодом. Например:
Валидация на стороне клиента и на стороне сервера¶
Валидация входных данных должна быть реализована на стороне сервера до обработки каких-либо данных функциями приложения, поскольку любая валидация на стороне клиента на JavaScript может быть обойдена злоумышленником, который отключит JavaScript или использует веб-прокси. Реализация как клиентской валидации на JavaScript для UX, так и серверной валидации для безопасности является рекомендуемым подходом, использующим каждый из них в соответствии с их сильными сторонами.
Валидация богатого пользовательского контента¶
Очень сложно валидировать богатый контент, отправляемый пользователем. Подробнее см. в шпаргалке по XSS о Санитизации HTML-разметки с помощью библиотеки для этой задачи.
Предотвращение XSS и Content Security Policy¶
Все пользовательские данные должны быть закодированы при возврате в HTML-страницу для предотвращения выполнения вредоносных данных (например, XSS). Например, <script> будет возвращён как <script>.
Тип кодирования зависит от контекста страницы, в который вставляются данные, контролируемые пользователем. Например, кодирование HTML-сущностей подходит для данных, размещаемых в теле HTML. Однако пользовательские данные, размещённые в скрипте, требуют специфического для JavaScript кодирования вывода.
Подробная информация о предотвращении XSS: Шпаргалка OWASP по предотвращению XSS.
Валидация загружаемых файлов¶
Многие сайты разрешают пользователям загружать файлы, например фотографию профиля или другие. Этот раздел помогает реализовать эту функцию безопасно.
Ознакомьтесь со Шпаргалкой по загрузке файлов.
Проверка загрузки¶
- Используйте валидацию входных данных для обеспечения того, что имя загружаемого файла использует ожидаемый тип расширения.
- Убедитесь, что загружаемый файл не превышает определённый максимальный размер файла.
- Если сайт поддерживает загрузку ZIP-файлов, выполните проверку перед разархивированием файла. Проверка включает целевой путь, уровень сжатия, оценочный размер после разархивирования.
Хранение загруженных файлов¶
- Используйте новое имя файла для его хранения в ОС. Не используйте какой-либо текст, контролируемый пользователем, для этого имени файла или для временного имени файла.
- При загрузке файла в интернет рекомендуется переименовать файл при хранении. Например, загруженный файл называется test.JPG, переименуйте его в JAI1287uaisdjhf.JPG со случайным именем. Цель этого — предотвратить риски прямого доступа к файлу и неоднозначного имени файла для обхода фильтра, например
test.jpg;.asp или /../../../../../test.jpg. - Загруженные файлы должны быть проанализированы на наличие вредоносного содержимого (антивредоносное ПО, статический анализ и т.д.).
- Клиент не должен иметь возможности указывать путь к файлу; он должен определяться сервером.
Публичная раздача загруженного контента¶
- Убедитесь, что загруженные изображения раздаются с правильным типом содержимого (например,
image/jpeg,application/x-xpinstall).
Остерегайтесь определённых типов файлов¶
Функция загрузки должна использовать подход на основе списка разрешённых, допуская только определённые типы и расширения файлов. Тем не менее важно учитывать следующие типы файлов, которые, если их разрешить, могут привести к уязвимостям безопасности:
- crossdomain.xml / clientaccesspolicy.xml: позволяют кросс-доменную загрузку данных во Flash, Java и Silverlight. При разрешении на сайтах с аутентификацией это может допускать кражу данных между доменами и CSRF-атаки. Обратите внимание, что это может быть довольно сложным в зависимости от конкретной версии плагина, поэтому лучше всего просто запрещать файлы с именами «crossdomain.xml» или «clientaccesspolicy.xml».
- .htaccess и .htpasswd: предоставляют параметры конфигурации сервера для каждого каталога и не должны разрешаться. См. документацию HTACCESS.
- Не рекомендуется разрешать выполняемые веб-скриптом файлы, такие как
aspx, asp, css, swf, xhtml, rhtml, shtml, jsp, js, pl, php, cgi.
Проверка загружаемых изображений¶
- Используйте библиотеки перезаписи изображений для проверки корректности изображения и удаления посторонних данных.
- Устанавливайте расширение хранимого изображения как допустимое расширение изображения, основанное на обнаруженном типе содержимого изображения при обработке изображений (то есть не доверяйте заголовку из загрузки).
- Убедитесь, что обнаруженный тип содержимого изображения входит в список определённых типов изображений (jpg, PNG и т.д.).
Валидация адресов электронной почты¶
Синтаксическая валидация¶
Формат адресов электронной почты определён в RFC 5321 и значительно сложнее, чем большинство людей осознаёт. Например, все следующие адреса считаются допустимыми:
"><script>alert(1);</script>"@example.orguser+subaddress@example.orguser@[IPv6:2001:db8::1]" "@example.org
Правильный синтаксический разбор адресов электронной почты с помощью регулярных выражений очень сложен, хотя существует ряд публично доступных документов о regex.
Главный нюанс состоит в том, что, хотя RFC определяет очень гибкий формат для адресов электронной почты, большинство реальных реализаций (таких как почтовые серверы) используют значительно более ограниченный формат адресов, то есть они отклоняют адреса, которые технически являются допустимыми. Хотя такие адреса могут быть технически корректными, они бесполезны, если ваше приложение не сможет фактически отправлять на них электронные письма.
Поэтому лучший способ валидации адресов электронной почты — выполнить некоторую базовую начальную валидацию, а затем передать адрес на почтовый сервер и перехватить исключение, если он отклоняет его. Это означает, что приложение может быть уверено, что его почтовый сервер может отправлять электронные письма на все принимаемые адреса. Начальная валидация может быть такой простой, как:
- Адрес электронной почты содержит две части, разделённые символом
@. - Адрес электронной почты не содержит опасных символов (таких как обратные кавычки, одинарные или двойные кавычки или нулевые байты).
- Какие именно символы являются опасными, зависит от того, как адрес будет использоваться (отображён на странице, вставлен в базу данных и т.д.).
- Доменная часть содержит только буквы, цифры, дефисы (
-) и точки (.). - Адрес электронной почты имеет разумную длину:
- Локальная часть (перед
@) должна быть не более 63 символов. - Общая длина должна быть не более 254 символов.
- Локальная часть (перед
Семантическая валидация¶
Семантическая валидация касается определения того, является ли адрес электронной почты корректным и законным. Наиболее распространённый способ сделать это — отправить пользователю электронное письмо и потребовать, чтобы он перешёл по ссылке в письме или ввёл код, отправленный ему. Это обеспечивает базовый уровень гарантии того, что:
- Адрес электронной почты корректен.
- Приложение может успешно отправлять на него электронные письма.
- Пользователь имеет доступ к почтовому ящику.
Ссылки, отправляемые пользователям для подтверждения владения, должны содержать токен, который:
- Имеет длину не менее 32 символов.
- Сгенерирован с использованием безопасного источника случайности.
- Является одноразовым.
- Ограничен по времени (например, истекает через восемь часов).
После подтверждения владения адресом электронной почты пользователь должен пройти аутентификацию в приложении обычным способом.
Одноразовые адреса электронной почты¶
В некоторых случаях пользователи могут не захотеть предоставлять свой реальный адрес электронной почты при регистрации в приложении и вместо этого предоставят одноразовый адрес. Это публично доступные адреса, не требующие аутентификации пользователя, и они обычно используются для уменьшения количества спама, получаемого на основные адреса электронной почты пользователей.
Блокировка одноразовых адресов электронной почты практически невозможна, поскольку существует большое количество сайтов, предлагающих эти услуги, и ежедневно создаются новые домены. Существует ряд публично доступных и коммерческих списков известных одноразовых доменов, но они всегда будут неполными.
Если эти списки используются для блокировки одноразовых адресов электронной почты, пользователю следует показать сообщение, объясняющее, почему они заблокированы (хотя, скорее всего, они просто найдут другого одноразового провайдера вместо того, чтобы предоставить свой законный адрес).
Если необходимо заблокировать одноразовые адреса электронной почты, то регистрация должна разрешаться только от конкретно разрешённых провайдеров электронной почты. Однако если это включает публичных провайдеров, таких как Google или Yahoo, пользователи могут просто зарегистрировать у них собственный одноразовый адрес.
Субадресация¶
Субадресация позволяет пользователю указывать метку в локальной части адреса электронной почты (перед знаком @), которая будет игнорироваться почтовым сервером. Например, если домен example.org поддерживает субадресацию, следующие адреса электронной почты эквивалентны:
user@example.orguser+site1@example.orguser+site2@example.org
Многие почтовые провайдеры (например, Microsoft Exchange) не поддерживают субадресацию. Наиболее известным провайдером, поддерживающим её, является Gmail, хотя есть и многие другие.
Некоторые пользователи будут использовать разную метку для каждого сайта, на котором они регистрируются, чтобы в случае получения спама на один из субадресов они могли определить, какой сайт утёк или продал их адрес электронной почты.
Поскольку это может позволять пользователям регистрировать несколько аккаунтов с одним адресом электронной почты, некоторые сайты могут захотеть заблокировать субадресацию, удаляя всё между символами + и @. Это в целом не рекомендуется, поскольку предполагает, что владелец сайта либо не осведомлён о субадресации, либо желает помешать пользователям идентифицировать их при утечке или продаже адресов электронной почты. Кроме того, это можно тривиально обойти, используя одноразовые адреса электронной почты или просто зарегистрировав несколько адресов электронной почты у доверенного провайдера.