WordPress как на ладони

Функции проверки и очистки (по типам данных)

Небезопасные данные могут прийти из разных источников: пользователи, другие сайты, ваша база данных и т.д. Такие данные должны проверяться/очищаться (проходить валидацию) при получении или при выводе на экран. Например очистка нужна при создании SQL запроса. Новички часто недооценивают эту «не обязательную» процедуру - ведь все же и так работает.

WordPress предлагает кучу функций, чтобы грамотно проверить и очистить данные. В этой статье я собрал все функции валидации - для какого типа данных какие PHP функции можно использовать.

Многие функции очистки вводимых данных подходят и для очистки принимаемых данных.

Читайте также про принципы очистки данных

Функции валидации (по типам передаваемых данных)

Числа

(int) $int
intval( $int )
Превращает $int в целое число. Это функции PHP.
(float) $int
floatval( $int )
Превращает $int дробное число (число с плавающей точкой). Это функция PHP.
absint( $int )
Превращает $int в целое положительное число. Т.е. если передать -5 вернет 5.

Массивы

array_map( 'absint', $array )

Превращает все значения $array в неотрицательные числа. array_map() — это PHP функция.

'absint' — это функция absint(), которая применяется к значением массива $array. Замените 'absint' на любое другое название функции и все значения массива будут обработаны этой функцией.

map_deep( $value, $callback ) (с версии 4.4)
Применяет указанную функцию к значениям переданного массива или свойствам объекта. Рекурсивная функция.

Строки (вывод на экран)

esc_html( $text )
Заменяет спецсимволы на HTML сущности в переданном тексте, возвращает отформатированный текст. Заменяются следующие символы: ", ', &, <, >.
esc_attr( $text )
Преобразует знаки " ' < > & в html сущности. Не создает двойного преобразования.
esc_textarea( $text )
Очищает текст/строку для использования в html теге textarea.
esc_url( $url, $protocols, $_context )
Очищает УРЛ для использования его в тексте, изменяет неправильные и удаляет опасные символы.
esc_sql( $data )
Подготавливает строку к использованию в SQL запросе. Защищает от SQL инъекций. Может принимать массив строк для обработки.
esc_js( $text )
Подготавливает строку для использования в JavaScript. Чтобы уберечь от ошибок: экранирует кавычки, меняет символы " <> & на спецсимволы HTML и поправляет окончание строки.
esc_html__( $text, $domain )
Переводит строку заменяя в ней спецсимволы на HTML сущности. Возвращает текст, которой можно отображать в HTML как HTML код.
esc_html_e( $text, $domain )
Переводит (локализует) строку и очищает её для вывода на экран - заменяет в ней спецсимволы на HTML сущности.
esc_attr__( $text, $domain )
Переводит указанную строку и обрабатывает её функцией esc_attr().
esc_attr_e( $text, $domain )
Выводит переведенный текст очищенный функцией esc_attr().

Строки (принимаемые данные)

WordPress функции для очистки

В WordPress есть ряд функций с помощью которых можно очистить строки которые приходят в виде данных. Почти все такие функции начинаются с префикса sanitize_:

sanitize_key( $key )
Очищает строку, чтобы использовать её как ключ. Ключи используются как разные внутренние ID. Оставит только: a-z0-9_-.
sanitize_text_field( $text )
Очищает строку передаваемую из поля input (обычно при сохранении в базу данных) или при получении из БД. Удаляет почти все оставляя только текст: без HTML тегов, переносов строк и т.д.
sanitize_textarea_field( $text )
Очищает строку передаваемую из поля textarea (при сохранении в базу данных) или при получении из БД. Удаляет все HTML символы, табуляции, HTML сущности и т.д. Оставляет чистый текст. С версии WP 4.7.
sanitize_html_class( $text )
Подготавливает текст для использования его в html атрибуте class: удаляет все неподходящие символы.
sanitize_title( $title )
Используется для создания ярлыков (slug) записей/рубрик.
sanitize_title_with_dashes( $title )
Очищает заголовок, заменяя пробелы на (-).
sanitize_user( $username, $strict )
Очищает имя пользователя (логин, username), удаляя небезопасные символы.
$strict = true — значит в именах будут доступны только: [a-zA-Z0-9 _*.-].
sanitize_file_name( $filename )
Очищает название файла, заменяя пробелы на '_' и удаляя недопустимые символы.
sanitize_mime_type( $mime_type )
Очищает строку для использования её как MIME тип. Удаляет все кроме -+*.a-zA-Z0-9/.
sanitize_term_field( $field, $value, $term_id, $taxonomy, $context )
Очищает значение термина (рубрики) для использования в тексте.
sanitize_post_field( $field, $value, $post_id, $context )
Очищает указанное значение указанного поля поста.
sanitize_option( $option, $value )
Очищает значения различных опций, в зависимости от типа опции.
PHP функции для проверки
ctype_alnum( $text )

Проверят состоит ли переданная строка только из чисел и букв.

ctype_alnum( 'AbCd1zyZ9' ); // true
ctype_alnum( 'foo!#$bar' ); // false
ctype_alnum( 'foo bar' );   // false
strlen( $string )
mb_strlen( $string )
Для проверки того, что строка имеет ожидаемое количество символов.
preg_match( $pattern, $subject, $match )

Проверка подстроки в строке по регулярному выражению (маске).

// проверка по символам: могут быть только 0-9.-
if ( preg_match( '/[^0-9.-]/', $data ) ) {
	wp_die( 'Неверный формат' );
}
strpos( $haystack, $needle )

Для проверки на предмет наличия подстроки в другой строке.

$mystring = 'abc';
$findme   = 'a';
if( false !== strpos( $mystring, $findme ) ){
	echo 'a найдена в abc';
}

Строки (HTML)

wp_kses( $string, $allowed_html )

Чистит строку, оставляя в ней только указанные/допустимые HTML теги, их атрибуты и значения атрибутов. Следует использовать при выводе текста где могут быть небезопасные HTML теги.

Для удобного использования, у wp_kses() есть обертки. Например, чтобы не передавать массив допустимых тегов, а использовать базовый с минимальным набором допустимых html тегов, можно использовать:

wp_kses_post( $text ) — удалить недопустимые для записи теги, учитывая права текущего пользователя.

wp_filter_post_kses( $text ) — тоже что wp_kses_post(), только ожидает экранированные данные.

wp_kses_data( $text ) — если нужно ограничить допустимые теги до минимума, как это делается в комментариях. Ожидает не экранированный текст.

wp_filter_kses( $text ) — тоже что и wp_kses_data(), только ожидает экранированную строку.

wp_filter_nohtml_kses( $text ) — удаляет все HTML теги из переданного текста. Ожидает экранированную строку. Возвращает очищенный текст.

Фильтры wp_kses() медленные и съедают много ресурсов, поэтому не стоит использовать их каждый раз при выводе данных, лучше очищать ими входящие данные, например перед записью текста в базу данных. Такая очистка например запускается WordPress при добавлении комментария или записи.

wp_rel_nofollow( $html )
Добавляет rel="nofollow" ко всем элементам <a> в переданном HTML тексте.
wp_kses_allowed_html( $context )
Возвращает массив допустимых HTML элементов для указанного контекста. См. описание...
balanceTags( $html )
force_balance_tags( $html )
Закрывает незакрытые HTML теги, чтобы вывод не вызвал ошибку.
balanceTags() срабатывает только если включена спец настройка в настройках сайта «WordPress должен исправлять некорректный XHTML-код автоматически.». А вот force_balance_tags() срабатывает всегда!
tag_escape( $tag_name )
Очищает название HTML тега. Удаляет все символы кроме a-zA-Z0-9_:. Переводит строку в нижний регистр.
sanitize_html_class( $class, $fallback )
Подготавливает текст для использования его в html атрибуте class: удаляет все неподходящие символы. Удаляет всё кроме A-Za-z0-9_-. Если результат пустой, то в $fallback можно установить значение по умолчанию.
wp_strip_all_tags( $string, $remove_breaks )

Удаляет все HTML теги из строки. script и style удаляются вместе с содержимым.

Разница со strip_tags() - теги <script> и <style> удаляются вместе с содержимым. Например:

strip_tags( '<script>something</script>' );        // something
wp_strip_all_tags( '<script>something</script>' ); // пусто ''
strip_tags()
Удаляет все HTML теги из строки.

Boolen (логические)

wp_validate_boolean( $val )

Превращает значение указанной переменной в логические true или false.

Альтернатива конструкции: filter_var( $var, FILTER_VALIDATE_BOOLEAN ).

wp_validate_boolean( 'false' ); // bool(false)
bool_from_yn( $val )
Проверяет "да" или "нет" (yes/no). Нужно указывать 'y' или 'Y', чтобы функция вернула true.

Email

is_email( $email )
Проверяет, является ли переданная строка e-mail адресом. Вернет true или false.
sanitize_email( $email )
Очищает e-mail: убирает недопустимые символы из e-mail адреса.

URL (ссылки)

esc_url( $url )

Очищает УРЛ для использования его в тексте, изменяет неправильные и удаляет опасные символы. Не пропускает URL, если в нем указан протокол не из белого списка (http, https, ftp, ftps, mailto, news, irc, gopher, nntp, feed, и telnet).

Используйте эту очистку при выводе любого URL на экран (в тексте, в атрибутах или где-либо еще).

Также, эта функция кодирует некоторые спец. символы, поэтому её рекомендуется использовать при генерации строк для (X)HTML или XML документов. Кодирует амперсанд (&) и одинарные кавычки в их числовые сущности (&#038, &#039).

sanitize_url( $url )
esc_url_raw( $url )

Очищает УРЛ для безопасного использования. В отличии от esc_url(), не очищает для безопасного вывода на экран. Используйте, когда нужно получить НЕкодированный URL, например: в запросах к БД, при редиректах, в HTTP запросах.

sanitize_url() - это алиас esc_url_raw().

urlencode( $url )

PHP функция, которая кодирует URL, так что его можно использовать как параметр запроса. Т.е. заменяет все возможные символы URL (&, /, пробел и т.д.) на их сущности. Чтобы вернуть такой URL в прежнее состояние, используйте urldecode().

Эта функцию используется не для вывода URL на экран, а для использования URL где-то в запросе, чтобы PHP не мог интерпретировать строку как URL. К примеру, если обработать http://example.com/one, то получим http%3A%2F%2Fexample.com%2Fone — это уже не URL, а строка с набором символов...

urlencode_deep( $array )
Обрабатывает все элементы переданного массива функцией urlencode().

XML

XML документы, в отличии от HTML, понимают лишь некоторые спецсимволы: &apos;, &amp;, &gt;, &lt;, &quot;. Поэтому для вывода текста для XML документов, в WordPress есть функция:

ent2ncr( $text )
Конвертирует строковые сущности в их числовые значения: &rsquo; станет &#8217;.

JavaScript

esc_js( $text )

Подготавливает строку для использования в JavaScript. Полезна при использовании однострочного JS кода (в HTML атрибутах, например onclick='…'). Строка должна быть заключена в одинарные кавычки.

Или вот такой пример:

<script>
	var = '<?php echo esc_js( $js ); ?>';
</script>

Файловая система

validate_file( $filename, $allowed_files )

Используется для защиты от атаки «выход за пределы каталога» (directory traversal attacks). Или, когда нужно проверить находится ли файл в белом списке (параметр $allowed_files).

Возвращает: 0 - если проверка пройдена. И вернет число ошибки (> 0), если есть ошибка. Это нестандартный подход, поэтому будьте внимательны при написании кода!

Для проверки функции нужно передавать только абсолютные пути без ../ или ./. Например /etc/hosts пройдет проверку, а ./hosts - не пройдет.

Атака выхода за директорию выглядит примерно так, отправляется такой запрос: http://test.ru/?dir=../../Windows/system.ini и если сервер или код не защищен, то можно получить содержимое файла system.ini.
Такие атаки не частые судя по информации из google и относятся к средней степени риска. Но игнорировать их все же не стоит...

HTTP Headers (URL, редиректы)

Атаки с разделением ответов HTTP (header splitting attacks) создаются на стороне клиента, а не сервера и поэтому их очень трудно выловить. WordPress иногда добавляет данные отправляемые пользователем в HTTP заголовок, но чтобы избежать такие атаки, передаваемые заголовки проверяются в белом списке и все опасное вырезается.

Для очистки потенциально опасных заголовков в WordPress предусмотрены 2 функции редиректа:

wp_redirect( $location, $status = 302 )
Безопасный способ перенаправить пользователя на любой URL. Функция проверяет чтобы итоговый HTTP запрос не содержал опасных данных.
wp_safe_redirect( $location, $status = 302 )
Еще более надежный способ редиректа, при котором перенаправление возможно только на разрешенные в белом списке хосты.
wp_sanitize_redirect( $location )
Очищает указанный URL для использования при перенаправлениях.

Коротко об атаках с разделением ответов HTTP

В ходе данной атаки, также называемой CRLF-инъекцией, уязвимый веб-сервер отвечает на специально сформированный вредоносный запрос ответом HTTP, который интерпретируется как два отдельных ответа вместо одного. Это становится возможным в случаях, когда в заголовках ответа HTTP используются данные, введенные пользователем без дополнительных проверок. Взломщик может спровоцировать ситуацию, при которой браузер жертвы атаки интерпретирует добавленный заголовок как ответ на второй запрос, при этом специально сформированные данные будут отображены и, возможно, добавлены в кэш браузера.

Для реализации разделения ответов HTTP с использованием уязвимого сервера, взломщик выполняет следующие действия:

  • Обнаруживает возможности для ввода данных пользователем с возможностью добавления этих данных в заголовок HTTP.

  • Формирует вредоносную строку для завершения запроса от приложения и добавления своего собственного запроса с необходимыми данными в заголовке.

  • Принуждает жертву атаки к отправке двух запросов серверу. Первый запрос содержит специально сформированные вредоносные данные в составе заголовка HTTP, а второй - запрос от приложения, поэтому браузер жертвы атаки интерпретирует разделенный ответ таким образом, что он считается принадлежащим второму запросу.

Подробнее читайте тут.

База данных

$wpdb->prepare( $format, $value1, $value2, ... )

Очищает запрос: безопасно заменяет заполнители (placeholder) в $format указанными в $value1, $value2, ... значениями.
$format — это строка на подобие sprintf(), в которой можно указать только следующие заполнители: %s, %d и %f. Нет необходимости в кавычках для строк (%s). prepare() добавит кавычки сам, т.е. foo=%s станет foo='значение'. Но если кавычки есть, ничего страшного, функция понимает такую конструкцию.

Работает на основе wpdb::_real_escape( $string ) с предварительной обработкой форматирования.

$wpdb->get_var( $wpdb->prepare(
  "SELECT something FROM table WHERE foo = %s and status = %d",
  $_GET['name'],  // 'не \' безопасные строка' (функция сделает очистку сама)
  $_GET['status'] // 'не безопасное число (функция сделает очистку сама)
) );
esc_sql( $sql )
Экранирует строку или массив строк для использования в SQL запросе. Опирается на функцию addslashes().
$wpdb->prepare() предпочтительнее, потому что исправляет некоторые ошибки форматирования (кавычки).
Работает на основе wpdb::_real_escape( $string ).
$wpdb->escape_by_ref( &$text )
Не возвращает данные, т.к. они передаются по ссылке. Данные очищаются «на лету».
Работает на основе wpdb::_real_escape( $string )
$wpdb->esc_like( $text )

Подготавливает строку для использования в LIKE части SQL запроса. Обработанная строка должна быть еще обработана одной из функций очистки!

$link = $wpdb->esc_like( $link ); // подготовим строку для LIKE аргумента
$link = esc_sql( $link );         // очистим переменную
$link = '%' . $link . '%';        // создадим полную переменную поиска LIKE
// $link готова для использования в SQL запросе.
sanitize_sql_orderby( $orderby )
Проверяет можно ли использовать переданную строку в ORDER BY части SQL запроса.
sanitize_title_for_query( $title )
Подготавливает строку для использования её в качестве ярлыка (slug) в SQL запросе. Очистка от инъекций делается отдельно. Подразумевается что это название чего-либо: заголовка, имени файла и т.д.

Функции не связанные с типом данных

Остальные функции очистки, которые не вошли в предыдущие списки, потому что подходят для разных типов данных.

isset( $var )
Проверяет существует ли переменная.
empty( $var )
Проверяет не пустая ли переменная. Игнорирует ошибки, если переменной не существует.
in_array( $needle, $haystack, true )
Для проверки, есть ли указанный элемент в указанном массиве.
count( $array )
Для проверки количества элементов в массиве.
sanitize_meta( $meta_key, $meta_value, $meta_type )

Очищает значение мата данных. Сама функция ничего не делает, а примеряет фильтр "sanitize_{$meta_type}_meta_{$meta_key}", через который разные мета данные можно очистить по-разному.

Примечательно то, что эта функция используется во всех функциях при добавлении/обновлении метаданных WordPress: update_*_meta() или add_*_meta(). Т.е. использовать её напрямую обычно нет смысла, но вот использовать хук который она обрабатывает очень удобно при обновлении любых метаданных...

sanitize_term( $term, $taxonomy, $context )
Очищает все поля элемента таксономии с помощью функции sanitize_term_field().

Также смотрите все функции ядра содержащие exists, validate или is_:

Функция filter_var()

filter_var() - это очень интересная для проверки и очистки данных функция.

Проверяет или очищает значение указанной переменной по указанным параметрам.

Возвращает

Отфильтрованные данные или FALSE, в случае неудачной проверки, фильтрации.

Использование

$var = filter_var( $var, $filter, $options );
$var(разное) (обязательный)
Переменная, которую нужно очистить, проверить.
$filter(число)

ID фильтра который будет использоваться для проверки, очистки. Такие ID хранятся в виде чисел в предопределенных константах PHP. Их полный список можно посмотреть тут:

По умолчанию указана константа FILTER_DEFAULT, значит что никакой фильтр не применяется.
По умолчанию: FILTER_DEFAULT

$options(массив/константа)

Различные параметры или флаги фильтрации. Константой или комбинацией констант через | (ИЛИ). Или может быть массивом, который поддерживает всего 2 ключа: array('options'=>..., 'flags'=>... ).

Все возможные флаги смотрите здесь: флаги, используемые a filter_var.

Пример опций фильтрации, с указанием констант:

// вернуть null при неудачной проверке
filter_var('example.com', FILTER_VALIDATE_URL, FILTER_NULL_ON_FAILURE ); //> null

// или флаги можно указать в элементе массива flags
filter_var('example.com', FILTER_VALIDATE_URL, array('flags'=>FILTER_NULL_ON_FAILURE) ); //> null

Пример опций фильтрации, с указанием массива:

// массив параметров фильтрации
$options = array(
	'options' => array(
		'default' => 'http://example.com/info', // вернется при неудачной проверке
	),
	'flags' => FILTER_FLAG_PATH_REQUIRED, // флаг: Включает содержание пути в URL в качестве необходимого условия.
);
$var = filter_var('http://example.com', FILTER_VALIDATE_URL, $options );

echo $var; //> http://example.com/info
// т.е. вернулось указанное значение по умолчанию,
// потому что флаг говорит, что у URL должен быть путь

По умолчанию: null

Примеры filter_var()

#1 Демонстрация работы

// email
$email = filter_var('bob@example.com', FILTER_VALIDATE_EMAIL); //> bob@example.com
$email = filter_var('bob@example', FILTER_VALIDATE_EMAIL); //> false

// url
$url = filter_var('http://example.com', FILTER_VALIDATE_URL); //> http://example.com
$url = filter_var('example.com', FILTER_VALIDATE_URL); //> false

$url = filter_var('http://example.com/path', FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED); //> http://example.com/path
$url = filter_var('http://example.com', FILTER_VALIDATE_URL, FILTER_FLAG_PATH_REQUIRED); //> false

#2 Проверим является ли строка IP адресом

if( filter_var( 'foo', FILTER_VALIDATE_IP ) )
	echo 'это IP';
else
	echo 'это не IP';

// выведет: 'это не IP'

if( filter_var( '123.111.222.123', FILTER_VALIDATE_IP ) )
	echo 'это IP';
else
	echo 'это не IP';

// выведет: 'это IP'

// по-другому можно записать так
filter_var( 'foo', FILTER_VALIDATE_IP ); //> false
filter_var( '123.111.222.123', FILTER_VALIDATE_IP ); //> true

Ваши функции валидации данных

Можно написать свои собственные PHP функции и включить их в тему или плагин.

При написании функции проверки, рекомендуется назвать функции вопросительно, например: is_phone(), is_available(), is_us_zipcode().

В конце функции всегда возвращайте только true или false!

Пример 1

Пример функции PHP, которая проверяет, является ли значение действующим в США индексом (за пределами США, это называется почтовый код).

function is_us_zipcode( $zipcode ) {

	if ( empty( $zipcode ) ) {
		return false;
	}

	// a zip code should never have more than 10 characters
	if ( 10 <= strlen( trim( $content ) ) ) {

		// use a regex to check whether this zip code is correct
		if ( preg_match( '/^\d{5}(\-?\d{4})?$/', $content ) ) {
			return true;
		}

	} else {
		return false;
	}
}

Теперь при получении данных поле можно проверить так:

if( isset($_POST['wporg_zip_code']) && is_us_zip_code($_POST['wporg_zip_code']) ){
	// поле проверено, делаем что-либо
}

Пример 2

Допустим вы собираетесь создать запрос для получения записей, а пользователям нужно дать возможность выбрать поле по которому будет проходить сортировка.

Этот пример проверяет входящее поле сортировки, указанное в input поле orderby. Вариантов возможных полей сортировки и их можно перечислить - это будет наш белый список для проверки. Проверять этот список будем с помощью php функции in_array().

<?php
$allowed_keys = ['author', 'post_author', 'date', 'post_date'];

// белый список в нижнем регистре (author),
// поэтому изменим данные, если они указанны в верхнем регистре (AUTHOR)
$orderby = strtolower( $_POST['orderby'] );

if( in_array( $orderby, $allowed_keys, true ) ){
	// проверка пройдена изменяем запрос
}

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

В третьем параметре in_array() мы указали true - это означает что тип данных тоже должен совпадать - должна быть строка. Например, если указать в $_POST['orderby'] = true, а подделать запрос так можно, и не указать проверку по типу, то проверка будет пройдена всегда и мы получим доступ к коду внутри проверки:

// пример плохой проверки
if( in_array( true, [ 'date', 'author' ] ) ){
	// этот код будет срабатывать всегда!
}

-

Информация этого руководства взята из официального источника и личного опыта.

7 комментариев
    Войти