Почему предварительный запрос OPTIONS аутентифицированного запроса CORS работает в Chrome, но не в Firefox?

Я пишу JavaScript-клиент, который будет включен на сторонние сайты (подумайте, кнопка Facebook Like). Необходимо получить информацию из API, который требует базовой HTTP-аутентификации. Упрощенная настройка выглядит следующим образом:

Сторонний сайт включает этот фрагмент на своей странице:

<script 
async="true"
id="web-dev-widget"
data-public-key="pUbl1c_ap1k3y"
src="http://web.dev/widget.js">
</script>

widget.js вызывает API:

var el = document.getElementById('web-dev-widget'),
    user = 'token',
    pass = el.getAttribute('data-public-key'),
    url = 'https://api.dev/',
    httpRequest = new XMLHttpRequest(),
    handler = function() {
      if (httpRequest.readyState === 4) {
        if (httpRequest.status === 200) {
          console.log(httpRequest.responseText);
        } else {
          console.log('There was a problem with the request.', httpRequest);
        }
      }
    };

httpRequest.open('GET', url, true, user, pass);
httpRequest.onreadystatechange = handler;
httpRequest.withCredentials = true;
httpRequest.send();

API был настроен для ответа с соответствующими заголовками:

Header set Access-Control-Allow-Credentials: true
Header set Access-Control-Allow-Methods: "GET, OPTIONS"
Header set Access-Control-Allow-Headers: "origin, authorization, accept"
SetEnvIf Origin "http(s)?://(.+?\.[a-z]{3})$" AccessControlAllowOrigin=$0
Header set Access-Control-Allow-Origin %{AccessControlAllowOrigin}e env=AccessControlAllowOrigin

Обратите внимание, что Access-Control-Allow-Origin установлен на Origin вместо использования подстановочного знака, потому что я отправляю запрос с полномочиями (withCredentials).

Теперь все готово для выполнения асинхронного междоменного запроса с проверкой подлинности, и он отлично работает в Chrome 25 на OS X 10.8.2. В Dev Tools я вижу сетевой запрос для OPTIONS запрос до GET запрос, и ответ возвращается, как ожидалось.

При тестировании в Firefox 19 в Firebug к API не появляются сетевые запросы, и эта ошибка регистрируется в консоли: NS_ERROR_DOM_BAD_URI: Access to restricted URI denied

После долгих поисков я обнаружил, что в соответствии с комментариями Gecko не позволяет напрямую вводить имя пользователя и пароль в межсайтовый URI. Я предположил, что это было от использования необязательных параметров пользователя и пароля для open() поэтому я попробовал другой метод создания аутентифицированных запросов, который заключается в кодировке учетных данных Base64 и отправке в заголовке авторизации:

// Base64 from http://www.webtoolkit.info/javascript-base64.html
auth = "Basic " + Base64.encode(user + ":" + pass);

...
// after open() and before send()
httpRequest.setRequestHeader('Authorization', auth);

Это приводит к 401 Unauthorized ответ на OPTIONS запрос, который приводит к поиску в Google, например: "Почему это работает в Chrome, а не в Firefox!?" Тогда я понял, что попал в беду.

Почему это работает в Chrome, а не в Firefox? Как я могу получить OPTIONS запрос отправить и ответить последовательно?

4 ответа

Решение

Почему это работает в Chrome, а не в Firefox?

В спецификации W3 для предварительных запросов CORS четко указано, что учетные данные пользователя должны быть исключены. В Chrome и WebKit есть ошибка, из-за которой OPTIONS запросы, возвращающие статус 401, все еще отправляют последующий запрос.

В Firefox есть связанная ошибка, которая заканчивается ссылкой на список рассылки общедоступных веб-приложений W3, в котором требуется изменить спецификацию CORS, чтобы разрешить отправку заголовков аутентификации на OPTIONS запрос в пользу пользователей IIS. По сути, они ждут, чтобы эти серверы были устаревшими.

Как я могу получить OPTIONS запрос отправить и ответить последовательно?

Просто попросите сервер (API в этом примере) ответить на OPTIONS запросы без аутентификации.

Кинви проделал хорошую работу, подробно остановившись на этом, а также связался с проблемой API-интерфейса Twitter, в которой интересно описал проблему catch-22 этого точного сценария за пару недель до того, как были поданы какие-либо проблемы с браузером.

Это старый пост, но, возможно, это поможет людям решить проблему с CORS. Для завершения основной проблемы авторизации вам следует избегать авторизации запросов OPTIONS на вашем сервере. Это пример конфигурации Apache. Просто добавьте что-то подобное в ваш VirtualHost или Location.

<LimitExcept OPTIONS>
    AuthType Basic
    AuthName <AUTH_NAME>
    Require valid-user
    AuthUserFile <FILE_PATH>
</LimitExcept>

Это было особенно для меня. Я отправляю заголовок с именем 'SESSIONHASH'. Нет проблем для Chrome и Opera, но Firefox также хочет, чтобы этот заголовок был в списке "Access-Control-Allow-Headers". В противном случае Firefox выдаст ошибку CORS.

Для справки: причина хорошо объяснена в другом ответе , а Firefox 87+ предоставляет обходной путь, как первоначально объяснялось здесь .

Начиная с Firefox 87 (выпущенного в марте 2021 г.), в about:config можно установить указанные ниже настройки , а именно в редакторе конфигурации Firefox:

      network.cors_preflight.allow_client_cert: true

Из :

Корпорации, использующие клиентские сертификаты TLS, могут перевернуть network.cors_preflight.allow_client_certпредпочтение получить совместимую с Google Chrome обработку протокола CORS. В частности, в отличие от стандарта Fetch, при этом сертификаты клиента TLS будут передаваться вместе с предварительной отправкой CORS. Дополнительную информацию см. в

Использованная литература:

Другие вопросы по тегам