Сообщений 10 Оценка 77 [+1/-0] Оценить |
Введение Чем грозит ошибка? Как исправить? Заключение |
Очень часто при анализе сторонних скриптов обнаруживается одна и та же распространенная ошибка: отсутствие проверки передаваемых данных в «неизменяемых» полях, таких как <input type=”hidden”>, <input type=”radio”>, <input type=”checkbox”> и, конечно же, <select></select>.
Интересно, что никто не забывает проверять данные полей, которые пользователь задает явно в тех или иных окнах ввода: <input type=”text”>, <textarea></textarea>. Здесь все отлично помнят, что переданные данные необходимо проверить на соответствие шаблону, или хотя бы на длину:
if (strlen($_REQUEST['field']) > 64) die('Слишком длинная строка!'); |
Почему-то и начинающие, и более опытные программисты считают, что «неизменяемые» явно поля никак нельзя отредактировать. Поверьте, это далеко не так! Вот простой пример, как можно стандартными средствами Mozilla Firefox «обмануть» скрипт.
Создадим файл HTML с формой выбора суммы, которую мы хотим внести на счет:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>Тест неизменяемых полей</title> </head> <body> <form action="test.php"method="post"> Какую сумму вы хотите внести на счет?<br> <selectname="testselect"> <option value="500">500 рублей</option> <option value="1000">1000 рублей</option> </select> <input type="submit"value="Внести сумму"> </form> </body> </html> |
Казалось бы, что можно сделать? Ведь пользователь может выбрать только два варианта, 500 и 1000. Не все так просто, обычной проверкой «переданности» переменной обойтись нельзя, даже если вы явно указываете массив переменных $_POST, а не $_REQUEST.
if (empty($_POST['testselect'])) die('Не указана сумма для внесения на счет!'); |
Некоторые проверяют ещё поле referer, чтобы удостовериться, что пользователь не сохранил страницу на локальный диск и затем не отправил скрипту запрос с подправленными данными.
if ($_SERVER['HTTP_REFERER']!='http://www.landgraph.ru/script/test.html') die('Выполнение скрипта запрещено!'); |
Что теперь может сделать взломщик? Кроме возможности вручную написать запрос к скрипту и передать «левые» данные, существует еще одна интересная возможность. Открываем нужную страницу в браузере FireFox (рисунок 1).
Рисунок 1.
Кажется, скрипт хорошо защищен от внешнего воздействия, потому что пользователь «не может» исправить данные. Но в меню «Инструменты» есть такая вещь – DOM Inspector. Открываем его (рисунок 2).
Рисунок 2.
Нажимаем на первую кнопку слева, потом на наше поле выбора на странице. Смотрим, что у нас теперь в инспекторе DOM (рисунок 3).
Рисунок 3.
Видим наше поле выбора и оба варианта – в виде объектов Option. Выбираем первый из них (рисунок 4).
Рисунок 4.
В правой части окна мы видим значение этого варианта – 500. Жмем на нем правой кнопкой мыши и выбираем в открывшемся меню пункт Edit, в открывшемся окне пишем 1, нажимаем OK, закрываем инспектор объектов и нажимаем кнопку «Внести сумму» (рисунок 5).
Рисунок 5.
Как вы думаете, что мы увидим? Правильно, скрипт отработал и внес нам на счет 1 рубль, вместо «выбранных» 500 рублей!
Мы не использовали ничего, кроме стандартных средств, предоставляемых браузером, и «кинули» систему на 499 рублей.
Ну и чем это грозит, спросите вы, ведь все равно деньги зачислены на счет! А если бы это была сумма, которую нужно заплатить за товар, и вместо 500 рублей вам заплатили рубль и «купили» вашу разработку?
Другой вариант – это запись ошибочных сведений в базу данных и дальнейшая работа с ошибками. При этом путь к «сердцу» системы потенциальному взломщику вы открываете сами. А самое неприятное, что открытой уязвимостью может воспользоваться даже взломщик низкой квалификации.
Вот пример из жизни. Мне нужно было скачать одну песню. Сайт, на котором она лежала, целиком и полностью платный. Но проблема была не в деньгах, а в их количестве, которое запрашивал сайт. Песня стоит 10 центов, а минимальная сумма пополнения счета, которую мне предлагала внести система – 10 долларов! Конечно же, такую сумму вносить на счет из-за одной песни очень не хотелось. Описанным выше способом я поменял в одном из полей выбора 10 долларов на 1 доллар… И что вы думаете? Система спокойно выписала мне счет на 1 доллар, который я оплатил и скачал себе необходимую песню! В итоге владелец сайта недополучил 9 долларов. Для меня это плюс, но владелец вряд ли так считает.
Исправить это очень просто – нужно проверять все поля, которые передает пользователь, на правильность данных. Сделать это можно несколькими способами. Способ первый применим, когда передается числовое значение в заданном диапазоне, например, форма выглядит так:
<form action="test.php"method="post"> Выберите пункт<br> <selectname="testselect"> <option value="1">Пункт 1</option> <option value="2">Пункт 2</option> <option value="3">Пункт 3</option> </select> <input type="submit"value="Выбор"> </form> |
Необходимо удостовериться, что пользователь выбрал именно предложенный пункт:
if (empty($_POST['testselect']) || intval($_POST['testselect']) != $_POST['testselect'] || $_POST['testselect'] < 1 || $_POST['testselect'] > 3 ) die('Нет такого пункта!'); |
Логика такая: если переданное поле пусто, или передано не число, или переданное число меньше 1, или переданное число больше 3 – вывести сообщение об ошибке.
Другой вариант – если передается произвольный набор чисел, которые не попадают под определение непрерывного диапазона, или возможен выбор текстовых вариантов, например:
<form action="test.php"method="post"> Выберите пункт<br> <selectname="testselect"> <option value="1">Пункт 1</option> <option value="5">Пункт 5</option> <option value="asd">Пункт 3</option> </select> <input type="submit"value="Выбор"> </form> |
Можно все записать так:
if (empty($_POST['testselect']) || ($_POST['testselect'] != 1 && $_POST['testselect'] != 5 && $_POST['testselect'] != 'asd') ) die('Нет такого пункта!'); |
Но это очень объемное перечисление и им можно воспользоваться, только если количество вариантов невелико. Если же вариантов много, можно прибегнуть к проверке массивом. Необходимо создать массив возможных вариантов, а потом функцией проверки вхождения в массив проверить правильность переданного варианта.
$variants=Array(1,5,'asd'); if (empty($_POST['testselect']) || !in_array($_POST['testselect'], $variants)) die('Нет такого пункта!'); |
Если же варианты лежат в БД, то необходимо сделать выборку из таблицы и проверить существование переданного варианта:
$query = mysql_query('SELECT `id` FROM `variants` WHERE `id`="' . (get_magic_quotes_gpc() ? $_POST['testselect'] : addslashes($_POST['testselect'])) . '"'); if (mysql_num_rows($query)<=0) die('Нет такого пункта!'); mysql_free_result($query); |
Конечно, кроме описанных выше вариантов проверки правильности ввода «неизменяемых» полей существуют и другие. Но важно запомнить, что неизменяемых полей нет! Так или иначе они могут быть изменены, и вы, или ваш заказчик, может понести убытки из-за того, что вы решили, что пользователь выберет именно предложенный вами вариант, а не подставит свой. Все, написанное выше, справедливо не только для полей <select></select>, но и для любых других, где вы явно задаете выбор из возможных вариантов.
Сообщений 10 Оценка 77 [+1/-0] Оценить |