Принципы проверки и очистки

Основной принцип - это не доверять никаким данных и всегда проверять или очищать данные получаемые из любых источников: POST/GET/AJAX параметры, БД, внешние API, RSS-каналы. Остальные принципы которые вытекают из этого рассмотрим ниже.

Я думаю и так понятно, что если игнорировать проверку данных, то ваш сайт рано или поздно взломают.

Проверка (валидация) данных

Валидация данных, означает рассмотрение входящих данных и проверка подходят ли они для использования, прежде чем что-либо с ними делать.

Данные формы

«Входящие данные» обычно поступают из формы, предоставленной пользователю, но это также могут быть данные, поступающие от любого внешнего источника, например: RSS-каналы, AJAX запросы, других вызовов API.

Немного примеров валидации данных:

  • Убедитесь, что обязательные поля заполнены.
  • Убедитесь, что введенный номер телефона содержит только цифры и пунктуацию.
  • Убедитесь, что введенный почтовый адрес является правильным.
  • Убедитесь, что поле для ввода количества больше чем 0.

Валидация данных должна быть выполнена как можно раньше. Такую проверку нужно делать сразу при получения данных формы и только потом работать с данными.

Проверка формы может быть выполнена в JavaScript, прежде чем форма будет отправлена. Полагаться такую проверку нельзя! JavaScript - это клиентская часть и там данные можно подделать отключив скрипт проверки формы или отправив данные напрямую на сервер. Конечную проверку данных всегда нужно делать именно в PHP!

Очистка данных

Когда независимо от обстоятельств нужно принять данные или вывести на экран, то их нужно очистить от опасных символов. Очистка нужна:

  • перед сохранением данных в БД.
  • перед выводом данных на экран.
  • Иногда перед проверкой данных - сначала очищаем, потом проверяем подходит ли. Например, удаляем пробелы на концах строк и проверяем подходит ли значение.

Чем проверять входящие данные?

Для этого есть куча функции из ядра WordPress и из PHP. Полный список.

Принципы проверки и очистки данных

Не доверяйте никаким данным

Любые данные вводимые пользователем потенциально опасны, даже если они выводятся в админке, только для администратора.

Относитесь к данным получаемым из базы данных как к потенциально опасным. Даже если при добавлении данных вы произвели их очистку. Дело в том, что данные могут использоваться в разных контекстах. Например, заголовки могут содержать кавычки и вызовут ошибки если использовать их в атрибуте html тэга...

Проверяйте на входе, очищайте на выходе

Выводимые данные нужно очищать как можно позднее, в идеале прямо перед выводом на экран. Логика думаю понятна: не думаем об очистке вообще, кроме случаев вывода данных на экран...

Принимаемые данные наоборот: нужно очищать как можно раньше, в идеале прямо на входе. Но, при этом, их также нужно чистить перед записью в базу данных. Дело в том, что очистка для SQL запроса может отличаться от начальной очистки, например при входе мы удалил все теги и думаем что данные очищены, но такая очистка не обезопасит от SQL инъекций, т.е. это разные контексты очистки...

Доверяйте WordPress

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

$posts = get_posts( array(
	's' => $_GET['s']
) );

К таким функциям можно отнести все функции, которые что-то выводят на экран или что-то добавляют в базу данных и при этом вы не составляете SQL запрос самостоятельно, например это: $wpdb->update(), $wpdb->insert(), wp_update_post() и т.д. Вывод: the_title(), the_content() и т.д.

Лучше лишний раз очистить

Если разобраться, то очистка данных нужна не всегда. Но при разработке и так приходится за многим следить, и отвлекаться теряя драгоценное время и внимание на очистку порой нерационально. Поэтому если сомневаетесь, лучше лишний раз очистить в нужном контексте перед выводом/вводом, чем потом ловить баги, или еще хуже — иметь дыры в коде...

-

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

Белый список

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

Пример использование оператора сравнения
$val = '1 непроверенная строка';

if ( 1 === $val ) {
	echo 'Допустимое значение';
}
else {
	wp_die( 'НЕ допустимое значение' );
}

Важно чтобы данные проверялись с учетом типа (=== или !==).

Обратите внимание что мы использовали оператор === а не ==. Это важно, потому что при сравнении == проверка будет пройдена - $val превратится в 1.

Пример с in_array()
$val = '1 опасная строка';

$safe_values = array( 1, 5, 7 ); // белый список

if ( in_array( $val, $safe_values, true ) ) {
	// `true` указывает что нужно учитывать тип данных - точная проверка
	echo 'Допустимое значение';
}
else {
	wp_die( 'Недопустимое значение' );
}

Обратите внимание что мы использовали третий параметр true для in_array(). Это важно, потому что без него проверка будет пройдена - $val превратится в 1.

Пример со switch()
$val = '1 опасная строка';

switch ( true ) {
	case 1 === $val:  // нужно отдельно проверять с учетом типа, а не надеется на switch
		echo 'Допустимое значение';
		break;

	default:
		wp_die( 'Недопустимое значение' );
}

Важно чтобы данные проверялись с учетом типа (=== или !==).

Обратите внимание что отдельно проверяем значение с помощью === а не просто case 1. Это важно, потому switch по умолчанию преобразует тип и если так не сделать, то проверка будет пройдена - $val превратится в 1.

Черный список

Не пропускать данные, если они есть в черном списке. Чаще всего это лучшая проверка, если такая проверка подходит под вашу логику.

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

Данные из числовой колонки в базе данных всегда безопасны

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

Числовые данные нужно всегда превращать в числа на входе:

$count = (int) $_GET['count'];   // 10
$price = (float) $_POST['price']; // 52.5

Примеры

Пример проверки данных

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

// Пройдет если $data состоит только из букв и цифр: 'AbCd1zyZ9' - да, 'foo!#$bar' - нет
if ( ! ctype_alnum( $data ) ) {
	wp_die( 'Неверный формат' );
}

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

Пример очистки данных

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

// когда нужно получит только число
$trusted_integer = (int) $untrusted_integer;

// когда нужно получит только строчные буквы
$trusted_alpha = preg_replace( '/[^a-z]/i', "", $untrusted_alpha );

// когда нужно получит стандартный ярлык (slug)
$trusted_slug = sanitize_title( $untrusted_slug );