Фильтрация и проверка данных php. частые ошибки

  • Автор темы wh1skas
  • Дата начала
wh1skas
wildcake
Участник
Сообщения
1.017
Реакции
432
ICQ
564739604 564739604
Уважаемые форумчане, прошу обратить внимание на этот пост. Это пост из песочницы ХАБРЫ. Это не мой пост и отношения к нему, я не имею. Но прочтите пост, во многом поможет и ответит на многие вопросы!
Ссылка на материал в ХАБРЕ: http://habrahabr.ru/post/143035/

---------------------------------------------------------------------------------------------------------------------------------------
Материал предназначен в основном для начинающих веб-программистов.

Введение.

Часто ко мне обращаются клиенты, у которых установлены самописные CMS или модули, написанные начинающими веб-программистами, которые не понимают, что нужно для защиты данных и зачастую копируют функции фильтрации, не задумываясь о том как они работают и что именно нужно с ними делать.

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

В сети много статей по поводу фильтрации данных, но они как правильно не полные и без подробные примеров.
intval получает целочисленное значение переменной, т.е. 1, но в самой переменной $number ничего не изменилось, поэтому весь вредоносный код будет передан в SQL запрос.
Правильная фильтрация:
$number = intval($_GET['input_number']);
if ($number)
{
... выполняем SQL запрос ...
}

Конечно, условие может меняться, например если вам нужно получить только определенный диапазон:
if ($number >= 32 AND $number <= 65)


Если вы используете чекбоксы или мультиселекты с числовыми значениями, выполните такую проверку:
$checkbox_arr = array_map('intval', $_POST['checkbox']);

array_map
Так же встречаю фильтрацию в виде:
$number = htmlspecialchars(intval($_GET['input_number']));

htmlspecialchars
Или:
$number = mysql_escape_string(intval($_GET['input_number']));

mysql_escape_string

Ничего кроме улыбки это не может вызвать :)

Фильтрация. Ошибка №2.

Для стринг-переменных используется такая фильтрация:
$input_text = addslashes($_GET['input_text']);

Функция addslashes экранирует спец. символы, но она не учитывает кодировку БД и возможен обход фильтрации. Не стану копировать текст автора, который описал данную уязвимость и дам просто ссылку Chris Shiflett (перевод можно поискать в рунете).

Используйте функцию mysql_escape_string или mysql_real_escape_string, пример:
$input_text = mysql_escape_string($_GET['input_text']);

Если вы не предполагаете вхождение html тегов, то лучше всего сделать такую фильтрацию:
$input_text = strip_tags($_GET['input_text']);
$input_text = htmlspecialchars($input_text);
$input_text = mysql_escape_string($input_text);

strip_tags — убирает html теги.
htmlspecialchars — преобразует спец. символы в html сущности.
Так вы защитите себя от XSS атаки, помимо SQL инъекции.
Если же вам нужны html теги, но только как для вывода исходного кода, то достаточно использовать:
$input_text = htmlspecialchars($_GET['input_text']);
$input_text = mysql_escape_string($input_text);


Если вам важно, чтобы значение переменной не было пустой, то используйте функцию trim, пример:
$input_text = trim($_GET['input_text']);
$input_text = htmlspecialchars($input_text);
$input_text = mysql_escape_string($input_text);


Фильтрация. Ошибка №3.

Она касается поиска в БД.
Для поиска по числам используйте фильтрацию, описанную в первой ошибке.
Для поиска по тексту используйте фильтрацию, описанную во второй ошибке, но с оговорками.
Для того, чтобы пользователь не смог выполнить логическую ошибку, нужно удалять или экранировать спец. символы SQL.
Пример без доп. обработки строки:
$input_text = htmlspecialchars($_GET['input_text']); // Поиск: "%"
$input_text = mysql_escape_string($input_text);

На выходе у нас получится запрос вида:
... WHERE text_row LIKE '%".$input_text."%' ... // WHERE text_row LIKE '%%%'

Это значительно увеличит нагрузку на базу.
В своём скрипте я использую функцию, которая удаляет нежелательные мне символы из поиска:
function strip_data($text)
{
$quotes = array ("\x27", "\x22", "\x60", "\t", "\n", "\r", "*", "%", "<", ">", "?", "!" );
$goodquotes = array ("-", "+", "#" );
$repquotes = array ("\-", "\+", "\#" );
$text = trim( strip_tags( $text ) );
$text = str_replace( $quotes, '', $text );
$text = str_replace( $goodquotes, $repquotes, $text );
$text = ereg_replace(" +", " ", $text);

return $text;
}

Конечно, не все из выше перечисленных символов представляют опасность, но в моём случаи они не нужны, поэтому выполняю поиск и замену.
Пример использования фильтрации:
$input_text = strip_data($_GET['input_text']);
$input_text = htmlspecialchars($input_text);
$input_text = mysql_escape_string($input_text);

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

Фильтрация. Ошибка №4.

Не фильтруются значения в переменной $_COOKIE. Некоторые думаю, что раз эту переменную нельзя передать через форму, то это гарантия безопасности.
Данную переменную очень легко подделать любым браузером, отредактировав куки сайта.
Например, в одной известной CMS была проверка, используемого шаблона сайта:
if (@is_dir ( MAIN_DIR . '/template/' . $_COOKIE['skin'] )){
$config['skin'] = $_COOKIE['skin'];
}
$tpl->dir = MAIN_DIR . '/template/' . $config['skin'];

В данном случаи можно подменить значение переменной $_COOKIE['skin'] и вызвать ошибку, в результате которой вы увидите абсолютный путь до папки сайта.
Если вы используете значение куков для сохранения в базу, то используйте одну из выше описанных фильтраций, тоже касается и переменной $_SERVER.

Фильтрация. Ошибка №5.

Включена директива register_globals. Обязательно выключите её, если она включена.
В некоторых ситуациях можно передать значение переменной, которая не должна была передаваться, например, если на сайте есть группы, то группе 2 переменная $group должна быть пустой или равняться 0, но достаточно подделать форму, добавив код:
<input type="text" name="group" value="5" />

В PHP скрипте переменная $group будет равна 5, если в скрипте она не была объявлена со значением по умолчанию.

Фильтрация. Ошибка №6.

Проверяйте загружаемые файлы.
Выполняйте проверку по следующим пунктам:
  1. Расширение файла. Желательно запретить загрузку файлов с расширениями: php, php3, php4, php5 и т.п.
  2. Загружен ли файл на сервер move_uploaded_file
  3. Размер файла


Проверка. Ошибка №1.

Сталкивался со случаями, когда для AJAX запроса (например: повышение репутации) передавалось имя пользователя или его ID (кому повышается репутация), но в самом PHP не было проверки на существование такого пользователя.
Например:
$user_id = intval($_REQUEST['user_id']);
... INSERT INTO REPLOG SET uid = '{$user_id}', plus = '1' ...
... UPDATE Users SET reputation = reputation+1 WHERE user_id = '{$user_id}' ...

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

Проверка. Ошибка №2.

При выполнении различного рода действий (добавление, редактирование, удаление) с данными не забывайте проверять права пользователя на доступ к данной функции и дополнительные возможности (использование html тегов или возможность опубликовать материал без проверки).

Давно исправлял в одном модуле форума подобную ошибку, когда любой пользователь мог отредактировать сообщение администрации.

Проверка. Ошибка №3.

При использовании нескольких php файлов сделайте простую проверку.
В файле index.php (или в любом другом главном файле) напишите такую строчку перед подключением других php файлов:
define ( 'READFILE', true );

В начале других php файлов напишите:
if (! defined ( 'READFILE' ))
{
exit ( "Error, wrong way to file.
<a href=\"/\">Go to main</a>." );
}

Так вы ограничите доступ к файлам.

Проверка. Ошибка №4.

Используйте хеши для пользователей. Это поможет предотвратить вызов той или иной функции путём XSS.
Пример составления хеша для пользователей:
$secret_key = md5( strtolower( "http://site.ru/" . $member['name'] . sha1($password) . date( "Ymd" ) ) ); // $secret_key - это наш хеш

Далее во все важные формы подставляйте инпут со значением текущего хеша пользователя:
<input type="hidden" name="secret_key" value="$secret_key" />

Во время выполнения скрипта осуществляйте проверку:
if ($_POST['secret_key'] !== $secret_key)
{
exit ('Error: secret_key!');
}


Проверка. Ошибка №5.

При выводе SQL ошибок сделайте простое ограничение к доступу информации. Например задайте пароль для GET переменной:
if ($_GET['passsql'] == "password")
{
... вывод SQL ошибки ...
}
else
{
... Просто информация об ошибке, без подробностей ...
}

Это позволит скрыть от хакера информацию, которая может ему помочь во взломе сайта.

Проверка. Ошибка №5.

Старайтесь не подключать файлы, получая имена файлов извне.
Например:
if (isset($_GET['file_name']))
{
include $_GET['file_name'] .'.php';
}

Используйте переключатель switch:
switch($_GET['file_name'])
{
case 'file_1':
include 'file_1.php';
break;

default:
include 'file_0.php';
break;
}

В таком случаи вы предотвратите подключение файлов, которые не были вами предусмотрены.

Совет.

Для большей надежности используйте один из готовых и популярных классов для фильтрации данных, дабы самому не пропустить какие-то вредоносные символы/данные. Также в этих классах часто имеется возможность выбора фильтра данных.
 
mobisaite
Участник
Сообщения
308
Реакции
63
не стала цитировать но все же хотелось бы увидеть примеры для каждого варианта как точно используется в коде, а особенно для новичков полезно было бы пример с внедрением проверки и без нее на одном и том же коде как выглядит можно с описаниями
 
Сардорбек
Местный
Сообщения
40
Реакции
3
mobisaite написал(а):
не стала цитировать но все же хотелось бы увидеть примеры для каждого варианта как точно используется в коде, а особенно для новичков полезно было бы пример с внедрением проверки и без нее на одном и том же коде как выглядит можно с описаниями
Да... я тоже за на примеры! ) Но это же не пост wh1skasа ! по этому и примеры нет?!?!
 
wh1skas
wildcake
Участник
Сообщения
1.017
Реакции
432
ICQ
564739604 564739604
ой, чот я даже не заметил тут активности )) Сорри, какие примеры интересуют? ))
 
Сардорбек
Местный
Сообщения
40
Реакции
3
Здравствуйте, Меня интересует как фильтровать внешние ссылки, а также БД в стандартном ФФерме ! Спасибо! Вы хорошо поддерживаете! +5
 
wh1skas
wildcake
Участник
Сообщения
1.017
Реакции
432
ICQ
564739604 564739604
Сардорбек написал(а):
Здравствуйте, Меня интересует как фильтровать внешние ссылки, а также БД в стандартном ФФерме ! Спасибо! Вы хорошо поддерживаете! +5
Давайте больше конкретики )) Не совсем понятен запрос. Что имеется ввиду под фильтрацией внешних ссылок? Конкретный случай/пример. Например:

У меня есть поле для воода input type="text", пользователь туда пишет ссылки. Как защитить ячейку от ввода нежелательных символов.

Если угадал, то напиши )) И что имеется в виду про БД. Больше конкретики )))
 
Сардорбек
Местный
Сообщения
40
Реакции
3
Я хотел узнать, с помощью фильтров можно защитить БД?! И какие фильтры нужно использовать для защиты ФФ
 
wh1skas
wildcake
Участник
Сообщения
1.017
Реакции
432
ICQ
564739604 564739604
Фильтруются входные данные. Например у тебя есть поле для ввода текста. Нужно заведомо знать, какой текст туда необходимо пропускать. Например поле для ввода суммы пополнения баланса. Как уже понятно, значения должны быть только числовыми, никакого текста. Поэтому существуют преобразования текста в числа:

<?PHP
$chislo = intval ($_POST['chislo']);
?>
<input type="text" name="chislo">

Это самый простой пример. С текстом сложнее.
 
Сардорбек
Местный
Сообщения
40
Реакции
3
А, понятно! Нет смысла использовать фильтр в БД. Не то место!?
 
wh1skas
wildcake
Участник
Сообщения
1.017
Реакции
432
ICQ
564739604 564739604
  • #10
Ты немного не о том ) Видимо не про тот фильтр думаешь. Фильтрация в данном случае убирает ненужное. Это не как фильтр при сортировках. Например злоумышленник вместо текста в чат-окно захочет написать запрос. Запрос будет неверным, если его отфильтровать функциями и процедурами, выложенными тут.
 
Сардорбек
Местный
Сообщения
40
Реакции
3
  • #11
Да да теперь понял! Я думал что они фильтрует тэги, коды в скрипте! Спасибо еще раз! В скором времени вам 3 модули заказать хочу! Но в первом надо хорошую защиту сделать) По этому делу помогите, к защите! Посоветуете все лучшего
 
rich-99000000
php developer
Участник
Сообщения
227
Реакции
48
  • #12
wh1skas написал(а):
Проверка. Ошибка №5.

При выводе SQL ошибок сделайте простое ограничение к доступу информации. Например задайте пароль для GET переменной:
if ($_GET['passsql'] == "password")
{
... вывод SQL ошибки ...
}
else
{
... Просто информация об ошибке, без подробностей ...
}

Это позволит скрыть от хакера информацию, которая может ему помочь во взломе сайта.
куда конкретно вставить? в index корневой?
 
Последнее редактирование:
rus56
Участник
Сообщения
126
Реакции
16
  • #13
rich-99000000 написал(а):
куда конкретно вставить? в index корневой?
Согласен вопрос уместный куды вставить код????
 
AriCosmo
Участник
Сообщения
515
Реакции
115
Skype
  • #14
wh1skas написал(а):
Ты немного не о том ) Видимо не про тот фильтр думаешь. Фильтрация в данном случае убирает ненужное. Это не как фильтр при сортировках. Например злоумышленник вместо текста в чат-окно захочет написать запрос. Запрос будет неверным, если его отфильтровать функциями и процедурами, выложенными тут.
А фильтрация на количество знаков после точки есть? Вот к примеру в поле вывода будет написано такое значение: 34.09999993339, и в топе будет не красиво отображаться!
 
wh1skas
wildcake
Участник
Сообщения
1.017
Реакции
432
ICQ
564739604 564739604
  • #15
Diknoa написал(а):
А фильтрация на количество знаков после точки есть? Вот к примеру в поле вывода будет написано такое значение: 34.09999993339, и в топе будет не красиво отображаться!
Эо не фильтрация. Это параметры хранимых значений после плавающей запятой. Все редактируется при помощи или Базы или формы вывода (округление в php)
 
rich-99000000
php developer
Участник
Сообщения
227
Реакции
48
  • #16
Diknoa написал(а):
А фильтрация на количество знаков после точки есть? Вот к примеру в поле вывода будет написано такое значение: 34.09999993339, и в топе будет не красиво отображаться!
в полях бд задай значения decimal (10,2)
 
Сверху