Можно ли защитить jsonp токеном csrf? - PullRequest
1 голос
/ 03 июня 2011

Вариант использования: В ситуации с одним доменом я хочу использовать jsonp для передачи данных, просто потому, что это происходит раньше в запросе, чем любой ajax или iframe транспорт. Кроме того, я хочу, чтобы эти данные кэшировались как обычный файл js.

Я не хочу предоставлять эти данные другим доменам.

Итак, я подумал о следующих приемах, чтобы предотвратить csrf:

  1. токен csrf, отправленный в качестве параметра GET с запросом.
  2. Еще один токен csrf, который хранится в переменной до выполнения jsonp. Таким образом, jsonp вызовет функцию, только если найдет, что эта переменная имеет правильное значение.

Это двойное параноидальное решение, потому что я не уверен, является ли какое-либо из этих двух пуленепробиваемым.

Вопросы будут:

  • Безопасно ли отправлять токен csrf в параметре GET? Или что я могу сделать, чтобы сделать его безопасным?
  • Может ли кто-то из другого домена заглянуть в исходный код js, чтобы обойти трюк с номером (2)?

Чтобы не начинать с нуля, вот документ, который я считаю полезным:
https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet
(но может быть, я что-то там пропустил)

Особенно интересно: «Раскрытие токена в URL», которое, я думаю, я до сих пор не до конца понял во всех его последствиях.

EDIT:
Другие решения, о которых можно подумать:

3. проверка реферера. Не достаточно безопасно, афаик. Особенно, если файл js кэшируется в браузере.
4. Шифрование. Вероятно, не стоит усилий ..

РЕДАКТИРОВАТЬ II:
Для пояснения: «ситуация с одним доменом» означает, что html-страница и jsonp обслуживаются из одного домена. Клиентский компьютер может быть где угодно.

РЕДАКТИРОВАТЬ III:
Я хочу быть таким же безопасным, как если бы я обслуживал обычный json или html / xml (запрашиваемый с помощью ajax или iframe) вместо jsonp. Как вы, вероятно, знаете, json и html / xml защищены политикой того же происхождения, а jsonp - нет.

Я хочу использовать токены csrf для достижения такой же защиты с помощью jsonp. Токены csrf, очевидно, будут обслуживаться со страницей html.

РЕДАКТИРОВАТЬ IV:
Вот как будет выглядеть трюк № 2.
HTML страницы:

<script type="text/javascript">
  var token1 = 'upoihjvpaoijpoj';
</script>
<script type="text/javascript" src="xyz/jsonp.js?token2=o8976987698540"></script>

А потом в xyz / json.js? Token2 = ..:

if (token1 == 'upoihjvpaoijpoj') {
  json_callback(data);
}

РЕДАКТИРОВАТЬ V:
Это достаточно сложный вопрос, поэтому я должен добавить пример из реальной жизни. Я не помню точный случай использования, когда я отправил этот вопрос, поэтому я просто пишу что-то, что подходит.

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

Первой идеей было бы загрузить меню с запросом XHR. На стороне сервера мы можем проверить cookie / сессию, чтобы проверить, вошел ли пользователь в систему, и предоставить персонализированное меню.

Но XHR может начать загружаться только после того, как остальная часть страницы уже там. Javascript, с другой стороны, может начать загрузку, как только браузер анализирует заголовок HTML. Таким образом, мы могли бы надеяться на выигрыш в производительности, если бы обслуживали данные меню с помощью javascript / jsonp вместо XHR.

Теперь сценарий CSRF:

  • Пользователь входит на oursite.com, который создает cookie-файл сеанса.
  • Затем тот же пользователь посещает сайт злоумышленника evil.com, на котором есть тег сценария для запроса (персонализированного) oursite.com/menudata.js.
  • Из-за политики одного и того же происхождения скрипты на evil.com не могут напрямую проверять содержимое файла menudata.js. Вместо этого он просто запустит menudata.js.

menudata.js может выглядеть так:

spawnMenu({...});

Или spawnMenu может быть динамически выбранной строкой, основанной на параметрах запроса.Если menudata.js выполняется на evil.com, злоумышленники, работающие на этом сайте, могут предоставить функцию с таким именем, а затем «позвонить домой» и украсть данные персонализированного меню.

Теперь идея (пункт 2 изпервоначальный вопрос) было сделать что-то вроде этого в menudata.js:

if (secret_var === 'opijpoijpoizvnpoiq92823pjnfn') {
  spawnMenu({...});
}

Сначала это выглядит вполне нормально.evil.com не знает secret_var, поэтому при запуске menudata.js он ничего не сделает.

Но потом я услышал, что есть некоторые неприятные приемы, с помощью которых можно «переписать» довольно базовые части js.Например, заменить обычный способ, которым материал приводится к строке.Может быть, даже заменить операторы сравнения?Таким образом, скрипт на evil.com мог бы обмануть нашу миленькую проверку.

Ответы [ 4 ]

0 голосов
/ 06 февраля 2013

Если данные доступны через JSONP, нет способа защитить их от третьих лиц.Единственное решение, которое я могу придумать, - это использовать канал за пределами границ, то есть прямой вызов API или своего рода секретный токен.В лучшем случае вы можете истечь токен CSRF после каждого обращения к нему, но это может привести к возможному отказу в обслуживании.

0 голосов
/ 03 июня 2011

Пока ваш токен csrf меняется на запрос (так что кто-то, кто слушает, не может выполнить атаку воспроизведения), все будет в порядке.

Не уверен насчет внезапного понижения голосов, но я проделал большую работу по обеспечению безопасности с Foursquare, Netflix и (lol) AOL. Маркер CSFR не похож на cookie. Злоумышленник не может угадать ваш токен CSRF. Это отправляется клиенту при входе в систему или находится на веб-странице. Он не отправляется автоматически со всеми запросами, такими как cookie, поэтому злоумышленник не может заставить ваш браузер отправить его с тегом img или каким-либо другим интересным делом. Он включен в вашу среду приложений.

0 голосов
/ 21 сентября 2012

Швы легко решить с помощью псевдокода.

По каждому запросу делай.

if(!session['csrf-token']) {
  session['csrf-token'] = randomString();
}

На странице, где вы хотите кэшируемых данных.

<script src="/some_cacheable_data.js?<%= session['csrf-token'] %>"></script>

В рендеринге JavaScript.

if(params['csrf-token'] == sesssion['csrf-token']) {
  renderData();
}
0 голосов
/ 03 июня 2011

Вне моей головы ...

  1. Настройте свой веб-сервер так, чтобы ограничивать доступ к вашему веб-сервису для клиентов в вашей собственной сети.

  2. Рефакторинг веб-службы для отклонения запросов от неизвестных хостов.

  3. Используйте SSL для своей веб-службы и требуйте аутентификацию при доступе.

  4. Вставьте прокси между клиентами и вашим веб-сервисом, чтобы ограничить доступ на основе сети, адреса или домена.

РЕДАКТИРОВАТЬ

В отношении ваших комментариев:

1 и 2 - Рефереры могут быть подделаны.Подделать IP-адрес намного сложнее.

3 - Да, а использование аутентификации с SSL делает атаки «человек посередине» более сложными.(У меня было соблазн сказать, что это невозможно, но это будет гордыня.: -)

4 - Я предложил это как вариант, если у вас нет доступа или вы не можете настроить свой веб-сервер.

Но это в основном спорный вопрос, так как либо я неправильно понял, либо вы изменили условия вопроса.

Первоначально вы указали, что это была «та же ситуация с доменом», которую я имел в виду, что клиенты и веб-сервисы существовали в ограниченных контролируемых местах (то есть в одной сети).клиенты вашего веб-сервиса, которые будут расположены в любом месте, будут работать только в варианте 3.

Однако уже есть стандартные средства ограничения доступа к веб-сервису (или его частям).

Для каждого авторизованного клиента создайте уникальный «ключ API» и потребуйте от любых вызовов защищенной функциональности включить этот токен.Веб-служба отклоняет вызовы в защищенные области без действительного ключа.

Используйте SSL (https), чтобы ограничить возможность отслеживания ключей и, если (когда) окажется, что кто-то нарушил вашу безопасность (например,поделившись своим уникальным ключом с другими), просто отзовите этот ключ, удалив его из списка авторизованных ключей доступа.

RE-EDIT

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

Итак, ваша первоначальная идеяможет показаться вашим лучшим выбором, если:

  • каждый ключ был сгенерирован с использованием неочевидного алгоритма (например, MD5 для случайных данных)
  • ключ был встроен в источник JavaScript ииспользуется для доступа к защищенным данным через AJAX
  • после использования ключа, он сразу становится недействительным и не может быть использован agaв

Но (и это огромно, но!),

  1. нет способа гарантировать, что исходный запрос страницы поступил от пользователя, а не от автоматическогопроцесс, предназначенный для подделки вашего сервиса.

  2. После того, как данные были получены браузером, они подвергаются проверке любым лицом, использующим различные инструменты мониторинга (например, firebug).

  3. Если данные затем представлены на веб-странице, их можно легко очистить (с помощью копирования / вставки).

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

...