HTTP запрос от Flash и защита от CSRF — варианты решения

Общие вопросы по использованию фреймворка. Если не знаете как что-то сделать и это про Yii, вам сюда.
Ответить
Аватара пользователя
resurtm
Сообщения: 299
Зарегистрирован: 2010.12.19, 09:13
Откуда: Казахстан, Алма-Ата
Контактная информация:

HTTP запрос от Flash и защита от CSRF — варианты решения

Сообщение resurtm »

Привет всем!

Кто-нибудь занимался тем, чтобы подружить защиту от CSRF вкупе с запросами от Flash? Основная проблема такая: Flash не передает браузерные куки в HTTP запросе (YII_CSRF_TOKEN как раз таки и хранится в кукисах браузера) — это факт.

На ум приходят два решения и обхода такой проблемы:

1. Создать свой класс-потомок от CHttpRequest и сделать так, чтобы YII_CSRF_TOKEN хранился в сессии, а не в кукисах. Сессию восстановить по переданному session_id получается вполне. Возникает другой вопрос: почему YII_CSRF_TOKEN вообще всегда не хранить в сессии? Почему в Yii CSRF токен хранится именно в кукисах?

2. Просто самому ручками сохранить в сессии значение YII_CSRF_TOKEN при отдаче страницы с этой Флэшкой, а уже из восстановленной по session_id сессии получить этот YII_CSRF_TOKEN и сравнить с тем, что пришло от Флэшки.

Кто что думает? Какой метод более правилен и в целом приятнее?

Просто вырубать проверку CSRF токена как-то желания нет — зачем самому открывать потенциальные дыры в системе? :)
Аватара пользователя
resurtm
Сообщения: 299
Зарегистрирован: 2010.12.19, 09:13
Откуда: Казахстан, Алма-Ата
Контактная информация:

Re: HTTP запрос от Flash и защита от CSRF — варианты решения

Сообщение resurtm »

Короче сделал гибридным методом.

Восстанавливаем сессию по переданному ее идентификатору в POST запросе, а также форсируем изменение CSRF токена в HttpRequest. Токен берем из сессии восстановленной, а не из POST запроса (как это делают многие).

Код: Выделить всё

    // Обработчик события onBeginRequest
    public static function onBeginRequest()
    {
        if(isset($_POST['SESSION_ID'], $_POST[Yii::app()->request->csrfTokenName]))
        {
            $session=Yii::app()->session;
            $session->close();
            $session->sessionID=$_POST['SESSION_ID'];
            $session->open();
            
            Yii::app()->request->csrfToken=Yii::app()->session['csrfToken'];
            Yii::app()->request->cookies[Yii::app()->request->csrfTokenName]=
                new CHttpCookie(Yii::app()->request->csrfTokenName, Yii::app()->session['csrfToken']);
            
            Yii::trace(var_export($_REQUEST, true), 'application.components.Helper');
        }
    }
 
Указываем в конфиге то, что HttpRequest у нас свой:

Код: Выделить всё

    // Конфиг приложения.
    'components'=>array(
        'request'=>array(
            'class'=>'HttpRequest',
            'enableCsrfValidation'=>true,
            'enableCookieValidation'=>true,
        ),
    ),
 
Виджет во вьюшке:

Код: Выделить всё

                <div class="button">
                    <?php $this->widget('ext.uploadify.MUploadify',array(
                        'model'=>$userProfile,
                        'attribute'=>'avatar',
                        'script'=>url('signup/avatarUpload', array('key'=>$userSignup->key)),
                    )); ?>
                </div>
Сохраняем CSRF токен в сессии (изначально он хранится в кукисах). Делать это нужно в действии контроллера, которое отображает страницу с виджетом выше.

Код: Выделить всё

        // сохраняем CSRF токен в сессии — понадобится позже
        Yii::app()->session['csrfToken']=Yii::app()->request->csrfToken;
 
Наш новый класс HttpRequest. Пришлось извернуться с тем, что $_csrfToken в CHttpRequest приватен (ну оно и понятно, ситуация немного необычная).

Код: Выделить всё

<?php

class HttpRequest extends CHttpRequest
{
    protected $_customCsrfToken=null;
    
    public function setCsrfToken($csrfToken)
    {
        $this->_customCsrfToken=$csrfToken;
    }
    
    public function getCsrfToken()
    {
        if($this->_customCsrfToken!==null)
            return $this->_customCsrfToken;
        return parent::getCsrfToken();
    }
}
 
Механизм валидации загрузки файлов и самой обработки загрузки файлов стандартный (http://www.yiiframework.com/wiki/2/how- ... ng-a-model). Полноценный пример могу сделать, если кто тоже столкнется с этим. Основное преимущество всех этих свистоплясок — это честная и правильная защита от CSRF при помощи токенов даже тогда, когда запрос осуществляется из Flash. Многие просто берут значение из $_POST, хотя это не есть безопасно.

Упоминаю теги на всякий случай для тех, кто будет искать: SWF Upload, Flash Upload, Uploadify, AJAX, upload, Flash, CSRF, token, Cookie, Cookies, Flash Cookies.
Последний раз редактировалось resurtm 2011.11.11, 21:03, всего редактировалось 1 раз.
Аватара пользователя
resurtm
Сообщения: 299
Зарегистрирован: 2010.12.19, 09:13
Откуда: Казахстан, Алма-Ата
Контактная информация:

Re: HTTP запрос от Flash и защита от CSRF — варианты решения

Сообщение resurtm »

А, ну и патч в MUploadify (http://www.yiiframework.com/extension/muploadify/). Автор возможно внесет свои изменения чуть позже:

Код: Выделить всё

    protected function setBaseOptions(){
        // ...
        
        //$this->_options['scriptData'][$this->sessionKey]=session_id();
        $this->_options['scriptData'][$this->sessionKey]=Yii::app()->session->sessionID;
        if(Yii::app()->request->enableCsrfValidation)
            $this->_options['scriptData'][Yii::app()->request->csrfTokenName]=Yii::app()->request->csrfToken;
    }
 
Ответить