Сен 30 2008

Защита сайта от cross-site request forgery (CSRF)

Категория: Безопасностьgugglegum @ 00:54

Cross-site request forgery — это разновидность атаки на пользователя, при которой посещаемый веб-сайт выполняет несанкционированные действия, используя действующую авторизацию.

Предположим Вы зашли на сайт A, авторизовались там как пользователь (используя куки) и не завершая сессии зашли на сайт B, который использует CSRF-уязвимость сайта A и выполняет от Вашего имени какие-либо действия, например, постит сообщение, содержащее спам, поднимает репутацию какому-нибудь пользователю или что-нибудь еще в таком духе. А может быть еще и вот такой забавный сценарий, который некогда воплотился на сайте вконтакте.ру.

Прежде чем продолжить, хочу сразу сделать оговорку. CSRF — это в общем понимании не совсем уязвимость. Уязвимость — это когда при каких-то определенных условиях программа ведет себя не так, как задумывал ее разработчик. Но CSRF — это нормальное поведение всех сайтов в Интернете! Это можно сказать фича Интернета, которая иногда может быть использована во вред.

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

Иногда это бывает удобно. Например, мне как-то раз надоело вручную заполнять одни и те же заявки на неисправность у провайдера КабiNET и я на локалхосте создал форму один в один повторяющую поля и указал в качестве обработчика (action) обработчик на сайте провайдера. В результате, у меня была своя форма заявки, в которой уже были заведены основные шаблоны описаний неисправностей, нужно было лишь выбрать шаблон и нажать Отправить.

Это был пример мирного применения CSRF, но существуют естественно и враждебные, защиту от которых должен предусмотреть разработчик сайта. Ситуация осложняется тем, что враждебный сайт может использовать Javascript, чтобы произвести отправку формы (без всяких кликов, неважно GET или POST) в невидимом IFrame’е так, что пользователь даже не будет знать о том, что от его имени где-то что-то выполнилось. Страшно? :)

Как же защититься от враждебного применения CSRF?

В примитиве обработчик формы может просто проверять HTTP-заголовок Referer, где указывается с какого адреса пришел запрос и если адрес левый, то не обрабатывать запрос. Но этот заголовок не является обязательным и некоторые файрволлы (я точно знаю, что Agnitum Outpost Firewall) подменяют значение этого заголовка на название или адрес сайта файрволла. Поэтому полагаться на Referer все же не стоит.

Более грамотным решением является встраивание в форму дополнительного скрытого поля, содержащее какое-то значение, зависящее от текущей сессии пользователя. Самое простое и первое что приходит в голову — естественно идентификатор сессии. Враждебный сайт не сможет прочитать куку другого сайта (см. same origin policy) и соответственно не сможет подставить в скрытое поле правильное значение.

Используйте описанный прием всегда там, где теоретически возможно враждебное применение CSRF.

См. также описание CSRF на википедии.


Сен 29 2008

Несколько функций __autoload в одном коде на PHP

Категория: PHPgugglegum @ 05:35

С полгода назад я писал про нюансы автоматической загрузки классов с использованием __autoload(), и там указывал на проблему использования данного способа, заключающуюся в том, что функция с магическим именем “__autoload” может быть объявлена только одна. Поэтому, если Вы пишете свой фреймворк и использовали в нем функцию с этим именем, то другой фреймворк, использующий аналогичную функцию уже не сможет работать в паре с Вашим.

Я писал про то, что единственным решением проблемы автозагрузки классов из двух несвязанных фреймворков может быть лишь создание гибрида из двух __autoload-функций. При этом я сам, выносил функцию автозагрузки за пределы фреймворка, отдавая загрузку классов на откуп программиста, непосредственно использующего мой фреймворк.

Так было полгода назад. В последствии я ближе познакомился со Standard PHP Library (SPL) и осознал, что эта проблема давно решена при помощи функции spl_autoload_register, которая активизирует стэк из функций автозагрузки классов.

Разумеется, Вы должны использовать уникальное имя для своей функции автозагрузки, поэтому от имени “__autoload” лучше отказаться сразу. Можете использовать уникальный префикс типа “myframework_autoload” или завернуть эту функцию в отдельный класс автозагрузки. А еще лучше (в рассчете на будущее) использовать нэймспэйсы в PHP, хотя это сразу ограничит минимальную версию PHP до 5.3.0.

Когда Вы первый раз вызываете функцию spl_autoload_register(), она активизирует стэк функций автозагрузки классов и с этого момента функция с именем “__autoload” уже не вызывается автоматически, если только Вы не зарегистрируете и ее. Т.е. с момента вызова spl_autoload_register функция с именем “__autoload” перестает быть магической. И даже разрегистрирование всех функций автозагрузки при помощи spl_autoload_unregister не возвращает ее магическое предназначение.

Зарегистрированные функции вызываются в том же порядке, в каком они были зарегистрированы. Цепочка вызовов прекращается сразу как только очередная функция автозагрузки нашла и загрузила нужный класс. Исходя из этого, логичнее регистрировать функции автозагрузки в порядке частоты их предполагаемого использования (частые в начало). Продолжить чтение “Несколько функций __autoload в одном коде на PHP”


Сен 27 2008

Что нового нас ждет в PHP 5.3?

Категория: PHPgugglegum @ 13:26

Случайно наткнулся на сайте php.net на анонс альфа версии PHP 5.3, глянул список изменений и был настолько воодушевлен, что решил написать об этом здесь. Данное обновление принесет нам, пожалуй, самые ожидаемые нововведения в синтаксисе языка.

Ура! В PHP наконец-то появятся нэймспэйсы! Что это такое? Это разделяемые пространства имен, в которых Вы определяете классы и функции, чтобы они не пересекались там, где не надо. Случалось ли Вам объявлять какой-то класс, например, DB или Log, а потом при подключении какой-нибудь библиотеки или репозитария классов обнаружить, что там уже есть класс с точно таким же именем и это создает Вам проблемы. Особенно при использовании функции автозагрузки классов (см. __autoload).

И что в этой ситуации делают почти все разработчики библиотек? Правильно, приписывают префиксы к именам своих классов типа “MyFramework_ClassName”. При этом сами имена классов бывают довольно длинные, а в купе с префиксом и вовсе разростаются иной раз до 40 и более символов. Конечно, это неудобно каждый раз везде писать такие длинные имена, особенно внутри самого фреймворка.

Решение в нэймспэйсах — Вы просто объявляете в начале каждого файла с классом нэймспэйс и все Ваши классы в рамках этого нэймспэйса, будут друг с другом “на короткой ноге”. А чтобы обратиться из другого нэймспэйса нужно либо указать полное имя класса вместе с нэймспэйсом, либо импортировать нэймспэйс при помощи ключевого слова “use”.

Что еще примечательно, нэймспэйсы могут быть вложенными, т.е. образовывать иерархическую структуру, обращаться к которой можно через путь. Вам это ничего не напоминает? Да, именно так реализованы пакеты классов в Java. Только там это называются package, а здесь namespace.

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

Еще советую познакомиться с другим интересным нововведением в синтаксисе — “Late static binding“. Дело в том, что при статических вызовах self:: не является своего рода статичным аналогом $this, т.к. self:: ссылается на класс, в котором он используется, а не на тот, к которому идет обращение. Пример в документации очень наглядно демонстрирует проблему и решение, при помощи нового синтаксиса “static::”. Как минимум это должно упростить использование паттерна Singleton когда класс, реализующий данный паттерн, необходимо расширить (наследовать).


Сен 03 2008

Google Chrome. Первые впечатления

Категория: Googlegugglegum @ 12:50

Вчера мой друг Макс прислал мне ссылку на подборку новостей о том, что Google опубликовал анонс выпуска собственного браузера “Chrome”, основанного на исходниках Apple WebKit (Safari). По правде сказать, для меня это было неожиданностью. Уже 4 года ходят слухи о том, что гугл вроде как собирается выпустить свой браузер, но воспринимались они как что-то очень недостоверное и далекое во времени. К тому же были и официальные заявления с опровержениями этих слухов — мол существующие браузеры их и так устраивают. А тут бац! Нате новый браузер.

Вчера же поздно вечером в 11 утра по дневному тихоокеанскому времени (в 22 часа по Москве) была прямая видео-трансляция презентации нового браузера в Google, где разработчики рассказывали о том, как они делали свой браузер и в чем его преимущества перед существующими. Я не буду перечислять все преимущества нового браузера, они уже перечислены до меня.

Просто я установил Chrome-beta себе на компьютер и могу поделиться первыми впечатлениями. Итак, первое впечателние: просто супер! Эргономика интерфейса на высшем уровне. Сделано так, чтобы было удобно пользоваться. Правда, удобство может обернуться обратной стороной, когда возникнет потребность расширять функционал посредством сторонних расширений. Надеюсь, разработчики предусмотрели и это. А пока что без расширений Chrome для меня просто забавная игрушка типа Opera. Странно, конечно, что разработчики взяли за основу движок Apple WebKit, а не Mozilla Gecko, но главное чтоб он хорошо работал и соблюдал стандарты по части рендеринга страниц.

Пожалуй, главным новшеством в Chrome является мульти-процессная архитектура (multi-process design). Это означает, что все вкладки и окна браузера живут не в одном процессе и делят процессорное время посредством искусственных механизмов в самом браузере, а в отдельных процессах и их разделением занимается операционная система. Теоретически это также должно позволить обрабатывать разные табы разными ядрами процессора на компьютерах с мультиядровыми процессорами. В Chrome есть один главный процесс самого браузера и по одному процессу на каждый непустой таб. Продолжить чтение “Google Chrome. Первые впечатления”