| ||||||||||||||||
| ||||||||||||||||
| ||||||||||||||||
Безопасное программирование веб-приложений Данная статья не претендует на роль всеобъемлющего руководства на тему "как сделать так, чтоб меня никто не поломал". Так не бывает. Единственная цель этой статьи - показать некоторые используемые мной приемы для защиты веб-приложений типа WWW-чатов, гостевых книг, веб-форумов и других приложений подобного рода. Итак, давайте рассмотрим некоторые приемы программирования на примере некоей гостевой книги, написанной на PHP. Первой заповедью веб-программиста, желающего написать более-менее защищенное веб-приложение, должно стать "Никогда не верь данным, присылаемым тебе пользователем".
Пользователи - это по определению такие злобные хакеры, которые только и ищут
момента, как бы напихать в формы ввода всякую дрянь типа PHP, JavaScript, SSI, вызовов
своих жутко хакерских скриптов и тому подобных ужасных вещей. Поэтому первое, что
необходимо сделать - это жесточайшим образом отфильтровать все данные, присланные
пользователем.
На роль настоящей защиты, конечно, это претендовать не может - единственное назначение этого элемента - ограничить пользователя от случайного ввода имени длиннее 20-ти символов. А для того, чтобы у пользователя не возникло искушения скачать документ с формами ввода и подправить параметр maxlength, установим где-нибудь в самом начале скрипта, обрабатывающего данные, проверку переменной окружения web-сервера HTTP-REFERER:
Теперь, если данные переданы не из форм документа, находящегося на сервере www.myserver.com, хацкеру будет выдано деморализующее сообщение. На самом деле, и это тоже не может служить 100%-ой гарантией того, что данные ДЕЙСТВИТЕЛЬНО переданы из нашего документа. В конце концов, переменная HTTP_REFERER формируется браузером, и никто не может помешать хакеру подправить код браузера, или просто зайти телнетом на 80-ый порт и сформировать свой запрос. Так что подобная защита годится только от Ну Совсем Необразованных хакеров. Впрочем, по моим наблюдениям, около 80% процентов злоумышленников на этом этапе останавливаются и дальше не лезут - то ли IQ не позволяет, то ли просто лень. Лично я попросту вынес этот фрагмент кода в отдельный файл, и вызываю его отовсюду, откуда это возможно. Времени на обращение к переменной уходит немного - а береженого Бог бережет. Следующим этапом станет пресловутая жесткая фильтрация переданных данных. Прежде всего, не будем доверять переменной maxlength в формах ввода и ручками порежем строку:
Не дадим пользователю использовать пустое поле имени - просто так, чтобы не давать писать анонимные сообщения:
Запретим пользователю использовать в своем имени любые символы, кроме букв русского и латинского алфавита, знака "_" (подчерк), пробела и цифр:
Я предпочитаю везде, где нужно что-нибудь более сложное, чем проверить наличие паттерна в строке
или поменять один паттерн на другой, использовать Перл-совместимые регулярные выражения (Perl-compatible Regular Expressions).
То же самое можно делать и используя стандартные PHP-шные Для поля ввода адреса e-mail добавим в список разрешенных символов знаки "@" и ".", иначе пользователь не сможет корректно ввести адрес. Зато уберем русские буквы и пробел:
Поле ввода текста мы не будем подвергать таким жестким репрессиям - перебирать все
знаки препинания, которые можно использовать, попросту лень, поэтому ограничимся
использованием функций Как-то раз меня попросили потестировать html-чат. Первым же замеченным мной багом было именно разрешение вставки картинок.
Учитывая еще пару особенностей строения чата, через несколько минут у меня был файл, в котором аккуратно были перечислены IP-адреса, имена и пароли всех присутствовавших в этот момент на чате пользователей.
Как? Да очень просто - чату был послан тег Для примитивной гостевой книги перечисленных средств хватит, чтобы сделать ее более-менее сложной для взлома. Однако для удобства, книги обычно содержат некоторые возможности для модерирования - как минимум, возможность удаления сообщений. Разрешенную, естественно, узкому (или не очень) кругу лиц. Посмотрим, что можно сделать здесь. Допустим, вся система модерирования книги также состоит из двух частей - страницы со списком сообщений, где можно отмечать подлежащие удалению сообщения, и непосредственно скрипта, удаляющего сообщения. Назовем их соответственно admin1.php и admin2.php. Простейший и надежнейший способ аутентикации пользователя - размещение скриптов в директории, защищенной файлом .htaccess. Для преодоления такой защиты нужно уже не приложение ломать, а web-сервер. Что несколько сложнее и уж, во всяком случае, не укладывается в рамки темы этой статьи. Однако не всегда этот способ пригоден к употреблению - иногда бывает надо проводить авторизацию средствами самого приложения. Первый, самый простой способ - авторизация средствами HTTP - через код 401. При виде такого кода возврата, любой нормальный браузер высветит окошко авторизации и попросит ввести логин и пароль. А в дальнейшем браузер при получении кода 401 будет пытаться подсунуть web-серверу текущие для данного realm'а логин и пароль, и только в случае неудачи потребует повторной авторизации. Пример кода для вывода требования на такую авторизацию есть во всех хрестоматиях и мануалах:
Разместим этот кусочек кода в начале скрипта admin1.php. После его выполнения, у нас
будут две установленные переменные
Упомянутая ошибка, между прочим, очень распространена среди начинающих и невнимательных программистов.
Когда-то я сам поймался на эту удочку - по счастью, особого вреда это не принесло,
не считая оставленных хакером в новостной ленте нескольких нецензурных фраз.
То есть - проверкой наличия одного и только одного пользователя в базе. Ни больше, ни меньше.
К сожалению, не всегда удается воспользоваться алгоритмом авторизации через код 401 и приходится выполнять ее только средствами приложения. В общем случае модель такой авторизации будет следующей:
Такая модель называется сессионной - после прохождения авторизации открывается так называемая "сессия",
в течение которой пользователь имеет доступ к защищенной части системы.
Сессия закрылась - доступ закрывается. На этом принципе, в частности, строится большинство www-чатов: пользователь может получить доступ к чату только после того, как пройдет процедуру входа.
Основная сложность данной схемы заключается в том, что все скрипты защищенной части приложения каким-то образом должны знать о том, что пользователь, посылающий данные, успешно авторизовался.
После авторизации пользователя скрипт, проведший авторизацию, генерирует достаточно длинное случайное число:
Это число он: Пользователь при каждом запросе, помимо другой информации (сообщение в чате, или список сообщений в гостевой книге), отправляет серверу свой uid. При этом в документе с формами ввода будет присутствовать, наряду с другими формами, тег вида:
Форма uid невидима для пользователя, но она передается скрипту защищенной части приложения. Тот сличает переданный ему uid с uid'ом, хранящимся в локальной базе и либо выполняет свою функцию, либо... "hacker? he-he...". Единственное, что необходимо сделать при такой организации - периодически чистить локальный список uid'ов и/или сделать для пользователя кнопку "выход", при нажатии на которую локальный uid пользователя сотрется из базы на сервере - сессия закрыта. Некоторые программисты используют в качестве uid не "одноразовое" динамически генерирующееся число, а пароль пользователя. Это допустимо, но это является "дурным тоном", поскольку пароль пользователя обычно не меняется от сессии к сессии, а значит - хакер сможет сам открывать сессии. Та же самая модель может быть использована везде, где требуется идентификация пользователя - в чатах, веб-конференциях, электронных магазинах. В заключение стоит упомянуть и о такой полезной вещи, как ведение логов. Если в каждую
из описанных процедур встроить возможность занесения события в лог-файл с указанием
IP-адреса потенциального злоумышленника - то в случае реальной атаки вычислить хакера
будет гораздо проще, поскольку хакеры обычно пробуют последовательно усложняющиеся
атаки. Для определения IP-адреса желательно использовать не только стандартную
переменную REMOTE_ADDR, но и менее известную HTTP_X_FORWARDED_FOR,
которая позволяет определить IP пользователя, находящегося за прокси-сервером.
Естественно - если прокси это позволяет. Я буду очень признателен, если кто-нибудь из программистов поделится своими не описанными здесь методами обеспечения безопасности при разработке приложений для Web. |
|
| ||||||||||||||||
|