Куки не доступны в JavaScript (и в инструментах разработки), но отправляются вместе с запросом XHR (httponly не используется)

Я использую как интерфейсное, так и фоновое приложение в другом домене с авторизацией на основе сеанса. Я настроил рабочую конфигурацию CORS, которая работает как положено на localhost (например, из порта :9000 в порт :8080). Как только я развертываю приложения в защищенных доменах (оба домена поддерживают только HTTPS), файл cookie CSRF больше не доступен в JavaScript, что приводит к неправильному последующему запросу внешнего интерфейса (отсутствует заголовок CSRF).

Файл cookie устанавливается сервером в Set-Cookie заголовок без использования HttpOnly флаг. Это фактически установлено где-то в браузере, потому что последующий запрос содержит и cookie сессии, и cookie CSRF. Попытка получить доступ к нему с помощью JavaScript (используя, например, document.cookie в консоли) возвращает пустую строку. DevTools в Chrome не показывают файлы cookie в домене переднего плана (домен не указан даже в списке).

Я ожидаю, что файл cookie будет установлен и будет виден в текущем домене (входной домен). Я использую withCredentials флаг библиотеки axios.

Есть ли у вас какие-либо идеи, почему cookie не может быть доступен из JavaScript или из DevTools в Chrome? Это как-то связано с Strict-Transport-Security заголовок?


Заголовки

1. Начальный заголовок ответа GET

HTTP/1.1 401 Unauthorized
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: https://[my-frontend-domain]
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Content-Encoding: gzip
Content-Type: application/json;charset=UTF-8
Date: Wed, 20 Sep 2017 11:57:07 GMT
Expires: 0
Pragma: no-cache
Server: Apache-Coyote/1.1
Set-Cookie: CSRF-TOKEN=[some-token]; Path=/
Vary: Origin,Accept-Encoding
X-Content-Type-Options: nosniff
X-Vcap-Request-Id: [some-token]
X-Xss-Protection: 1; mode=block
Content-Length: [some-length]
Strict-Transport-Security: max-age=15768000; includeSubDomains

2. Последующий заголовок запроса POST

POST /api/authentication HTTP/1.1
Host: [my-backend-host]
Connection: keep-alive
Content-Length: [some-length]
Pragma: no-cache
Cache-Control: no-cache
Accept: application/json, text/plain, */*
Origin: [my-frontend-host]
User-Agent: [Google-Chrome-User-Agent]
Content-Type: application/x-www-form-urlencoded
DNT: 1
Referer: [my-frontend-host]
Accept-Encoding: gzip, deflate, br
Accept-Language: de-DE,de;q=0.8,en-US;q=0.6,en;q=0.4,de-CH;q=0.2,it;q=0.2
Cookie: [some-other-cookies]; CSRF-TOKEN=[same-token-as-in-the-previous-request]

Этот запрос должен содержать заголовок CSRF, который будет автоматически добавлен, если файл cookie будет доступен с помощью JavaScript.

4 ответа

Решение

Короче говоря, невозможно получить доступ к файлам cookie разных источников, document.cookie может получить доступ только к текущим ( или родительским) файлам cookie.

Намек на то, что это является основной причиной, был ssc-hrep3, упомянув "оба домена" в своем вопросе.

Эту ошибку очень легко совершить, переключаясь с локального хоста, использующего только разные порты для внутренних и внешних серверов, на тот, который использует два разных хоста. Это будет работать локально, потому что cookie-файлы распределяются между портами и не будут работать, когда используются два разных хоста. (В отличие от некоторых других проблем CORS, которые также будут обнаружены локально)

См. Ответ ssc-hrep3 для получения дополнительной информации и обходного пути.

TL; DR: доступ для чтения к междоменным файлам cookie невозможен. Добавление токена CSRF в заголовок ответа было бы решением. Другое решение для полного обхода CORS и междоменных запросов - использование обратного прокси-сервера.


проблема

Как указано в моем вопросе выше, JavaScript часть моего интерфейса (например, https://example1.com пытается получить доступ к HttpOnly печенье из моего бэк-энда, например, https://example2.com, Чтобы иметь возможность доступа к удаленному API с помощью JavaScript, я использую CORS. Это позволяет запросам проходить. я использую withCredentials: true на передней стороне и Access-Control-Allow-Credentials: true на задней стороне. Set-Cookie Затем заголовок устанавливает cookie на внутреннем источнике, а не на внешнем источнике. Следовательно, cookie не видны ни в DevTools, ни в document.cookie команда в JavaScript.

Файлы cookie, установленные на внутреннем источнике, всегда являются частью запроса к внутреннему интерфейсу через CORS. Однако мне понадобится доступ к содержимому файла cookie CSRF, чтобы добавить токен в заголовок запроса (чтобы предотвратить атаки CSRF). Как я выяснил, нет способа читать (или записывать) куки-файлы из другого домена с помощью JavaScript - независимо от того, какой параметр CORS используется (см. Эти ответы Stackru: [1], [2]). Браузер ограничивает доступ к содержимому куки-файлов происхождением из одного домена.

Решения

Это приводит к выводу, что нет возможности доступа к содержимому HttpOnly cookie другого домена. Обойти эту проблему можно было бы установить маркер CSRF в дополнительный, настраиваемый заголовок ответа. Эти заголовки обычно также не могут быть доступны из другого домена. Однако они могут быть выставлены настройкой CORS на сервере Access-Control-Expose-Headers, Это безопасно, если использовать строго ограниченный Access-Control-Allow-Origin заголовок.

Другой обходной путь - использование обратного прокси-сервера, который вообще обходит проблемы с CORS и междоменными запросами. Использование такого обратного прокси-сервера предоставляет специальный путь на входной стороне, который будет перенаправлен на серверную часть (на стороне сервера). Например, звонки на https://front-end/api прокси к https://back-end/api, Поскольку все запросы от внешнего интерфейса передаются внешнему прокси-серверу в том же домене, браузер обрабатывает каждый вызов как запрос того же домена, и файлы cookie напрямую устанавливаются в исходном источнике. Недостатками этого решения являются потенциальные проблемы с производительностью из-за того, что другой сервер находится в промежутке (задержки), и файлы cookie должны быть установлены на двух источниках (вход в систему дважды при непосредственном доступе к серверу). Настроить обратный прокси-сервер можно с помощью nginx, apache или очень просто, используя http-proxy-middleware в Node.js:

var express = require('express');
var proxy = require('http-proxy-middleware');

var options = {
 target: 'https://[server]',
 changeOrigin: true,
 secure: true
};

var exampleProxy = proxy(options);
var app = express();

app.use('/api', exampleProxy);
app.use(express.static(__dirname + "/public"));
app.listen(process.env.PORT || 8080);

1

Вам может понадобиться добавить заголовок Access-Control-Allow-Headers, чтобы разрешить передачу определенных заголовков.

Пожалуйста, попробуйте добавить следующее в заголовки ответа вашего сервера (метод OPTIONS) для целей тестирования.

Access-Control-Allow-Headers: Content-Type, *

В производстве я рекомендую ограничить заголовки следующим образом (но я не уверен на 100% в правильном списке заголовков, нужно поэкспериментировать здесь, если это работает)

Access-Control-Allow-Headers: Cookie, Set-Cookie

Смотрите это для ссылки https://quickleft.com/blog/cookies-with-my-cors/

2

Другая проблема, с которой вы можете столкнуться, заключается в том, что файлы cookie будут установлены в том домене, где расположена ваша серверная служба (а не в домене, с которого вы запрашиваете).

Пожалуйста, проверьте это также

3

Как вариант последней проблемы - браузер может запретить установку cookie для домена b.xxx.com из запроса, который приходит от a.xxx.com

В этом случае вы можете попытаться установить cookie на родительский домен xxx.comтак что он будет доступен для вашей клиентской стороны

Как вы можете прочитать здесь, спецификация XHR явно запрещает чтение Set-Cookie. Лучший способ сделать это - передать информацию в заголовке вместо куки.

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