Drupal → Защищаем модули от CSRF уязвимости

06.06.2010

Что такое CSRF

XSRF (англ. Сross Site Request Forgery — «Подделка межсайтовых запросов») — вид атак на посетителей веб-сайтов, использующий недостатки протокола HTTP. Если жертва заходит на сайт, созданный злоумышленником, от её лица тайно отправляется запрос на другой сервер (например, на сервер платёжной системы), осуществляющий некую вредоносную операцию (например, перевод денег на счёт злоумышленника). Для осуществления данной атаки, жертва должна быть авторизована на том сервере, на который отправляется запрос, и этот запрос не должен требовать какого-либо подтверждения со стороны пользователя, который не может быть проигнорирован или подделан атакующим скриптом. via wikipedia

Пример CSRF уязвимости в Drupal

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

Список материалов

Если ссылки имеют адрес вида nodemngr/delete/{nid} и удаление материала происходит без страницы подтверждения, то этот модуль подвержен CSRF атакам.

Любой пользователь, который имеет права оставлять комментарии, может написать в одном из них что-нибудь типа:

текст...
<img src="/nodemngr/delete/13" />
текст...

При открытии, администратором, страницы с этим комментарием, будет удалена нода с номером 13.

Защита от CSRF

Для модификации данных (например удаления материалов) желательно использовать формы, созданные с помощью Form API. Они защищены от CSRF скрытым полем с уникальным значением — токеном. Токены создаются на основе сессии пользователя, приватного ключа сайта и произвольного параметра, что гарантирует их уникальность для каждого юзера.

Если же нет возможности или желания использовать Form API, можно самостоятельно добавлять к ссылкам токены и делать их проверку в соответствующем калбаке меню.

Для примера, модифицируем созданный ранее модуль Node Manager. Сделаем удаление нод без промежуточной страницы:

/**
 * Реализация hook_menu()
 */
function nodemngr_menu()
{
    $items['nodemngr'] = array(
        'title' => 'Список нод',
        'page callback' => 'nodemngr_list',
        'access arguments' => array('administer nodes'),
        'type' => MENU_CALLBACK,
    );

    $items['nodemngr/delete/%nodenmgr_node'] = array(
        'title' => 'Delete node',
        'page callback' => 'nodemngr_delete',
        'page arguments' => array(2),
        'access arguments' => array('administer nodes'),
        'type' => MENU_CALLBACK,
    );

    return $items;
}


/**
 * Menu callback
 * Выводит список нод в табличной форме
 */
function nodemngr_list()
{
    $nodes = db_query("SELECT nid, title, created FROM {node} ORDER BY nid DESC");
    $tableHeader = array('Nid', 'Заголовок', 'Дата создания', 'Действия');
    $tableData = array();
    
    while ($node = db_fetch_object($nodes))
    {
        $actions  = l('изменить', 'node/' . $node->nid . '/edit', array('alias' => true)) . ' | ';
        $actions .= l('удалить', 'nodemngr/delete/' . $node->nid, array(
            'alias' => true,
            'query' => array('token' => drupal_get_token($node->nid)) // токен
        ));
        
        $tableData[] = array(
            $node->nid,
            $node->title,
            format_date($node->created, 'small'),
            $actions,
        );
    }
    
    return theme('table', $tableHeader, $tableData);
}


/**
 * Wildcard loader
 * Возвращает информацию о ноде
 */
function nodenmgr_node_load($nid)
{
    $node = db_query("SELECT nid, title FROM {node} WHERE nid = %d", $nid);
    return db_fetch_object($node);
}


/**
 * Menu callback
 * Удаление ноды
 */
function nodemngr_delete($node)
{
    // проверка токена
    if (!drupal_valid_token($_GET['token'], $node->nid))
    {
        return drupal_access_denied();
    }
 
    db_query('DELETE FROM {node} WHERE nid = %d', $node->nid);            
    drupal_set_message('Нода удалена');
    drupal_goto('nodemngr');
}

Вся работа с токенами происходит с помощью двух функций: drupal_get_token() и drupal_valid_token().

Исходники модуля.
— По мотивам статьи Protecting your Drupal module against Cross Site Request Forgeries.

Написанное актуально для
Drupal 6.x
Похожие записи

Добавить комментарий