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

More than 1 year has passed since last update.

<AWS SAM> 複数originからのCORSを許可する

Last updated at Posted at 2023-10-01

はじめに

業務でAWS SAM(lambdaプロキシ統合)にて作成されたAPIに対し、複数originからのアクセスを許可するという作業があったためメモとして残します。
また今回の状況は開発環境と本番環境、両方からアクセスしたい場合などにも役立つ内容かと思います。

状況再現

AWSの公式チュートリアルよりSAMの簡単なAPIを作成します。

諸々作業を終え、生成されたURLにcurlすると以下のような結果になると思います。

$ curl https://xxxxxxx.execute-api.us-west-2.amazonaws.com/Prod/hello/

{"message": "hello world"}

ではブラウザからアクセスしてみます。
今回はlocalhost(http://localhost:3000)とs3,cloudfrontを用いてホスティングしているCDNのVueの環境(cloudfrontより生成されたURL)からアクセスしてみます。

「Access to XMLHttpRequest at 'https://xxxxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/hello/' from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.」

上記のようなエラーがアクセスしたブラウザすべてで発生するかと思います。

No 'Access-Control-Allow-Origin' header is present on the requested resource.

上記のエラーはバックエンド側(今回の場合APIGatewayまたはLambda)でレスポンスヘッダーにてAccess-Control-Allow-Originを設定しアクセスできるオリジン(URL)を指定しなさいと注意されているエラーになります。

実装

Lambdaのコードとtemplate.yamlのコードを改修していきます。

template.yaml
AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  sam-app

  Sample SAM Template for sam-app

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 3
    MemorySize: 128

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function # More info about Function Resource: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#awsserverlessfunction
    Properties:
      CodeUri: hello_world/
      Handler: app.lambda_handler
      Runtime: python3.9
      Environment:  # 環境変数を定義
        Variables:
          ALLOWED_ORIGINS: 'http://localhost:3000,https://xxxxxxxx.cloudfront.net'
      Architectures:
        - x86_64
      Events:
        HelloWorld:
          Type: Api # More info about API Event Source: https://github.com/awslabs/serverless-application-model/blob/master/versions/2016-10-31.md#api
          Properties:
            Path: /hello
            Method: get

Outputs:
  # ServerlessRestApi is an implicit API created out of Events key under Serverless::Function
  # Find out more about other implicit resources you can reference within SAM
  # https://github.com/awslabs/serverless-application-model/blob/master/docs/internals/generated_resources.rst#api
  HelloWorldApi:
    Description: "API Gateway endpoint URL for Prod stage for Hello World function"
    Value: !Sub "https://${ServerlessRestApi}.execute-api.${AWS::Region}.amazonaws.com/Prod/hello/"
  HelloWorldFunction:
    Description: "Hello World Lambda Function ARN"
    Value: !GetAtt HelloWorldFunction.Arn
  HelloWorldFunctionIamRole:
    Description: "Implicit IAM Role created for Hello World function"
    Value: !GetAtt HelloWorldFunctionRole.Arn

チュートリアルのコードから修正した部分は環境変数を設定した点です。(上記コードから抜粋)
許可したいURLを下記のように定義します。

Environment:  # 環境変数を定義
        Variables:
          ALLOWED_ORIGINS: 'http://localhost:3000,https://xxxxxxxx.cloudfront.net'

続いてLambdaのコードです。

app.py
import json
import os

def get_cors_headers(origin):
    return {
        'Access-Control-Allow-Origin': origin,
        'Access-Control-Allow-Headers': 'Content-Type',
        'Access-Control-Allow-Methods': 'OPTIONS,POST,GET'
    }

def lambda_handler(event, context):

    origin = event["headers"].get("origin")
    allowed_origins = os.environ.get("ALLOWED_ORIGINS", "").split(',')

    if origin in allowed_origins:
        cors_headers = get_cors_headers(origin)

        return {
            "statusCode": 200,
            "body": json.dumps({
                "item": "hello lambda!",
            }),
            "headers": cors_headers
        }
    else:
        return {
            "statusCode": 403,
            "body": json.dumps({
                "message": "Origin not allowed.",
            }),
        }

ヘッダー情報をdef get_cors_headersにて設定します。(引数にはリクエストしてきたオリジンのURLが入ります。)

下記コードではeventからoriginのURL情報の抽出と先ほどtemplate.yamlにて設定した環境変数のURLを配列に変換します。

origin = event["headers"].get("origin")
allowed_origins = os.environ.get("ALLOWED_ORIGINS", "").split(',')

続いて条件分岐について環境変数にて設定した許可するオリジンとリクエストしてきたオリジン情報が一致しているかどうか確かめます。一致している場合はget_cors_headersを呼び出し、200と本来レスポンスする情報を返します。一致しない場合は403とmessageを返します。

if origin in allowed_origins:
        cors_headers = get_cors_headers(origin)

        return {
            "statusCode": 200,
            "body": json.dumps({
                "item": "hello World",
            }),
            "headers": cors_headers
        }
    else:
        return {
            "statusCode": 403,
            "body": json.dumps({
                "message": "Origin not allowed.",
            }),
        }

各オリジンからアクセスする

無事レスポンスが返ってきました。
スクリーンショット 2023-10-01 16.23.33.png

無事Access-Control-Allow-Originも設定できています。
スクリーンショット 2023-10-01 16.25.23.png

スクリーンショット 2023-10-01 16.26.45.png

終わりに

思ったよりもサクッと実装できました。
CORSについて触れるいい機会になったかなと思います。この記事がどなたかにお役に立てば幸いです。

参考

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