Защита от CSRF в Zend Framework
16 сентября 2011 Zend 40239 просмотров
CSRF (Сross Site Request Forgery) — вид web-атак, использующий недостатки протокола HTTP. Если жертва заходит на сайт, созданный злоумышленником, от её лица тайно отправляется запрос на другой сервер (например, на сервер платёжной системы), осуществляющий некую вредоносную операцию (например, перевод денег на счёт злоумышленника). Для осуществления данной атаки, жертва должна быть авторизована на том сервере, на который отправляется запрос, и этот запрос не должен требовать какого-либо подтверждения со стороны пользователя, который не может быть проигнорирован или подделан атакующим скриптом.

Существует несколько путей защиты от такого типа атак. Проверка заголовка запроса, создание форм подтверждения, создание уникального токена. Можно проверять значение $_SERVER['HTTP_REFERER'], но это неправильно, т.к. его можно подделать. Создание форм подтверждения тоже является не очень удобным способом. Поэтому создание токена является наиболее надежным и качественным вариантом.

В Zend Framework существует специальный элемент формы Zend_Element_Hash, добавляющий скрытый элемент на форму. Hash хранится в сессии и сравнивается при вызове метода isValid().
$form = new Zend_Form();
$token = new Zend_Form_Element_Hash('csrf_token');
$token->setSalt(md5(microtime() . uniqid()));
$form->addElement($token);
if ($form->isValid($data)) {
    // ...
}
Метод isValid() сравнит 2 значения (переданное формой и сохраненное в сессии). Также стоит отметить, что если вы имеете на странице несколько форм с элементом Hash, то вам нужно позаботиться об уникальности имен этих элементов.

Не очень удобно для каждой формы создавать свой элемент Hash, поэтому можно создать класс, наследуемый от Zend_Form и в конструкторе объявить добавление такого элемента к каждой форме.
class Lib_Form extends Zend_Form {
    public function __construct($formName, $options = null) {
        parent::__construct($options);
        $uniqueSalt = Zend_Crypt::hash('MD5', $formName . microtime());
        $csrfToken = new Zend_Form_Element_Hash($formName . '_csrf_token');
        $csrfToken->setSalt($uniqueSalt);
        $this->addElement($csrfToken);
    }
}
$form = new Lib_Form('my_unique_name');