Как, бывает, хранят пароли

Я, пока, опущу ту вкусную часть, как при помощи SQL-Injection можно получить пароли пользователей, но желающие могут самостоятельно прочесть IV раздел мануала о Безопастности SQL Injection . И расскажу о самой неприятной (ок, не всем не приятной) привычке – открытых паролях и том как их хранят в других случаях.

Итак, первый и самый простой случай, когда пароли хранятся в таблице (название не имеет значения, но очень часто это бывает таблица users, admins или members, возможны разные префиксы), в поле password (обычно нужные поля очень легко узнать по форме регистрации или логина). Оно очень приятно, когда у сайта есть уязвимость ввиде SQL-Injection – вытянуть пароль и логин нужного вам пользователя не составит никакого труда. Время, нужно на поиск, занимает от 10 минут до нескольких часов – всё зависит от мастерства и фортуны (но не стоит исключать человеческий фактор). Кстати, если базу уводят (физический дамп с сервера), создателям скажут “спасибо”. Такие разработчики обычто оставляют незащищённый phpMyAdmin на сервере в легко доступном месте, а иногда оставляют SQL-Injection в скрипте логина в CMS. Вы конечно скажете – бред, не может быть, но всё-же такие случаи встречаются в моей практике и не раз.

Более сложный вариан для взломщиков – когда пароли хешируют. Обычно, встречаются варианты когда пароль просто хеширую при помощи md5 или других функций. Хотя тут, народ исхитрился иметь небольшие базы готовых хешей. Значит и тут пароли могут пострадать – вопрос размера базы хешей и то, какой это хеш. Конечно, все пароли не раскроют. Часто в базе – словари, фразы, а всякие “kld8jSkhYakKd772” там наврятли будут – всё зависит от размера базы, вашей удачи и, иногда, от теории вероятности (про коллизию не забываем, да).

Итак, в резюме, получается, что с паролями излишняя параноидальность вам/нам не помешает. Если вы поманипулируете с паролями пользователей (да и администраторов) – вам это не повредит ни в коем случае. Сделать пару SALT, докинуть чего из “логина”, добавить битовую операцию, потом сделать base64 енкодинг и только потом хеш – это наверняка затруднит восстановление ворованного пароля.

А что вы посоветуете?

Что я знаю о register_globals

Постоянно сталкиваюсь с серверами, где по умолчанию register_globals = On. Да, мне ничто не мешает взять .htaccess и отключить: php_value register_globals Off или написать письмо администратору сервера. Однако, иногда и сам забываешь о той угрозе, которую несёт в себе register_globals = On.

Для того, что-бы воспользоваться чем-то, что даёт вам register_globals = On, нужно либо иметь на руках исходники скрипта, либо быть очень счастливым с…м сыном (ну ещё есть шанс, что ошибки, выбрасываемые PHP вам помогут). Естественно, речь идёт о Global Scope.

На днях я перебирал один маленький сайтик, где PHP используется только для того, что бы можно было удобно и быстро подключить хедер и футер, меню и для страниц легко менять тайтл и мета данные. Такой классический сайт из учебника, плюс несколько наворотов, которые позволяют кешировать сайт на стороне клиента так, как будто это статически HTML. Теоретически, если отключить expose_php и сделать ServerSignature Off, а ServerTokens Prod мало кто догадается, что это PHP.
И нашёл я там одну переменную, которая отвечала за подключение отдельного CSS для страницы. Она была задекларированна не во всех файлах, а толxко там, где требовался дополнительный CSS файл. Именно в тех файлах, где эта переменная не была установлена, её можно было установить при помощи GET, POST или COOKIE (ведь мы помним о variables_order). Плюс никакой фильтрации. Идеальное место для XSS type1 атаки. Но для такой атаки имя переменной нужно знать.

Есть ещё такой стиль программирования, когда массивы никто не декларирует, а сразу к ним обращается. Я имею ввиду, что некоторые программисты не пользуются таким выражением: $arr = array ();, а сразу выполнят присваивание: $arr[] = ‘some value’;. Опять же через GET, POST или COOKIE в таком случае можно передать первые ключи для массива $arr. И тогда сколько таких переменных есть у них, которые можно поменять, зная их имена?

Вроде и нет ничего страшного, действует только на Global Scope, надо знать название переменной, но вы не можете гарантировать, что копия сайта е попала в руки мальчишам-плохишам. Так, однажды, получив исходники одной CMS, можно досконально изучить её код, найти уязвимые места, а потом вскрыть этак сайтов 10-15 с одной и той-же ошибкой. Именно так однажды случилось с CMS моего знакомого: CMS решал, человек залоглен или нет, опираясь на онду переменную $admin_id. Стоило сделать запрос admin.php?admin_id=1 и коробочка раскрылась и даже не смотря на то, что этот $admin_id должен был придти из сессии – н там не был установлен (я взял новую сессию) и скрипт пощитал, что всё впорядке и можно пользоваться.

Короткое резюме: пишите свои проекты с учётом register_globals = Off; если вы уверены, что вами написанный проект отлично работает с register_globals = Off, а на сервере register_globals = On – отключите register_globals, так как в редких случаях это может вызвать неадекватное поведения вашего проекта; если вам приходиться работать со скриптами, которые требуют register_globals = On (только в том случае, если это необходимо), будьте предельно бдительны, не сделайте глупых ошибок, связанных со свойством register_globals = On, а также проведите аудит скрипта на возможные ошибки – вы последний, кто его редактировал и все бочки посыпятся на вас.

Заметки о загрузке файлов с PHP

Знаете, я не специалист по безопастности, но постоянно встречаюсь с элементарными проблемами любого WEB-программиста. А за последнюю неделю в жизни/работе призошло не мало изменений, связанных с безопасностью. Первое что очень сильно повлияло – это PHP Security conference, на котором показали на сколько бывают глупы создатели сайтов. Второе – это то, что я уже проверить и накопать на окружающих сайтах и своём коде.

Сегодня, к примеру, после работы, я копнул одну маленькую форму, которой посетители элементарно загружают картинки на сервер, скрипт меняет их размер на заданный в системе.

Элементарная форма. Элементарный скрипт. Для того, что-бы нормально обработать картинку на “любом” сервере, её в начале нужно двигать в свой каталог на сервере. Чаще всего это где-то внутри DocumentRoot, и в редких случаях вне его. Почему? А никто не отменял случаев с включенным open_basedir. И вроде нет ничего опасного в том, что я написал, но, если файл попал в DocumentRoot – его можно соответственно вызвать броузером. А если вы не проверили что это за файл – могут быть проблемы. Ведь могли загрузить и PHP или Perl скрипт, в котором может быть бог знает что.

И я не буду скрывать, что я купился на этой форме. Да, тут я лажанулся. Я понадеялся на getimagesize. Оказалось – зря. В первую очередь нужно проверять расширение файла в массиве $_FILES[‘userfile’][‘name’] и ещё перед выполнением move_uploaded_file. И не забудьте провериться перед этим с is_uploaded_file.

Вот на расширении файла я и попался. Я этим (по непонятным мне причинам) очень сильно принебрёг и понадеялся на функцию getimagesize. А вот она то и подвела. Оказывается (установлено экспериментальным путём на локальном PHP версии 4.4) – при загрузке файла с расширением .php (помните, я этим принебрёг), если в этот файл всунуть первые 128 байт из PNG файла, getimagesize считает что это картинка. Соответственно PHP файл попал на сервер, а что дальше бывает, я вам рассказывать не буду.

Некоторые говорят, что нужно ещё проверять $_FILES[‘userfile’][‘type’], но доверять ему нельзя – это данные, которые нам посылает клиент и они могут быть легко потделаны на строне клиента.

Загружая файл, не рекомендуют полностью доверят и $_FILES[‘userfile’][‘name’] – только профильтровав, или только расширение. Если же его прямо вписать, возможна атака путём подделки имени файла (скажем впишут вам ../../../index.php, а если у вас suEXEC или su_php можно и пострадать). Причём, они (создатели PHP) даже не подумали это дело отфильтровать автоматически, хотя в примере Validating file uploads создатели мануала об этом подумали (сам пример на данный момент мне не понятен).

Если разобрать пример Validating file uploads, а мне он виден вот таким совсем не понятно, каким образом они установили что происходит “Possible file upload attack!”? Ведь файл будет сдвинут в каталог /var/www/uploads/, а если он не будет сдвинут – следовательно, либо нету прав на это, либо исходный файл пропал. По моему, там должна была функция is_uploaded_file, а move_uploaded_file уже потом.

Итак, короткое резюме: проверять файл по разрешению и разрешать загружать только те файлы, которые 100% не будут выполнены сервером как скрипты (я бы разрешал загружать только картинки); не доверять $_FILES[‘userfile’][‘name’], проверять и фильтровать её, если используете; если файл переноситься во временное место для обработки, постарайтесь это место либо держать вне DocumentRoot, либо закрыть его при помощи .htaccess; не доверять вообще $_FILES[‘userfile’][‘type’]; пользуйтесь функциями is_uploaded_file и move_uploaded_file; проверяйте и перепроверяйте параметры.

Если у вас есть комментарии на эту тему, советы или замечания какие – пишите.