Достаточно ли проверки сервера заголовков X-Requested-With для защиты от CSRF для приложения, управляемого Ajax?
Я работаю над приложением, полностью управляемым AJAX, где все запросы проходят через то, что в основном равнозначно главному контроллеру, который, по сути, выглядит примерно так:
if(strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest') {
fetch($page);
}
Достаточно ли этого для защиты от подделки межсайтовых запросов?
Довольно неудобно иметь вращающийся токен, когда вся страница не обновляется при каждом запросе.
Я полагаю, что я мог бы передавать и обновлять уникальный токен как глобальную переменную javascript с каждым запросом, но каким-то образом это кажется неуклюжим и в любом случае кажется небезопасным.
РЕДАКТИРОВАТЬ - Может быть, статический токен, как UUID пользователя, будет лучше, чем ничего?
РЕДАКТИРОВАТЬ № 2 - Как указала The Rook, это может быть непростой вопрос. Я читал спекуляции в обоих направлениях и слышал далекие шепоты о том, что старые версии флеш-памяти можно использовать для такого рода махинаций. Поскольку я ничего не знаю об этом, я назначаю вознаграждение любому, кто может объяснить, каков риск CSRF. В противном случае, я отдаю это Артефакто. Благодарю.
6 ответов
Я бы сказал, что этого достаточно. Если бы междоменные запросы были разрешены, вы все равно были бы обречены, потому что злоумышленник мог использовать Javascript для получения токена CSRF и использовать его в поддельном запросе.
Статический токен не очень хорошая идея. Токен должен генерироваться как минимум один раз за сеанс.
EDIT2 Майк не прав в конце концов, извините. Я не прочитал страницу, на которую я ссылался должным образом. Это говорит:
Простой межсайтовый запрос: [...] не устанавливает пользовательские заголовки с HTTP-запросом (например, X-Modified и т. Д.)
Поэтому, если вы установите X-Requested-With
запрос должен быть предварительно обработан, и если вы не ответите на предполетный период OPTIONS
запрос авторизации межсайтового запроса, он не пройдет.
РЕДАКТИРОВАТЬ Майк прав, начиная с Firefox 3.5, разрешены межсайтовые запросы XMLHttp. Следовательно, вы также должны проверить, если 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();
Да. Это признанный подход к защите CSRF.
https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet
Я не верю, что это безопасно. Одни и те же политики происхождения предназначены для предотвращения доступа к документам из разных доменов контента, который возвращается из другого домена. Вот почему проблемы XSRF существуют в первую очередь. В целом XSRF не заботится об ответе. Он используется для выполнения запроса определенного типа, например, действия удаления. В простейшей форме это можно сделать с правильно отформатированным тегом img. Предлагаемое вами решение предотвратит эту простейшую форму, но не защитит кого-либо от использования объекта XMLHttp для выполнения запроса. Вам необходимо использовать стандартные методы профилактики для XSRF. Мне нравится генерировать случайное число в javascript и добавлять его в cookie и переменную формы. Это гарантирует, что код также может записывать куки для этого домена. Если вы хотите больше информации, пожалуйста, смотрите эту запись.
Кроме того, чтобы упредить комментарии о том, что XMLHttp не работает в скрипте. Я использовал следующий код с Firefox 3.5, чтобы сделать запрос к Google из HTML, работающего в локальном домене. Содержимое не будет возвращено, но, используя 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");
Нет, это можно легко обойти, сделав запрос Cross-domain-Flash к серверу, который содержит этот заголовок и запрос с его учетными данными, см. Это: https://www.geekboy.ninja/blog/exploiting-json-cross-site-request-forgery-csrf-using-flash/?unapproved= 6685 &moderation-hash=91554c30888cfb21580f6873e0569da0
Лучший способ защиты от CSRF - сделать так, чтобы заголовок или параметр содержал секретный ключ для каждого запроса,
То, что вы делаете, безопасно, потому что xmlhttprequest обычно не подвержен подделке межсайтовых запросов.
Поскольку это проблема на стороне клиента, самый безопасный способ - проверить архитектуру безопасности каждого браузера:-)
(Это резюме; я добавляю этот ответ, потому что этот вопрос очень запутанный, давайте посмотрим, что говорят голоса)
Краткий ответ: нет. Любой злоумышленник просто использует Ajax для атаки на ваш сайт. Вы должны сгенерировать случайный токен с коротким, но не слишком большим сроком службы, который вы будете обновлять при каждом ajax-запросе.
Вы должны будете использовать массив токенов в javascript, так как у вас может быть несколько одновременно запущенных ajax-запросов.
Я не думаю, что это предлагает какую-либо защиту. Атакующий сайт все еще может использовать xmlhttprequest
для его межсайтового запроса обходите ваш чек.