2
1

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 ~AWS100本ノック~ 17/100

Posted at

はじめに

前回は、【概要編】CORS ~AWS100本ノック~ 14/100という記事でCORSの概要について説明しました。
今回は、実際に試しながら挙動を確認していきます。

以下のようなAWSの構成で試していきます。
CORS-AWS.drawio (1).png

S3やAPI Gatewayについては適宜、IP制限などを行ってください

各AWSリソースについて簡単に紹介します。

1. バックエンドのLambda関数を作成

以下のコードを配置したLambda関数を作成します。

index.js
export const handler = async (event) => {
    console.log(event);
    const response = {
        statusCode: 200,
        headers: {
            "Test-Header": "xxx"
        },
        body: JSON.stringify('Hello from Lambda!'),
    };
    return response;
};

2. バックエンド用のAPI Gatewayを作成

HTTP APIを選択します。
ルートとしてはusersを作成し、メソッドとしてはGETPOSTを設定します。
Lambda統合を選択し、1.で作成したLambda関数に紐づけます。

3. 画面表示用のS3バケットの作成

S3バケットを作成します。
jsファイルのurl部分を作成したAPIのURLに変更した以下のhtmlファイルとjsファイルをアップロードします。
S3バケットに静的ウェブサイトホスティングを設定し、インデックスドキュメントにindex.htmlを設定します。

index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <link rel="icon" href="data:,">
    <title>ファイルアップロード</title>
</head>
<img src="https://placehold.jp/300x200.png">
<body>
    <h1>GETリクエスト</h1>
    <button onclick="getRequest()">実行</button>
    <script src="getRequest.js"></script>
    <h1>POSTリクエスト</h1>
    <button onclick="postRequest()">実行</button>
    <script src="postRequest.js"></script>
</body>
</html>
getRequest.js
async function getRequest() {
    const url = 'https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/users'; // xxxxxの部分はAPI GatewayでAPIをデプロイ時に発行されるユニークなIDです
    const response = await fetch(url, {
        method: 'GET'
    });
}
postRequest.js
async function postRequest() {
    const url = 'https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/users'; // xxxxxの部分はAPI GatewayでAPIをデプロイ時に発行されるユニークなIDです
    const response = await fetch(url, {
        method: 'POST'
    });
}

ここまでやったら、ウェブサイトホスティングで払い出されたURLにアクセスしてみましょう。
以下のような画面が表示されれば準備OKです :smiley:
スクリーンショット 2024-12-05 172535.png

試してみる

では、実際に試していきます。

SimpleRequest, PrefrightRequestの挙動確認

同一オリジンポリシーの対象にならないリクエスト

こちらで紹介した通り、一部のHTMLタグなどは同一オリジンポリシーの対象になりません。

別オリジンの画像が表示されていることからもわかるように、<img>タグは同一オリジンの対象になっていないことが確認できます:v:

SimpleRequestになるリクエスト

こちらに記載した通り、SimpleRequestになる条件があります。

GETリクエストを実行してみましょう。
usersに関して本リクエストしか飛んでいないため、SimpleRequestであることがわかります。

スクリーンショット 2024-12-05 204445.png

PrefrightRequestが飛ぶリクエスト

次にgetRequest.jsを修正してカスタムヘッダーとして、Content-Type: application/jsonを付けて実行してみましょう。

getRequest.js
async function getRequest() {
    const url = 'https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/users';
    const response = await fetch(url, {
        method: 'GET',
        headers: {
          'Content-Type': 'application/json'
        }
    });
}

スクリーンショット 2024-12-05 204236.png

SimpleRequestの条件から外れたため、usersのプリフライトリクエストが飛んでいることがわかります。

ヘッダー設定による挙動の確認

全許可

API GatewayのCORS設定を修正し、Access-Control-Allow-OriginAccess-Control-Allow-HeadersAccess-Control-Expose-Headers*にして全てを許可します。
スクリーンショット 2024-12-05 204847.png

この状態であれば、どこからでもアクセスできるし、どのヘッダーもリクエストできるし、どのレスポンスヘッダーもjsから取得できます。

getRequest.jsを少し変えて
リクエストにCustom-Headerを追加し、LambdaのレスポンスヘッダーのTest-Headerの値を取得して表示するようにします。

getRequest.js
async function getRequest() {
    const url = 'https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/users';
    const response = await fetch(url, {
        method: 'GET',
        headers: {
          'Content-Type': 'application/json',
          'Custom-Header': 'xxx'
        }
    });
    console.log(response.headers.get('Test-Header'));
}

リクエストすると、Custom-Headerがリクエストでき、レスポンスヘッダーのTest-Headerの値が取得出来ています。
スクリーンショット 2024-12-05 205705.png

スクリーンショット 2024-12-05 210126.png

許可していないOriginからアクセス

Access-Control-Allow-Originを修正し、Originがhttps://example.comからのリクエストのみ許可してみましょう。
スクリーンショット 2024-12-05 210321.png

リクエストすると、CORSエラーになっていて本リクエストは実行されていないことがわかります。

スクリーンショット 2024-12-05 210603.png

コンソールを見ると以下のような許可していないオリジンからアクセスしている旨のエラーメッセージが表示されています。

Access to fetch at 'https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/users'
from origin 'http://static-hosting-cors-client.s3-website-ap-northeast-1.amazonaws.com' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource. 
If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

スクリーンショット 2024-12-05 210808.png

許可していないAccess-Control-Request-Headers

Access-Control-Allow-Headersを修正し、Content-Typeのみ許可し、Custom-Headerは許可しないでみましょう。

スクリーンショット 2024-12-05 211158.png

リクエストすると、CORSエラーになります。
コンソールを見ると先ほどと同じメッセージですね :thinking:
どうやら何が原因かはわからないように隠蔽されているようです。(セキュリティ対策?)

Access to fetch at 'https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/users' 
from origin 'http://static-hosting-cors-client.s3-website-ap-northeast-1.amazonaws.com' has been blocked by CORS policy:
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

許可していないAccess-Control-Request-Method

Access-Control-Allow-Methodsを修正し、POSTとOPTIONSのみ許可にしましょう。

スクリーンショット 2024-12-05 211944.png

リクエストすると、これまでと同様CORSエラーになります。
※エラーメッセージは先ほどと同じなので省略

許可していないAccess-Control-Expose-Header

Access-Control-Expose-Headersを修正し、未設定にしましょう。

スクリーンショット 2024-12-05 212209.png

リクエストすると、正常にリクエストできます。
しかし、コンソールを見るとjsからレスポンスヘッダーTest-Headerの値が取得できずにnullになっていることがわかります。

スクリーンショット 2024-12-05 212409.png

スクリーンショット 2024-12-05 212435.png

Access-Control-Allow-Credentialsをfalseで資格情報をリクエスト

getRequest.jsを修正し、資格情報を送るようにしてみましょう。

getRequest.js
async function getRequest() {
    const url = 'https://xxxxx.execute-api.ap-northeast-1.amazonaws.com/users';
    const response = await fetch(url, {
        method: 'GET',
        credentials: 'include',
        headers: {
          'Content-Type': 'application/json',
          'Custom-Header': 'xxx'
        }
    });
    console.log(response.headers.get('Test-Header'));
}

スクリーンショット 2024-12-05 212650.png

リクエストをすると、Access-Control-Allow-Origin*のリソースに対しては資格情報を送ることが出来ないエラーになります。

スクリーンショット 2024-12-05 212928.png

Access-Control-Allow-Originを指定して再実行してみます。
スクリーンショット 2024-12-05 213158.png

しかし、同じエラーになります :cry:
エラーメッセージではわかりませんが資格情報を送る際は、Access-Control-Allow-Headers*を利用できません。

Access-Control-Allow-Headersを指定して再実行してみます。
スクリーンショット 2024-12-05 213437.png

今度こそリクエストでき...ませんでした :cry:
エラーメッセージを見ると、Access-Control-Allow-Credentialsが許可されていないメッセージが表示されます。
スクリーンショット 2024-12-05 213609.png

Access-Control-Allow-Credentialsをtrueにしてリクエストしてみます。
スクリーンショット 2024-12-05 213743.png

リクエスト出来ました :smile:

スクリーンショット 2024-12-05 213900.png

Access-Control-Max-Ageが0

今までずっとAccess-Control-Max-Ageの設定を0にしていましたが、この設定だと常にプリフライトリクエストが飛ぶことになります。
何回か実行してみると、毎回飛んでいることがわかります。
スクリーンショット 2024-12-05 214117.png

Access-Control-Max-Ageの期限内or期限外

毎回プリフライトリクエストを飛ばすのも過剰ではあると思うので、試しに30秒に設定してみましょう。
スクリーンショット 2024-12-05 214340.png

試してみると、最初の1回目はプリフライトリクエストが飛びますが、それ以降は30秒経過するまでプリフライトリクエストが飛びませんでした。
スクリーンショット 2024-12-05 215002.png

おまけ

AWSを用いたWebアプリケーションでCORS設定をする場合、それぞれどのサービスで設定するのでしょうか?
簡単に紹介します。

  • CloudFrontとS3で設定する
  • API Gatewayで設定する
  • バックエンドで設定する(Laravelならconfig/cors.php)

おわりに

今回はCORSについて、S3、API Gateway、Lambdaを利用して実際に画面からAPIを叩きながら説明しました。
前回の記事と併せて、CORSの全体像が分かっていただければうれしいです:smile:

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?