Является ли сервер заголовка X-Requested-With достаточным для защиты от CSRF для приложения, управляемого ajax?

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

if(strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
    fetch($page);
}

Как правило, это достаточно для защиты от подделок поддонов?

Это довольно неудобно иметь вращающийся токен, когда вся страница не обновляется с каждым запросом.

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

EDIT - Возможно, статический токен, такой как пользовательский UUID, будет лучше, чем ничего?

РЕДАКТИРОВАТЬ # 2 - Как заметил Ладья, это может быть вопрос, связанный с волосами. Я читал спекуляции в обоих направлениях и слышал отдаленные шепоты о более старых версиях флеш-памяти, которые можно использовать для таких махинаций. Поскольку я ничего не знаю об этом, я предлагаю щедрость всем, кто может объяснить, как это риск CSRF. В противном случае я отдаю его Артефакто. Спасибо.

Ответ 1

Я бы сказал это достаточно. Если разрешены междоменные запросы, вы все равно обречены на то, что злоумышленник может использовать Javascript для извлечения токена CSRF и использовать его в поддельном запросе.

Статический токен - отличная идея. Маркер должен быть сгенерирован как минимум один раз за сеанс.

EDIT2 Майк не прав, извините. Я не читал связанную с ним страницу. В нем говорится:

Простой кросс-сайт-запрос - это тот, который: [...] Не устанавливает пользовательские заголовки с HTTP-запросом (например, X-Modified и т.д.).

Следовательно, если вы установите X-Requested-With, запрос должен быть предварительно обработан, и если вы не ответите на запрос перед полетом OPTIONS, разрешающий запрос на межсайтовый сайт, он не пройдет.

EDIT Майк прав, а с Firefox 3.5 межсайтовые XMLHttpRequests разрешено. Следовательно, вам также нужно проверить, соответствует ли заголовок Origin, когда он существует, ваш сайт.

if (array_key_exists('HTTP_ORIGIN', $_SERVER)) {
    if (preg_match('#^https?://myserver.com$#', $_SERVER['HTTP_ORIGIN'])
        doStuff();
}
elseif (array_key_exists('HTTP_X_REQUESTED_WITH', $_SERVER) &&
        (strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest'))
    doStuff(); 

Ответ 2

Я не считаю, что это безопасно. Те же политики происхождения предназначены для предотвращения доступа документов из разных доменов к содержимому, которое возвращается из другого домена. Вот почему проблемы XSRF существуют в первую очередь. Вообще XSRF не заботится об ответе. Он используется для выполнения определенного типа запроса, например, для удаления. В простейшей форме это можно сделать с правильно отформатированным тегом img. Ваше предлагаемое решение предотвратит эту простую форму, но не защитит кого-либо от использования объекта XMLHttp для запроса. Вам необходимо использовать стандартные методы предотвращения XSRF. Мне нравится генерировать случайное число в javascript и добавлять его в файл cookie и переменную формы. Это гарантирует, что код также может записывать файлы cookie для этого домена. Если вы хотите получить дополнительную информацию, см. эту запись.

Кроме того, чтобы упредить комментарии о том, что XMLHttp не работает в script. Я использовал следующий код с firefox 3.5, чтобы сделать запрос на google из html, запущенного в домене localhost. Содержимое не будет возвращено, но с использованием firebug вы можете увидеть, что запрос сделан.

<script>
var xmlhttp = false; 

if (!xmlhttp && typeof XMLHttpRequest != 'undefined') {
    try {
        xmlhttp = new XMLHttpRequest();
    } catch (e) {
        xmlhttp = false;
    }
}
if (!xmlhttp && window.createRequest) {
    try {
        xmlhttp = window.createRequest();
    } catch (e) {
        xmlhttp = false;
    }
}

xmlhttp.open("GET", "http://www.google.com", true);
xmlhttp.onreadystatechange = function() {
    if (xmlhttp.readyState == 4) {
        alert("Got Response");
        alert(xmlhttp.responseText)
    }
}

xmlhttp.send(null)
alert("test Complete");

Ответ 4

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

Ответ 5

Короткий ответ: нет. Любой злоумышленник просто использует Ajax для атаки вашего сайта. Вы должны создать случайный токен с коротким, но не слишком большим сроком жизни, который вы обновляете во время каждого запроса ajax.

Вам нужно будет использовать массив токенов в javascript, так как вы можете одновременно запускать несколько запросов ajax.

Ответ 6

То, что вы делаете, безопасно, потому что xmlhttprequest обычно не уязвим для подделки подделок.

Поскольку это проблема на стороне клиента, самым безопасным способом было бы проверить архитектуру безопасности каждого браузера: -)

(Это резюме, я добавляю этот ответ, потому что этот вопрос очень запутан, давайте посмотрим, что говорят голоса)