Можно долго спорить, почему некоторым людям так нравиться гадить и ломать плоды трудов других людей, но так или иначе это факт, актуальный и для виртуального мира. Еще на заре зарождения домашних ПК, т.е. когда доступ к ним начали получать все желающие, началась эта чума. Был написан первый вирус, впервые взломан веб-узел... Cейчас таким уже никого не удивишь. Многие уже привыкли, время от времени видеть надписи типа "Тут был я супер-пупер хакер" и другие проявления компьютерного вандализма.
Большинство таких атак происходят в результате использования "дыр" в серверных скриптах. Именно о прикрытии этих самых лазеек для хакеров и будет рассказано в данной статье.
Итак, как было сказано, хакер может получить доступ к сайту через серверные скрипты (конечно, есть множество других возможностей, например, украсть пароль - трояном, снифером или даже нагло, с компьютера пользователя, но это уже отдельная тема). Почему именно серверные? Да потому что с клиентскими - теми, что исполняются на машине посетителя сайта, он ничего не поделает. Они не имеют никаких прав доступа к серверу, разве что могут получать от него информацию да и только, но не в коем случае, клиентские скрипты не смогут самостоятельно изменить что-то на сервере.
Самой распространенной серверной технологией на сегодня является PHP. Думаю, раз вы читаете данную статью, не стоит снова останавливаться на том, что это такое, тем более на эту тему уже было немало хороших статей. Что ж, преступим.
Прежде всего, хочу сказать, что представленные тут примеры не гарантируют на 100% того, что вас никто не взломает, такое просто невозможно. Всегда, даже в самых распространенных и совершенных системах есть узкие места, пример тому сенсация полугодовой давности, когда на сайтах по всему миру в запросах на всеобще признанном языке SQL (Structured Query Language - структурированный язык запросов) была найдена грубейшая ошибка, получившая название SQL Injection. Но при этом вы увидите самые частые фатальные ошибки в защите и сможете на должном уровне защитить себя от атаки не только любителя, но и профессионального хакера.
Для начала вам нужно запомнить только одно - никогда не верить данным, полученным от посетителя - ведь они могут представлять собой вредоносный скрипт, который к стати, может быть как серверным, так и клиентским (будет работать на машинах посетителей). И этого вполне хватит, конечно, если в самих скриптах недопущены критические ошибки и нормально настроен сам сервер, но обо всем по порядку.
Первый пример, что приходит в голову - вы решили написать гостевую книгу. Значит вам нужно поле для ввода имени, адреса электронной почты и собственно сообщения. PHP скрипт принимает от формы данные и сохраняет их в специальный файл для последующего отображения при чтении гостевой книги. Вроде нет ничего опасного, но вот хакер так не считает, при отсутствии соответствующих мер безопасности, он может воспользоваться данной формой в своих целях. Что ж, не дадим ему ни шанса.
Для начала было бы неплохо ограничить длину имени и адреса e-mail. Это не только один из многочисленных методов частичной защиты, но и предохранение от шутников, которые думают что имя длиной в несколько сот символов очень забавно. Так что давайте в поле для ввода напишем, скажем, maxlength=25, например:
<input type=text name=user_email maxlength=25>
Таким способом никто не сможет ввести в данное поле более 25 символов. Однако это остановит только виртуальных вандалов-новичков ведь в адресной строке запросто можно написать что-то типа:
...guest.php?user_email=ha_ha_ha_slabaja_zashita_ha_ha_ha_tyt_bil_super_haker...
Что же, нанесем второй удар, написав в самом начале PHP скрипта примерно такое:
<?php $user_email=$_POST['user_email']; ...
Т.е. значение переменной $user_email берем прямо из полей соответствующих значений POST массива. И так для каждой переменной. К стати, в таком случае нужно подправить и форму для отправки сообщений, явно указав метод передачи данных - method="post", например:
<form action="guest.php" method="post">
Сможет ли теперь хоть что-то сделать хакер? Сможет, сможет и несомнивайтесь.
Напомню, что при передаче данных на сервер при использовании метода POST, в отличие от метода по умолчанию - GET, данные передаются не через адресную строку в броузере, а вместе с пакетами данных, т.е. и POST можно подделать на чем угодно - начиная от стандартных программ из поставки Windows и заканчивая тем же Делфи - отослать запрос вида:
POST /guest.php HTTP/1.0 user_email=vetaki_ja_tebla_vzlomal...
Конечно, есть еще свои нюансы со строками и спец символами, но это вовсе несложно, хотя и для этого нужно быть уже на соответствующем уровне, чего не хватает большинству лиц, считающими себя хакерами.
Так что же делать? Паниковать! Нет, конечно же, шучу.
Если хакер попытается вот так нагло передать данные, мы можем остановить его следующим образом:
p>
<?php $referer=getenv("HTTP_REFERER"); if (!ereg("^http://my.domain.com")) { echo "Hack off"; exit; } ...
Как видите, мы проверяем, послан ли запрос с одной из наших страниц, открытых в броузере (наш домен - http://my.domain.com) если все верно - выполняем что надо, ну а если нет- выводим поздравление хакеру: "Hack off" и заканчиваем работу скрипта: exit;
Ну что, уже, ликуете? А зря. Напомню что переменная HTTP_REFERER формируется броузером посетителя, т.е. на стороне клиента (читать - хакера), а из всего этого получается, что и в ее подлинности мы не можем быть уверены. Подделать ее так же несложно, как и POST запрос.
Уже устали и не верите в свои силы против этих всемогущих хакеров? Не стоит. Враг хоть и не выдает себя, но уже окончательно устал, до данного этапа дойдут в лучшем случае 5-10% всех пытающихся. Так что, не будем разворачиваться у самого финиша, нанесем сокрушительный удар.
Всегда все верно говорят, что со стороны хакеров любая система имеет уязвимости. Но я еще ни разу почему-то не слышал чтобы говорили про обратное - ведь у любой системы есть места, где хакер уже беспомощен. Самое время применить специальное вооружение админов. А именно безысходность выполнения PHP сценария: как бы хакер не изощрялся, если стоит exit; - значит exit и точка.
Итак, допустим нам попался такой редкий хакер, что прорвался сквозь всю нашу предыдущую линию обороны и может спокойно посылать серверу переменные любой нужной длины, откуда хочет. Ну и пусть, а мы добавим сразу после проверки HTTP_REFERER и присвоения значения из массива (например, с POST) жесткую урезку строки:
$user_email=substr($user_email,0,25);
Теперь мы имеем переменную $user_email длиной в 25 символов (если ее исходная была больше, остальные ее символы были отброшены) и не один хакер не в силах этого поменять.
Так, но 25 символов все еще представляют опасность. Разумеется, только в том случае, если это вредоносные инструкции хакера, пожалуй, следует через дефис написать -неудачника, так как дальше мы их профильтруем и удалим/заменим спецсимволы или же вовсе заблокируем.
Какие символы следует блокировать? Это зависит от поля, например, в имени это могут быть все, кроме букв из алфавита, пробела, цифр, ну и пусть знака _ т.е. нам следует поступить, например, так:
... if (preg_match("/[^(\w)|(\x7F-\xFF)|(\s)]/",$user_name)) { echo "В имени есть запрещенные символы..."; exit; }
Теперь имена в гостевой будут чистенькими и ровненькими.
Для адреса электронной почты следует разрешить собаку @ и точку, а пробелы и русские буквы запретить.
Для тела сообщения также < следует менять на <, а > на > например, так:
$message=ereg_replace("<","<",$message); $message=ereg_replace(">",">",$message);
А знаки переноса строки на тег переноса <br>:
$message=ereg_replace("(\r\n|\n|\r)","<br>",$message);
Можно использовать и специальные функции PHP - htmlspecialchars(), nl2br() и другие, в общем, думаю, сами разберетесь, а то от темы уходим (а если не разберетесь - мыльте).
Также может быть уместна проверка на пустые сообщение или имя пользователя. Ее можно осуществить как стандартной функцией empty() так и просто проверив, не равна ли переменная "" например:
if (empty($message)) { echo "Пустое сообщение оставлять не стоит"; exit; }
или вот так:
if ($message=="") { echo "Пустое сообщение оставлять не стоит"; exit; }
В общем, дело вкуса. Также, можно не просто выводить такие сообщения типа "Пустое сообщение оставлять не стоит" или "Hack off" можно и сохранять в файле на сервере/пересылать себе на почту IP адрес хулигана, явно получить его можно так:
$RIP=$GLOBALS['REMOTE_ADDR'];
Таким способом будет возможность и вычислить неудачника-взломщика или, скажем, ограничить попытки, не давая в сутки более, например 2 раз оставлять сообщения в гостевой книге. Но я бы не стал так делать. Причин много - начиная от прокси серверов (хотя можно без проблем обойти их и узнать реальный IP) и заканчивая тем, что диал-ап до сих пор господствует на просторах СНГ. К тому же вышеперечисленных мер защиты вполне достаточно, причем не только для гостевой книги.
Напоследок хочу обратить ваше внимание на честных пользователей, а ведь их более 99.99% из общего количества посетителей и из-за каких-то хакеров они недолжны страдать. О чем я говорю? Например, человек не знал, что мы запретили некоторые символы в имени и написал в поле имени что-то типа [NickName], а далее очень-очень долго писал слова благодарности или еще чего и в результате мы его грубо остановили, сказав, что имя не годится, возвращайся и делай все заново. Как вы думаете, он вернется? Возможно и вернется, но уже совсем с другими словами.
Что же делать? Писать возле каждой строки для ввода, что можно вводить, а что нет? Несерьезно. Можно, например, вместо блокировки таких имен, просто удалять заблокированные символы. Но я не думаю что и это лучший путь.
Лучше всего заранее предупредить пользователя о ошибке, еще до того как он отправит все на сервер. Как? С этой задачей с легкостью справляются клиентские скрипты, например технология JavaScript. Стоит лишь перед отправкой проверить введенные данные, в случае обнаружения ошибки, вывести соответствующее сообщение и попросить пользователя исправить ошибку. Вот как это можно реализовать для проверки корректности адреса почты:
<script language="JavaScript"> function checK(f) { if (f.email.value=='') { alert("Укажите адрес почты."); f.email.focus(); return false } if (/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,4})+$/.test(f.email.value)) { return true } alert('Неверный адрес почты.\nПопробуйте еще раз.'); f.email.select() return false } </script>
<form name=f action="guest.php" method="post" onSubmit="return checK(this)"> <input type="text" name="email" value="введите ваш e-mail" onfocus="if (this.select) this.select()" onclick="if (this.select) this.select()" size=28> <input type="submit" name="subscribe" value="Ок"> </form>
Как вы видите, после клика на кнопку Ок, данные, перед передачей скрипту guest.php, проверяются функцией checK, если введенный адрес пуст или содержит запрещенные знаки, пользователь получит сообщение: "Укажите адрес почты." или "Неверный адрес почты.\n Попробуйте еще раз." соответственно (\n - перенос строки) при этом обратите внимание, что сообщения будут в окне: alert() и никакой перезагрузки страницы даже и не произойдет: return false , а курсор выделит ошибочный ввод: f.email.focus(); или же f.email.select() что очень удобно для пользователя, особенно если на странице поле для ввода не одно.
|