1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

はじめに

フロントエンドとバックエンドを別のドメインで実装する際に必ずと言っていいほど必要となるCORSについて概念から実装方法までまとめた記事になります。

CORSとは

CORS(Cross-Origin Resource Sharing)は、追加のHTTPヘッダーを使用して、あるオリジンで実行中のWebアプリケーションに、異なるオリジンのリソースへのアクセスを許可するように伝えるブラウザの仕組みです。

CORSが必要な理由

ブラウザには同一オリジンポリシー(Same-Origin Policy)というセキュリティ機能が組み込まれており、これは、あるオリジンのスクリプトが異なるオリジンのリソースにアクセスすることを制限する仕組みです。

例えば、
https://frontend.example.comで動作するWebアプリケーションが
https://api.example.comのAPIにアクセスしようとする場合

これは異なるオリジン間の通信となり、デフォルトでは制限されます。

CORSを使用したリクエストの流れ(図解)

  1. フロントエンドがAPIリクエストを発行
  2. ブラウザが自動的にOPTIONSメソッドでプリフライトリクエストを送信
  3. バックエンドがCORSヘッダーを返却
  4. ヘッダーが適切な場合のみ、本来のリクエストを実行

同一オリジンポリシーのセキュリティ

同一オリジンポリシーは、以下のような攻撃を防ぎます。

  • クロスサイトスクリプティング(XSS)
  • 悪意のあるサイトによる機密データの窃取
  • クリックジャッキング

CORSの構成要素

ヘッダー名 説明
Access-Control-Allow-Origin 許可するオリジンを指定 http://localhost:3000 または *
Access-Control-Allow-Methods 許可するHTTPメソッドを指定 GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS
Access-Control-Allow-Headers 許可するHTTPヘッダーを指定 Content-Type, Accept
Access-Control-Allow-Credentials クレデンシャル(Cookie等)の送信を許可 true
Access-Control-Expose-Headers クライアントに公開するヘッダーを指定 Set-Cookie
Access-Control-Max-Age プリフライトリクエストの結果をキャッシュする時間(秒) 100

一部のフレームワークやライブラリではAccess-Control-Allow-Origin*に設定できないことがあります。

Goで実装

package main

import (
    "log"
    "net/http"
)

func corsMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // CORSヘッダーの設定
        w.Header().Set("Access-Control-Allow-Origin", "http://localhost:3000")
        w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
        w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")

        // プリフライトリクエストの処理
        if r.Method == "OPTIONS" {
            w.WriteHeader(http.StatusOK)
            return
        }

        next.ServeHTTP(w, r)
    })
}

func main() {
    // APIハンドラーの定義
    apiHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        w.Write([]byte(`{"message": "Hello, World!"}`))
    })

    // ミドルウェアの適用
    handler := corsMiddleware(apiHandler)

    // サーバーの起動
    log.Printf("Starting server on :8080")
    if err := http.ListenAndServe(":8080", handler); err != nil {
        log.Fatal(err)
    }
}

コード説明

1. corsMiddleware関数

  • HTTPハンドラーをラップするミドルウェア関数
  • CORSに必要なヘッダーを設定
  • OPTIONSメソッドによるプリフライトリクエストを処理

2. main関数

  • シンプルなJSONレスポンスを返すAPIハンドラーを定義
  • corsMiddlewareを適用
  • 8080ポートでサーバーを起動

テストしてみる

Postmanやcorlコマンドではブラウザを中継しないため、CORSがどう動いているのかを確認できません。
今回はReactを用いて動作のテストをします。

npx create-react-app cors-test

cors-test/src/App.jsを以下のコードに書き換え

App.js
import React, { useState } from 'react';

function App() {
  const [response, setResponse] = useState(null);
  const [error, setError] = useState(null);

  const testCORS = async () => {
    try {
      const res = await fetch('http://localhost:8080', {
        method: 'GET',
        headers: {
          'Content-Type': 'application/json',
        },
      });
      const data = await res.json();
      setResponse(data);
      setError(null);
    } catch (err) {
      setError(err.message);
      setResponse(null);
    }
  };

  return (
    <div style={{ textAlign: 'center', padding: '20px' }}>
      <h1>CORSテスト</h1>
      <button onClick={testCORS}>CORS APIを呼び出す</button>
      {response && <div><h2>レスポンス:</h2><pre>{JSON.stringify(response, null, 2)}</pre></div>}
      {error && <div><h2>エラー:</h2><pre>{error}</pre></div>}
    </div>
  );
}

export default App;
npm start

起動すると以下のようなページが見えるはずです。
スクリーンショット 2024-12-20 16.07.00.png

CORSの設定が正しい時の動作確認

上で説明したgoのコードを実際に動かしてみましょう
main.goに上記のコードを貼り付けて以下のコマンドを実行します

go run main.go

正常に動作するのを確認できたら
フロントのボタン(CORS APIを呼び出す)をクリックしてみましょう
正しく動作している場合は以下のようにレスポンスが帰ってきます。
スクリーンショット 2024-12-20 17.56.45.png

CORSの設定が誤っている時の動作確認

CORSの許可しているオリジンを変更してみます。

main.go
func corsMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // 許可するオリジンをlocalhost:3001に変更
        w.Header().Set("Access-Control-Allow-Origin", "http://localhost:3001")
        
        // 省略
    
    })
}

以下のようにエラーが表示されます。
スクリーンショット 2024-12-20 22.03.23.png
ブラウザのコンソールを見るとオリジンが許可されているものが違うというエラーが見て取れますね。フロントエンドの開発をしている際、このようなエラーが出ていればCORSを疑ってみてください。
スクリーンショット 2024-12-20 22.04.21.png

参考

1
0
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
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?