Куки не доступны в 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. Лучший способ сделать это - передать информацию в заголовке вместо куки.