29
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

クロスオリジンでcookieの挙動を確認する

Posted at

ローカル環境で2オリジンの環境を用意してcookieの挙動を調べる。自分用メモ

環境構築

1つ目の環境はreact create-react-appで作成し、yarn startでサーバーを立ち上げる

$ npx create-react-app react_test --template typescript
$ yarn start

App.tsx
import React from 'react';
import './App.css';

function App() {
  const request = async () => {
    const url = 'http://127.0.0.1:8000/api/hoge';
    try {
      const response = await fetch(url, {});
      const json = await response.json();
      console.log(json);
    } catch (error: any) {
      console.warn(error);
    }
  };
  const onClick = () => {
    request();
  };
  return (
    <div className="App">
      <button onClick={onClick}>ボタン</button>
    </div>
  );
}

export default App;

スクリーンショット 2021-03-13 21.36.56.png

2つ目の環境はlaravelで作成し、php artisan serveでサーバーを立ち上げる

$ composer create-project laravel/laravel laravel_test
$ php artisan sever

api.php
# cookieにpiyoをつける
Route::get('/hoge', function (Request $request) {
    $piyo = $request->cookie('piyo');
    return response()->json(['piyo' => $piyo])->cookie('piyo', 'pppp', 100000);
});

シンプルにapi投げる

fetchのオプションなしでシンプルにapi投げる。

App.tsx
    const url = 'http://127.0.0.1:8000/api/hoge';
    try {
      const response = await fetch(url, {});
      const json = await response.json();
      console.log(json);
    } catch (error: any) {
      console.warn(error);
    }

結果

リクエスト成功しcookieも受け取っているように見える。
スクリーンショット 2021-03-13 23.36.31.png

スクリーンショット 2021-03-13 23.38.08.png

ただし、2回目apiなげてもcookieはセットされていない。
スクリーンショット 2021-03-13 23.39.34.png

credentialsをincludeする

ブラウザーに認証情報の入ったリクエストを送るようにするには、オリジン間の呼び出しであっても、 credentials: 'include' を init オブジェクトに追加して fetch() メソッドに渡します。
https://developer.mozilla.org/ja/docs/Web/API/Fetch_API/Using_Fetch#sending_a_request_with_credentials_included

cookieなどの認証情報をリクエストに付与するにはfetchのoptionのcredentialsをincludeに指定する必要がある。

App.tsx
    const url = 'http://127.0.0.1:8000/api/hoge';
    try {
      const response = await fetch(url, {
        credentials: 'include',
      });
      const json = await response.json();
      console.log(json);
    } catch (error: any) {
      console.warn(error);
    }

結果 :

Laravelのログを見るとリクエストを受け付けてレスポンスを返していることが分かった。しかしブラウザのNetworkタブを見るとcorsエラーが発生しているのが分かる。

スクリーンショット 2021-03-13 23.43.58.png

スクリーンショット 2021-03-13 23.44.43.png
スクリーンショット 2021-03-13 23.46.20.png

↓エラーログ

Access to fetch at 'http://127.0.0.1:8000/api/hoge' from origin 'http://localhost:3000' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.
↓翻訳
オリジン「http://localhost:3000」から「http://127.0.0.1:8000/api/hoge」でのフェッチへのアクセスは、CORSポリシーによってブロックされました。リクエストの資格モードが「include」の場合、レスポンスの「Access-Control-Allow-Origin」ヘッダーの値は、ワイルドカードの「*」であってはなりません。

エラーログにも書かれているが、リクエストの資格モードが「include」の場合、レスポンスの「Access-Control-Allow-Origin」ヘッダーの値は、ワイルドカードの「」にしてはいけないとのこと。
もう一度レスポンスのヘッダーを見るとAccess-Control-Allow-Originが
になっているのが分かる。
スクリーンショット 2021-03-13 23.49.44.png

Laravelのcors設定

Laravel8でアプリケーションを作成した場合、デフォルトで↓のcorsパッケージが使用される
https://github.com/fruitcake/laravel-cors
cors設定は以下の通り。デフォルトで全てのmethod, origin, headerが許可されている。

cors.php
<?php

return [

    /*
    |--------------------------------------------------------------------------
    | Cross-Origin Resource Sharing (CORS) Configuration
    |--------------------------------------------------------------------------
    |
    | Here you may configure your settings for cross-origin resource sharing
    | or "CORS". This determines what cross-origin operations may execute
    | in web browsers. You are free to adjust these settings as needed.
    |
    | To learn more: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
    |
    */

    'paths' => ['api/*', 'sanctum/csrf-cookie'],

    'allowed_methods' => ['*'],

    'allowed_origins' => ['*'],

    'allowed_origins_patterns' => [],

    'allowed_headers' => ['*'],

    'exposed_headers' => [],

    'max_age' => 0,

    'supports_credentials' => false,

];

allowed_originsを ['http://localhost:3000'] に変更する

再度reactからapiリクエストを投げる

結果はエラー。

オリジン「http://localhost:3000」から「http://127.0.0.1:8000/api/hoge」でのフェッチへのアクセスは、CORSポリシーによってブロックされています。レスポンスの 'Access-Control-Allow-Credentials' ヘッダーの値は '' で、リクエストの資格情報モードが 'include' の場合は 'true' でなければなりません。
スクリーンショット 2021-03-14 0.00.20.png

Access-Control-Allow-Credentialsがtrueじゃないとだめとのことです。
レスポンスを見るとAccess-Control-Allow-Credentialsのヘッダーはありませんでした。

スクリーンショット 2021-03-14 0.01.56.png

レスポンスヘッダーにAccess-Control-Allow-Credentialsを返すようにする

cors.phpのsupports_credentialsをtrueに変更する

cors.php
'supports_credentials' => true,

再度reactからapiリクエストを投げる

リクエストは成功しSet-Cookieに値があるが、何度リクエストを投げてもリクエストヘッダーにCookieは付与されなかった。
スクリーンショット 2021-03-14 0.04.17.png

chrome開発ツールのcookieタブを見ると、cookieがブロックされていることが分かる。

このSet-Cookieは、"SameSite=Lax "属性を持っていましたが、トップレベルのナビゲーションに対するレスポンスではないクロスサイトレスポンスから来ていたため、ブロックされました。
スクリーンショット 2021-03-14 0.06.04.png

SameSite Cookieとは

Set-CookieにSameSite属性をつけることでCookieを制限することが可能。
↓ドキュメント
https://developer.mozilla.org/ja/docs/Web/HTTP/Headers/Set-Cookie/SameSite

3つの値から指定できる。

Lax

デフォルトでcookieはこの値になる。トップレベルナビゲーションで送信可能。
トップレベルナビゲーションとは、タグによる遷移時やformによるget actionなどを指すらしい。
↓ここに書いてある図が分かりやすい。
https://blog.jxck.io/entries/2018-10-26/same-site-cookie.html

Strict

ファーストパーティのコンテキストのみでCookieを送信する。
ファーストパーティクッキーとはブラウザのアドレスに表示されているドメインと同じドメインから発行されたcookieのこと。

None

全てのコンテキストでcookieを送信する。
以前はNoneがデフォルトだったが、最近のブラウザはLaxがデフォルト。これによりcorsの基本的な対策ができるようになった。
cookieをNoneにする場合はcookieにsecure属性が必須となる。

secure属性

secure属性をつけることでhttps通信時のみcookieを送信するようになる

クロスオリジンでapi通信するには?

クロスオリジンでapi投げる場合はSameSite属性が重要になる。
StrictやLaxだとAPIを投げてもcookieがリクエストに付与されないしSet-Cookieされた値もブラウザでブロックされる。
残るはNoneだが、cookieにsecure属性が必要になる。secure属性をつけるとhttps通信の時しかcookieのやりとりができなくなる。

ローカル環境もhttpsで通信する

ローカル開発環境 + クロスオリジンでapi通信する場合は、SameSiteをNoneにしsecure属性付けて通信をhttps化する方法がある。
docker開発環境をつかっているならhttps-portalイメージを使うことで簡単にローカル環境もhttps化できる。

docker-compose.yaml
version: '3'
services:
  https-portal:
    image: steveltn/https-portal:1
    ports:
      - '4443:443'
    environment:
      DOMAINS: 'localhost -> http://laravel:8000'
      STAGE: local

  laravel:
    build: ./
    volumes:
    - ./:/app
FROM php:7.4.16-zts-buster

WORKDIR /app

CMD php artisan serve --host 0.0.0.0 --port=8000

この状況でReactから https://localhost:4443/api/hoge にapiアクセスすると正常にcookieのやりとりができる。
スクリーンショット 2021-03-14 2.20.05.png

proxy使う

reactのcreate-react-appを使っているならproxyを使ってcors問題を回避することができる。

package.json
"proxy": "http://127.0.0.1:8000"

29
16
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
16

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?