クロスオリジンリクエストのCookieを設定する


102

クッキーをクロスオリジンで共有する方法は?より具体的には、Set-Cookieヘッダーをヘッダーと組み合わせて使用する方法はAccess-Control-Allow-Origin

これが私の状況の説明です:

localhost:4000でホストされているWebアプリで実行されているAPIのCookieを設定しようとしていますlocalhost:3000

ブラウザで正しい応答ヘッダーを受信して​​いるようですが、残念ながら効果はありません。応答ヘッダーは次のとおりです。

HTTP / 1.1 200 OK
Access-Control-Allow-Origin:http:// localhost:3000
変更:Origin、Accept-エンコーディング
セットCookie:token = 0d522ba17e130d6d19eb9c25b7ac58387b798639f81ffe75bd449afbc3cc715d6b038e426adeac3316f0511dc7fae3f7; 最大年齢= 86400; ドメイン= localhost:4000; パス= /; Expires = 2017年9月19日火曜日21:11:36GMT; HttpOnly
コンテンツタイプ:application / json; charset = utf-8
コンテンツの長さ:180
ETag:W / "b4-VNrmF4xNeHGeLrGehNZTQNwAaUQ"
日付:2017年9月18日月曜日21:11:36 GMT
接続:キープアライブ

さらに、Response CookiesChromeの開発者ツールの[ネットワーク]タブを使用してトラフィックを検査すると、下にCookieが表示されます。それでも、の下の[アプリケーション]タブにCookieが設定されているのがわかりませんStorage/Cookies。CORSエラーは表示されないので、他に何かが足りないと思います。

助言がありますか?

更新I:

React-Reduxアプリのリクエストモジュールを使用/signinして、サーバー上のエンドポイントにリクエストを発行しています。サーバーにはエクスプレスを使用しています。

Expressサーバー:

res.cookie( 'token'、 'xxx-xxx-xxx'、{maxAge:86400000、httpOnly:true、domain: 'localhost:3000'})

ブラウザでのリクエスト:

request.post({uri: '/ signin'、json:{userName: 'userOne'、password: '123456'}}、(err、response、body)=> {
    //何かをする
})

アップデートII:

リクエストヘッダーとレスポンスヘッダーを今ではクレイジーなように設定し、リクエストとレスポンスの両方に存在することを確認しています。以下はスクリーンショットです。ヘッダに注意してくださいAccess-Control-Allow-CredentialsAccess-Control-Allow-HeadersAccess-Control-Allow-MethodsAccess-Control-Allow-OriginAxiosのgithubで見つけた問題を見ると、必要なヘッダーがすべて設定されているという印象を受けます。それでも、まだ運がありません...

ここに画像の説明を入力してください


4
@PimHeijdenはこれを見てください:developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/…多分withCredentialsの使用はあなたが必要とするものですか?
カラマリコ2017

2
リクエストを使用していますが、これは最善の選択ではないと思います。この投稿と回答をご覧ください。axiosはあなたに役立つと思います。stackoverflow.com/questions/39794895/...
Kalamarico

ありがとう!requestモジュールがブラウザでの使用を目的としていないことに気づきませんでした。Axiosはこれまでのところ素晴らしい仕事をしているようです。ヘッダー:Access-Control-Allow-Credentials:trueAccess-Control-Allow-Origin:http://localhost:3000(CORSを有効にするために使用)の両方を受け取ります。これは正しいようですが、Set-Cookieヘッダーは何もしません...
Pim Heijden 2017

同じ問題ですが、Axiosを直接使用しています:stackoverflow.com/q/43002444/488666。一方で{ withCredentials: true }、実際Axios側で必要とされ、サーバヘッダが同様に慎重にチェックする必要があります(参照stackoverflow.com/a/48231372/488666
フロスティZ

どのサーバーヘッダー?
PimHeijden18年

回答:


169

するべきこと

CORSリクエストによるCookieの送受信を正常に許可するには、次の手順を実行します。

バックエンド(サーバー): HTTPヘッダーAccess-Control-Allow-Credentials値をに設定しますtrue。また、HTTPヘッダーAccess-Control-Allow-OriginAccess-Control-Allow-Headersが設定されており、ワイルドカード*が設定されていないことを確認してください。

Express jsでのCORSの設定の詳細については、こちらのドキュメントをご覧ください

フロントエンド(クライアント):XMLHttpRequest.withCredentialsフラグをtrueに設定します。これは、使用する要求/応答ライブラリに応じてさまざまな方法で実現できます。

または

CORSをCookieと組み合わせて使用​​する必要はありません。これはプロキシを使用して実現できます。

何らかの理由でそれを避けないでください。解決策は上記です。

ドメインにポートが含まれている場合、ChromeはCookieを設定しないことが判明しました。localhost(ポートなしで)設定しても問題ありません。このヒントをくれたErwinに感謝します!


2
私はあなただけのためのこのような問題があると思うlocalhostのチェックここでは、この:stackoverflow.com/a/1188145をしても、これはあなたのケース(助けるかもしれstackoverflow.com/questions/50966861/...を
エドウィン

5
この答えは私をとても助けました!それを見つけるのに長い時間がかかりました。しかし、答えはAccess-Control-Allow-Origin、明示的なドメインへの設定だけでなく、それ"*"も必要であると述べるべきだと思います。そして、それは完璧な答えだろう
e.dan

6
これは良い答えです。CORS、ヘッダー、バックエンド、フロントエンドのすべてのセットアップ、および実際のサブドメインでローカルにオーバーライド/ etc / hostsを使用してlocalhostを回避します。それでも、postmanは応答ヘッダーにSET-COOKIEを表示しますが、chromeデバッグは表示しません。これを応答ヘッダーに表示します。また、Cookieは実際にはChromeに設定されていません。他に確認すべきアイデアはありますか?
bjm 8819

1
@ bjm88あなたはこれを理解することになったのですか?私はまったく同じ状況にあります。localhost:3010からlocalhost:5001に接続すると、Cookieは正しく設定されますが、localhost:3010からfakeremote:5001(hostsファイルの127.0.0.1を指す)では機能しません。カスタムドメイン(localhost:3010からmydomain.comに接続)を使用して実サーバーでサーバーをホストする場合もまったく同じです。私はこの回答で推奨されていることをすべて行い、他の多くのことを試しました。
フォーム

1
真などHttpClient.post、に.getに渡されるオプション、オプション、に:角度では、必要なクライアント側の変更はwithCredentialsを追加することもある
マーヴィン

16

2020年にリリースされたChromeブラウザに関する注意。

クロームの将来のリリースでは、彼らだけがで設定されている場合は、クロスサイトリクエストでクッキーをお届けしますSameSite=NoneSecure

したがって、バックエンドサーバーがSameSite = Noneを設定していない場合、ChromeはデフォルトでSameSite = Laxを使用し、{withCredentials:true}リクエストでこのCookieを使用しません。

詳細https://www.chromium.org/updates/same-site

FirefoxとEdgeの開発者も、将来この機能をリリースしたいと考えています。

ここにある仕様:https//tools.ietf.org/html/draft-west-cookie-incrementalism-01#page-8


3
samesite = noneとセキュアフラグを提供するには、HTTPSが必要です。HTTPSがオプションではないローカルシステムでこれを実現するにはどうすればよいですか?どういうわけかバイパスできますか?
nirmalpatel20年

@nirmalpatelChome開発コンソールで「Lax」値を削除するだけです。
LennyLip

4

クライアントがクロスオリジンリクエストからCookieを読み取れるようにするには、次のものが必要です。

  1. サーバーからのすべての応答には、ヘッダーに次のものが含まれている必要があります。

    Access-Control-Allow-Credentials: true

  2. クライアントはwithCredentials: trueオプション付きですべてのリクエストを送信する必要があります

Angular7とSpringBootを使用した実装では、次の方法でそれを実現しました。


サーバ側:

@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、すべてのサーバーの応答ヘッダーに追加されます。これは、クライアントがCookieを読み取るために必要なものです。


クライアント側:

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と認証トークンを取得してから、実際のP​​OST / PUT / DELETEリクエストを送信します。このトークンを(ヘッダーで)添付する必要があります。サーバーがリクエストを確認して実行するように命令します。

2番目の部分は、すべてのリクエストに対してアンチCSRFトークンを具体的に処理する部分です。必要に応じてCookieから読み取り、すべてのリクエストのヘッダーに書き込みます。

望ましい結果は次のようなものです。

応答 リクエスト


この答えは既存のものに何を追加しますか?
PimHeijden20年

2
実際の実装。私が投稿することにした理由は、同じ問題を検索し、さまざまな投稿からピースを追加してそれを実現するために多くの時間を費やしているためです。この投稿を比較すると、誰かが同じことをするのははるかに簡単なはずです。
StefanosKargas20年

注釈に設定allowCredentials = "true"を表示すること@CrossOriginは私を助けました。
ponder2 7520年

上記の回答で言及されている@lennylipは、samesiteおよびsecureフラグのエラーを示しています。安全なフラグなしでローカルホストサーバーでそれを達成する方法。
nirmalpatel20年

2

Expressの場合4.17.1は、最新の安定バージョンであるExpressライブラリをアップグレードします。次に;

CorsOptionで:セットのoriginローカルホストのURLまたはあなたのフロントエンドの生産のURLへとcredentialstrue 例えば

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

confignpmモジュールを使用してオリジンを動的に設定しました。

次に、res.cookieで:

ローカルホストの場合:あなたが設定することができ、すべてのsameSiteと安全なオプションを設定する必要はありませんhttpOnlytrue防止XSS攻撃や他の有用なへのHTTPクッキーのためのオプションあなたのユースケースに応じました。

本番環境では、設定する必要がありますsameSitenone、クロスオリジン・リクエストのためと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,
    })

0

ピムの答えはとても役に立ちます。私の場合、私は使用する必要があります

Expires / Max-Age: "Session"

dateTimeの場合、有効期限が切れていなくても、Cookieはバックエンドに送信されません。

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

同じ問題に遭遇する可能性のある将来の人々に役立つことを願っています。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.