Файлы cookie недоступны в JavaScript (и в инструментах разработки), но отправляются вместе с запросом XHR (не используется только http).

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


person ssc-hrep3    schedule 20.09.2017    source источник
comment
Похоже, это может быть ваш ответ: stackoverflow. ком/вопросы/14686769/   -  person sirrocco    schedule 23.09.2017
comment
@ ssc-hrep3 Просто чтобы убедиться, потому что вы написали оба домена. В конфигурации безопасного развертывания и серверная, и клиентская части работают из одного домена, только с другим портом? (Как будто они оба работают на локальном хосте и на разных портах локально). Если они работают с разных, внешний интерфейс не сможет получить доступ к файлам cookie других доменов.   -  person Ido.Co    schedule 27.09.2017
comment
Убедитесь, что вы не заблокировали сторонние файлы cookie: stackoverflow.com/a/16634941/2346893   -  person Gökhan Kurt    schedule 27.09.2017
comment
@Ido.Co Спасибо за ваш комментарий. На самом деле я думал об использовании CORS для доступа к API из совершенно другого домена и доступа к токену CSRF с помощью JavaScript (который хранится в файле cookie): например. https://example1.com обращается к https://example2.com. Но благодаря вашему комментарию я понял, что невозможно получить доступ к файлам cookie API (example2.com) на внешнем интерфейсе (example1.com). В моем случае это означает, что я должен передать токен CSRF в заголовке ответа сервера вместо файла cookie или просто использовать обратный прокси-сервер.   -  person ssc-hrep3    schedule 28.09.2017
comment
@GökhanKurt Спасибо, но это было не так.   -  person ssc-hrep3    schedule 28.09.2017
comment
@ ssc-hrep3 Круто, я думал, что это может быть так, легко запутаться в этих вещах :) Я не думал, что это будет хорошим ТАК-ответом, поэтому я написал его как комментарий, но это стыдно видеть, что хорошая награда пропадает зря;)   -  person Ido.Co    schedule 28.09.2017
comment
@Ido.Co Вы можете оставить короткий ответ с содержанием вашего комментария и сослаться на мой ответ, и я дам вам награду. Потому что именно ваш комментарий содержал правильный ответ :)   -  person ssc-hrep3    schedule 29.09.2017


Ответы (4)


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

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

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

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

person Ido.Co    schedule 30.09.2017
comment
document.cookieможет получить доступ только к файлам cookie текущего домена, это не совсем так... он также может получить доступ к файлам cookie родительского домена - person Andrii Muzalevskyi; 30.09.2017
comment
@AndreyMuzalevsky Да, я стремился к очень короткому упрощенному объяснению. Но добавлю этот момент. - person Ido.Co; 02.10.2017

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


Проблема

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

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

Решения

Это приводит к выводу, что нет возможности получить доступ к содержимому файла cookie, отличного от HttpOnly, другого домена. Обходным путем для этой проблемы может быть установка токена 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);

person ssc-hrep3    schedule 28.09.2017

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

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

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

person Andrii Muzalevskyi    schedule 27.09.2017
comment
Спасибо! Проблема заключалась главным образом в том, что я использовал два разных домена и не мог получить доступ к файлам cookie внутреннего домена на внешнем интерфейсе (поэтому ваш второй пункт). - person ssc-hrep3; 28.09.2017
comment
@ssc-hrep3 Вы можете [установить файл cookie для родительского домена]( serverfault.com/questions/153409/), если ваша служба и потребитель используют одни и те же родительский домен — это решение подойдет вам - person Andrii Muzalevskyi; 29.09.2017
comment
@AndreyMuzalevsky Спасибо! К сожалению, это не случай. Я думал, что смогу обойти эту проблему с помощью CORS, но, как оказалось, доступ к файлам cookie между доменами невозможен. Но спасибо за ваши усилия! Я очень ценю это. - person ssc-hrep3; 29.09.2017

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

person Jean Robert    schedule 26.09.2017
comment
Я не хочу читать Set-Cookie, но получаю доступ к cookie обычным способом с помощью document.cookie в JavaScript. Это работает с CORS на localbost, но не в приведенных выше настройках. Сами файлы cookie недоступны, но отправляются вместе с предстоящим запросом. Это ожидаемое поведение для файлов cookie HttpOnly, но не для обычных. - person ssc-hrep3; 26.09.2017