2017-02-17 8 views
4

Fetch APIを使用している場合(実際にはfetch polyfillを介して)Safariにサーバの応答からSet-Cookieを正常に適用させることができません。 FFとChromeでも同じコードが正しく動作します(ネイティブとポリフィルの両方を使用してテスト済みfetch)。SafariがJS Fetch APIを使用してCORS Cookieを設定していない

  1. ドメイン間で要求があります。
  2. はい、私はcredentials: trueを設定しています。
  3. サーバーはSet-Cookieヘッダーで応答します。
  4. その後のリクエストはCookieリクエストヘッダーを使用してChromeとFFから送信されますが、Safariでは送信されません。
  5. リクエストはHTTPSを使用します(証明書は自己署名され、開発ドメイン上にありますが、Safariは通常のリクエストで受け付けているようです)。および

誰かが問題の原因を知っていますか?

私はドキュメントを読んで、closed bug reportsの多くを見てきました。私が何かを見逃していなければ、おそらく問題は'default browser behaviour'のクッキーとCORSを扱うことだと思います。フェッチするのではなく(ポリフィルのソースコードを読むと、100%クッキーを知らないようです)。いくつかのバグレポートには、不正な形式のサーバー応答がCookieの保存を妨げる可能性があることが示唆されています。

私のコードは次のようになります。

function buildFetch(url, init={}) { 
    let headers = Object.assign({}, init.headers || {}, {'Content-Type': 'application/json'}); 
    let params = Object.assign({}, init, { credentials: 'include', headers }); 

    return fetch(`${baseUrl}${url}`, params); 
} 

buildFetch('/remote/connect', {method: 'PUT', body: JSON.stringify({ code })}) 
.then(response => response.json()) 
.then(/* complete authentication */) 

実際の認証要求は以下の通りです。 Safariはコピー/貼り付けが難しいため、正確なリクエスト/レスポンスデータを取得するためにcURLを使用しています。

curl 'https://mydevserver:8443/api/v1/remote/connect' \ 
-v \ 
-XPUT \ 
-H 'Content-Type: application/json' \ 
-H 'Referer: http://localhost:3002/' \ 
-H 'Origin: http://localhost:3002' \ 
-H 'User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/602.4.8 (KHTML, like Gecko) Version/10.0.3 Safari/602.4.8' \ 
--data-binary '{"token":"value"}' 


* Trying 127.0.0.1... 
* Connected to mydevserver (127.0.0.1) port 8443 (#0) 
* TLS 1.2 connection using TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 
* Server certificate: mydevserver 
> PUT /api/v1/remote/connect HTTP/1.1 
> Host: mydevserver:8443 
> Accept: */* 
> Content-Type: application/json 
> Referer: http://localhost:3002/ 
> Origin: http://localhost:3002 
> User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_5) AppleWebKit/602.4.8 (KHTML, like Gecko) Version/10.0.3 Safari/602.4.8 
> Content-Length: 15 
> 
* upload completely sent off: 15 out of 15 bytes 
< HTTP/1.1 200 OK 
< Access-Control-Allow-Origin: http://localhost:3002 
< Access-Control-Allow-Credentials: true 
< Access-Control-Allow-Headers: Origin, X-Requested-With, Content-Type, Accept, Api-Key, Device-Key 
< Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS 
< Access-Control-Expose-Headers: Date 
< Content-Type: application/json; charset=utf-8 
< Content-Length: 37 
< Set-Cookie: express:sess=[SESSIONKEY]=; path=/; expires=Fri, 17 Feb 2017 15:30:01 GMT; secure; httponly 
< Set-Cookie: express:sess.sig=[SIGNATURE]; path=/; expires=Fri, 17 Feb 2017 15:30:01 GMT; secure; httponly 
< Date: Fri, 17 Feb 2017 14:30:01 GMT 
< Connection: keep-alive 
< 
* Connection #0 to host mydevserver left intact 
{"some":"normal","response":"payload"} 

答えて

6

自分の質問に答える。

私は彼らのモチベーションを理解していますが、これはSafariの「意図したように動作する」動作であることをかなり憤慨しています。 XHR(ネイティブに着陸するとおそらくネイティブフェッチ)はサードパーティのCookieの設定をまったくサポートしていません。このエラーは、スクリプト・コンテキスト外のブラウザによって処理されるため、完全に透過的です。したがって、クライアント・ベースのソリューションは実際には不可能になります。

ここでは、APIサーバーのHTMLページにウィンドウまたはiframeを開いてCookieを設定することをお勧めします。この時点で、サードパーティのCookieが動作し始めます。これはかなり醜いので、サファリがいつかその抜け道を閉じることはないという保証はありません。

私の解決策は、セッションクッキーが行う認証システムを基本的に再実装することです。すなわち: - 何かの間に変異することはほとんどありません

  1. [token]があなたのセッションのために必要な情報(理想的にはユーザIDのみを含む非常に小さな、短命JWTで新しいヘッダー、X-Auth: [token]を追加しますあなたのアプリケーションの存続期間 - ただし、セッション中にパーミッションを変更できる場合は、パーミッションのようなものではありません。
  2. X-Auth~Access-Control-Allow-Headers;
  3. サインイン時に、必要なペイロードでセッションCookieと認証トークンを設定します(Safariユーザーと非Safariユーザーの両方にCookieと認証ヘッダーが表示されます)。
  4. クライアントでは、X-Token応答ヘッダーを探し、いつでもそれが表示されるように、X-Token要求ヘッダーとしてエコーバックします(ローカルストレージを使用して永続性を達成できます。トークンは期限切れです。ある時点以降は償還することはできません)。
  5. サーバー上で、保護されたリソースのすべての要求に対して、Cookieが存在する場合はそのCookieを確認して使用します。
  6. そうでない場合(Safariが送信しなかったためCookieが存在しない場合)、ヘッダートークンを探し、トークンペイロードを検証して解読し、提供された情報で現在のセッションを更新してから新しい認証トークンを生成し、それを応答ヘッダーに追加します。
  7. 通常通りに進みます。

完全に異なる問題を解決するためのものであり、実際には「再生」問題のためにセッション管理に使用されるべきではないことに注意してください(ユーザーが2つのウィンドウを開いたそれ自身のヘッダー状態で)。ただし、この場合、通常必要となる一時的なセキュリティとセキュリティが提供されます。最終的には、それらをサポートするブラウザでクッキーを使用し、セッション情報を可能な限り小さく保ち、JWTを可能な限り短命に保ち、サーバーアプリケーションを構築して不慮のリプレイ攻撃を期待する必要があります。

関連する問題