Встановіть файли cookie для запитів із різними джерелами


97

Як поділитися файлами cookie перехресного походження? Більш конкретно, як використовувати Set-Cookieзаголовок у поєднанні з заголовком Access-Control-Allow-Origin?

Ось пояснення моєї ситуації:

Я намагаюся встановити файл cookie для API, який працює localhost:4000у веб-програмі, розміщеній на localhost:3000.

Здається, я отримую правильні заголовки відповідей у ​​браузері, але, на жаль, вони не мають ефекту. Ось заголовки відповідей:

HTTP / 1.1 200 OK
Access-Control-Allow-Origin: http: // localhost: 3000
Варіант: Походження, Прийняти-Кодування
Set-Cookie: маркер = 0d522ba17e130d6d19eb9c25b7ac58387b798639f81ffe75bd449afbc3cc715d6b038e426adeac3316f0511dc7fae3f7; Макс-вік = 86400; Домен = localhost: 4000; Шлях = /; Закінчується = Вівторок, 19 вересня 2017 21:11:36 GMT; HttpOnly
Тип вмісту: application / json; charset = utf-8
Довжина вмісту: 180
ETag: W / "b4-VNrmF4xNeHGeLrGehNZTQNwAaUQ"
Дата: пн, 18 вересня 2017, 21:11:36 GMT
Зв'язок: підтримувати життя

Крім того, я бачу файл cookie під Response Cookiesчас перевірки трафіку за допомогою вкладки Мережа інструментів розробника Chrome. Тим не менше, я не бачу, як було встановлено файл cookie на вкладці Програма під Storage/Cookies. Я не бачу помилок CORS, тому я вважаю, що я пропустив щось інше.

Будь-які пропозиції?

Оновлення I:

Я використовую модуль запитів у програмі React-Redux для видачі запиту /signinкінцевій точці на сервері. Для сервера я використовую express.

Експрес-сервер:

res.cookie ('маркер', 'xxx-xxx-xxx', {maxAge: 86400000, httpOnly: true, домен: 'localhost: 3000'})

Запит у браузері:

request.post ({uri: '/ signin', json: {userName: 'userOne', password: '123456'}}, (помилка, відповідь, тіло) => {
    // робити речі
})

Оновлення II:

Я встановлюю заголовки запитів та відповідей зараз як божевільні, переконуючись, що вони присутні як у запиті, так і у відповіді. Нижче скріншот. Зверніть увагу на заголовки Access-Control-Allow-Credentials, Access-Control-Allow-Headers, Access-Control-Allow-Methodsі Access-Control-Allow-Origin. Дивлячись на проблему, яку я знайшов у github Axios , я відчуваю, що всі необхідні заголовки тепер встановлені. Тим не менше, все ще не везе ...

введіть тут опис зображення


4
@PimHeijden погляньте на це: developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/ ... можливо, використання withCredentials саме те, що вам потрібно?
Каламаріко

2
Гаразд, ви використовуєте запит, і я думаю, що це не найкращий вибір, перегляньте цю публікацію, і відповідь, axios, я думаю, може бути для вас корисною. stackoverflow.com/questions/39794895/…
Каламаріко

Дякую! Я не помітив, що requestмодуль не призначений для використання в браузері. Здається, Аксіос поки що робить чудову роботу. Тепер я отримую і заголовок: Access-Control-Allow-Credentials:trueі Access-Control-Allow-Origin:http://localhost:3000(використовується для ввімкнення CORS). Це здається правильно, але Set-Cookieзаголовок нічого не робить ...
Пім Хейден

Та сама проблема, але безпосередньо з використанням Axios: stackoverflow.com/q/43002444/488666 . Хоча { withCredentials: true }це дійсно потрібно стороною Axios, заголовки серверів також слід ретельно перевіряти (див. Stackoverflow.com/a/48231372/488666 )
Frosty Z

які заголовки сервера?
Пім Хейден

Відповіді:


155

Що вам потрібно зробити

Щоб дозволити отримання та надсилання файлів cookie за запитом CORS, виконайте наступне.

Back-end (сервер): Встановіть для заголовка Access-Control-Allow-Credentialsзначення HTTP значення true. Крім того, переконайтеся, що заголовки HTTP Access-Control-Allow-Originі Access-Control-Allow-Headersвстановлені, а не з підстановкою* .

Для отримання додаткової інформації про встановлення CORS у express js прочитайте документи тут

Фронт-енд (клієнт): встановіть XMLHttpRequest.withCredentialsпрапорець true, цього можна досягти різними способами, залежно від використовуваної бібліотеки запиту-відповіді:

Або

Не використовуйте CORS у поєднанні з печивом. Ви можете досягти цього за допомогою проксі-сервера.

Якщо ви з якихось причин не уникайте цього. Рішення вище.

Виявилося, що Chrome не встановить файл cookie, якщо домен містить порт. Встановити його для localhost(без порту) не є проблемою. Велика подяка Ервіну за цю пораду!


2
Я думаю, у вас є ця проблема лише через localhostперевірку цього тут: stackoverflow.com/a/1188145, а також це може допомогти вашій справі ( stackoverflow.com/questions/50966861/… )
Едвін,

5
Ця відповідь мені так допомогла! Довго його знайшли. Але я думаю, що у відповіді слід зазначити, що встановлення Access-Control-Allow-Originявного домену, а не лише "*", також є обов'язковим. Тоді це буде ідеальна відповідь
Д.

6
це хороша відповідь, і всі налаштування для CORS, заголовків, серверної частини та інтерфейсу, а також уникнення localhost з перевизначенням / etc / hosts локально з реальним субдоменом, все ще я бачу, листоноша показує SET-COOKIE у заголовках відповідей, але налагодження chrome не показати це у заголовках відповідей, а також cookie насправді не встановлено в chrome. Будь-які інші ідеї для перевірки?
bjm88,

1
@ bjm88 Ти в підсумку це зрозумів? Я в точно такій же ситуації. Файл cookie встановлений належним чином при підключенні з localhost: 3010 до localhost: 5001, але не працює з localhost: 3010 до fakeremote: 5001 (що вказує на 127.0.0.1 у моєму файлі хостів). Точно так само, коли я розміщу свій сервер на реальному сервері з користувацьким доменом (підключаючись від localhost: 3010 до mydomain.com). Я зробив усе, що рекомендується у цій відповіді, і спробував багато інших речей.
Форма

1
У Angular необхідною зміною на стороні клієнта є також додавання withCredentials: вірно до параметрів, переданих HttpClient.post, .get, options тощо
Марвін,

13

Примітка для браузера Chrome, випущеного в 2020 році.

У майбутньому випуску Chrome буде надано файли cookie з міжсайтовими запитами, лише якщо вони встановлені з SameSite=Noneі Secure.

Отже, якщо на вашому серверному сервері не встановлено SameSite = None, Chrome за замовчуванням використовуватиме SameSite = Lax і не використовуватиме цей файл cookie із запитами {withCredentials: true}.

Більше інформації https://www.chromium.org/updates/same-site .

Розробники Firefox та Edge також хочуть випустити цю функцію в майбутньому.

Специфікацію можна знайти тут: https://tools.ietf.org/html/draft-west-cookie-incrementalism-01#page-8


2
надання samesite = none та безпечний прапор вимагає HTTPS. Як досягти цього в локальній системі, де HTTPS не є можливим? ми можемо якось обійти?
nirmal patel

@nirmalpatel Просто видаліть значення "Lax" у консолі розробника Chome.
LennyLip

3

Для того, щоб клієнт міг читати файли cookie із запитів про перехресне походження, потрібно мати:

  1. Усі відповіді сервера повинні містити в заголовку таке:

    Access-Control-Allow-Credentials: true

  2. Клієнту потрібно надіслати всі запити з withCredentials: trueопцією

У своїй реалізації з Angular 7 та Spring Boot я домігся цього за допомогою наступного:


На стороні сервера:

@CrossOrigin(origins = "http://my-cross-origin-url.com", allowCredentials = "true")
@Controller
@RequestMapping(path = "/something")
public class SomethingController {
  ...
}

origins = "http://my-cross-origin-url.com"Частина буде додати Access-Control-Allow-Origin: http://my-cross-origin-url.comв заголовок відповіді кожного сервера

allowCredentials = "true"Частина буде додати Access-Control-Allow-Credentials: trueв заголовок відповіді кожного сервера, який є тим, що нам потрібно для того , щоб клієнт для читання куки


З боку клієнта:

import { HttpInterceptor, HttpXsrfTokenExtractor, HttpRequest, HttpHandler, HttpEvent } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { Observable } from 'rxjs';

@Injectable()
export class CustomHttpInterceptor implements HttpInterceptor {

    constructor(private tokenExtractor: HttpXsrfTokenExtractor) {
    }

    intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        // send request with credential options in order to be able to read cross-origin cookies
        req = req.clone({ withCredentials: true });

        // return XSRF-TOKEN in each request's header (anti-CSRF security)
        const headerName = 'X-XSRF-TOKEN';
        let token = this.tokenExtractor.getToken() as string;
        if (token !== null && !req.headers.has(headerName)) {
            req = req.clone({ headers: req.headers.set(headerName, token) });
        }
        return next.handle(req);
    }
}

За допомогою цього класу ви фактично вводите додаткові речі до всього вашого запиту.

Перша частина req = req.clone({ withCredentials: true });- це те, що вам потрібно для того, щоб надіслати кожен запит з withCredentials: trueопцією. Це практично означає, що спочатку буде надіслано запит OPTION, так що ви отримаєте серед них свої файли cookie та маркер авторизації, перш ніж надсилати фактичні запити POST / PUT / DELETE, яким потрібен цей маркер, прикріплений до них (у заголовку), у замовлення серверу для перевірки та виконання запиту.

Друга частина - це та, яка спеціально обробляє маркер анти-CSRF для всіх запитів. Зчитує його з файлу cookie, коли це потрібно, і записує в шапці кожного запиту.

Бажаний результат приблизно такий:

відповідь запит


що ця відповідь додає до існуючої?
Пім Хейден

1
Фактична реалізація. Причиною того, що я вирішив його опублікувати, є те, що я витрачаю багато часу на пошуки одного і того ж випуску та додавання фрагментів з різних дописів для його реалізації. Комусь може бути набагато простіше зробити те саме, маючи цей пост для порівняння.
Стефанос Каргас

Показ налаштування allowCredentials = "true"в @CrossOriginанотації мені допоміг.
ponder275

@lennylip, згаданий у своїй відповіді вище, показує помилку для самого сайту та безпечного прапора. Як цього досягти за допомогою сервера localhost без захищеного прапора.
nirmal patel

0

Відповідь Піма дуже корисна. У моєму випадку я повинен використовувати

Expires / Max-Age: "Session"

Якщо це dateTime, навіть якщо термін його дії не минув, він все одно не надсилатиме cookie до серверної бази:

Expires / Max-Age: "Thu, 21 May 2020 09:00:34 GMT"

Сподіваюся, це буде корисно для майбутніх людей, які можуть зустрітися з тим самим питанням.


0

Для Express оновіть свою бібліотеку Express до 4.17.1останньої стабільної версії. Тоді;

У CorsOption: Встановити originна свій локальний URL або виробничої інтерфейс URL і credentialsдо true прикладу

  const corsOptions = {
    origin: config.get("origin"),
    credentials: true,
  };

Я динамічно встановлюю своє походження за допомогою модуля config npm .

Потім у res.cookie:

Для локального хоста: вам не потрібно встановлювати sameSite і безпечний варіант на всіх, ви можете встановити , httpOnlyщоб trueдля HTTP куки , щоб запобігти XSS атаку і інші корисні опції в залежності від вашого використання.

Для виробничого середовища потрібно встановити sameSiteзначення noneдля запиту про перехресне походження та secureдля true. Пам’ятайте, sameSiteпрацює лише з експрес-версією, як зараз, а остання версія chrome лише встановлює файли cookie https, тому необхідна безпечна опція.

Ось як я зробив своє динамічне

 res
    .cookie("access_token", token, {
      httpOnly: true,
      sameSite: app.get("env") === "development" ? true : "none",
      secure: app.get("env") === "development" ? false : true,
    })
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.