はじめに
こんにちは!山内です。先日とあるAPIをAWSで実装していた時に、「いくつかのオリジンからのアクセスのみ許可したい」というリクエストがありました。Webアプリを構築する際にCORS設定のため似たようなことをAPI Gatewayでやっていたので、楽勝だ~って思って二つ返事で了承しました。ところが、API Gatewayが複数オリジンの許可設定を書けない仕様らしく、意外と面倒だったので忘れないように記事にしようと思います。
API Gatewayでのオリジンアクセス許可設定
本題に入る前に、そもそもAPI Gatewayでオリジンアクセス許可ってどうやるんだっけというところを再確認しておきます。次の手順で実現できます。
- 「リソース」>「アクション」>「CORSの有効化」をクリックする
以上です!とっても簡単ですね!設定時にポップアップが出てくるとは思いますが、操作は簡単でも裏で色々設定をしてくれています。関連記事がたくさんあるはずなので、興味のある方は調べてみてくださいね。
複数オリジンのホワイトリストチェック
では、本題に入ります。実は、上記の設定で全てのオリジンからのアクセスも許可する設定はできてしまっています。それは、APIの対象リソース、「OPTIONS」>「統合レスポンス」>「▶(メソッドレスポンスのステータスが200となっている行)」>「▶(ヘッダーのマッピング)」から確認できます。
「Access-Control-Allow-Origin」の値が「*」になっていると思います。これにより、どのオリジンからのアクセスも許可ができているということです。
最初は、この値をカンマなりセミコロンなりで複数書けばいけるだろうと思っていたのですが、なんとこの値、そういう書き方はできないんだそうです。こいつは困った~!って思って色々調べた結果、「*で受け付けておいて、Lambdaでホワイトリストチェックする」というのが最適解でした。その実装(Python3.8)は次の通りです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
import json import os def is_white_origin(target_origin): """ 引数のオリジンがホワイトリストに登録されているかをチェックします。 Args: target_origin (str) : チェック対象のオリジン Returns: チェック結果 (boolean) ホワイトリストに含まれていればTrue、そうでなければFalse """ origin_whitelist = os.environ["ORIGIN_WHITELIST"].split(",") for white_origin in origin_whitelist: if white_origin in target_origin: return True return False def lambda_handler(event, context): """ Lambda関数のハンドラーです。 オリジンがホワイトリストに含まれているかいないかをメッセージで返します。 Args: event (dict) : Lambdaのイベントデータ context (dict) : Lambdaのランタイム情報 Returns: メッセージを含んだレスポンス情報 """ request_origin = event["headers"]["Origin"] response_headers = { "Access-Control-Allow-Origin" : request_origin, "Access-Control-Allow-Credentials" : True, "Access-Control-Allow-Headers" : "Origin, X-Requested-With, Content-Type, Accept, Authorization" } if is_white_origin(request_origin): return { 'headers': response_headers, 'statusCode': 200, 'body': json.dumps({"message": "許可されたオリジンです。"}) } else: return { 'headers': response_headers, 'statusCode': 403, 'body': json.dumps({ "message": "許可されたオリジンではありません。", }) } |
コード解説
短いコードですが、3つポイントがあるので簡単に各部分のコード解説をします。
環境変数へのアクセス
12行目のコードでLambda環境変数からホワイトリストを取得しています。今回はカンマ繋ぎで登録しているため、カンマで分割してリストに格納しています。
1 |
origin_whitelist = os.environ["ORIGIN_WHITELIST"].split(",") |
オリジン情報の取得
29行目のコードでLambdaのイベントデータに含まれるオリジン情報を取得しています。ちなみに以前、オリジン情報を使う実装をしていたAPIに対して、JMeterでパフォーマンステストをしたところ、JMeterがオリジン情報を送信しないため、エラーとなったことがありました。ツールによってはそういうこともあるので、考慮しましょう。
1 |
request_origin = event["headers"]["Origin"] |
レスポンスヘッダーの設定
30~34行目のコードでレスポンスヘッダーを設定しています。この時、取得したオリジン情報をそのまま設定します。こうすることで、全てのオリジンからのアクセス許可をせずに、特定の複数のオリジンに対してのみ正常にレスポンスを返すことができます。この実装の中では一番のポイントです。
1 2 3 4 5 |
response_headers = { "Access-Control-Allow-Origin" : request_origin, "Access-Control-Allow-Credentials" : True, "Access-Control-Allow-Headers" : "Origin, X-Requested-With, Content-Type, Accept, Authorization" } |
動作確認
動作確認のため、S3の静的Webホスティングを使って、次の2つのオリジンを用意します。
- whitelist-check-sample-ok(Lambda環境変数のホワイトリストに含まれている)
- whitelist-check-sample-ng(Lambda環境変数のホワイトリストに含まれていない)
それぞれのS3バケットには次のindex.htmlを用意します。許可されているかされていないかのメッセージがそのまま表示されるようにしています。なお、このコードの内容は本記事の主旨とは離れているので、解説は割愛します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
<!DOCTYPE html> <html> <head> <script src="https://code.jquery.com/jquery-3.5.1.min.js" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous" ></script> </head> <body> <script> $.ajax({ dataType: "json", type: "GET", url: "https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/default/whitelist-check-sample", }) .done(function (result) { $("body").append(result.message); }) .fail(function (e) { $("body").append(e.responseJSON.message); }) .always(function () {}); </script> </body> </html> |
まずはホワイトリストに含まれている、whitelist-check-sample-okにアクセスしてみます。
期待通り、「許可されたオリジンです。」というメッセージが表示されました。
Chromeの開発者ツールの「Network」タブでリクエストとレスポンス情報も見てみましょう。
リクエストヘッダー「Origin」に含まれている「https://whitelist-check-sample-ok.s3-ap-northeast-1.amazonaws.com」がそのままレスポンスヘッダー「Access-Control-Allow-Origin」にも含まれていることが確認できました。
次に、ホワイトリストに含まれていない、whitelist-check-sample-ngにアクセスします。
こちらも期待通り、「許可されたオリジンではありません。」というメッセージが表示されました。
同様にリクエストとレスポンス情報を見ます。
リクエストヘッダー「Origin」に含まれている「https://whitelist-check-sample-ng.s3-ap-northeast-1.amazonaws.com」がそのままレスポンスヘッダー「Access-Control-Allow-Origin」にも含まれていることが確認できました。
おわりに
今回の記事は以上で終わりです。AWSはデモを作るにも簡単で良いですね、今回使った環境はコード実装含めても30分ほどで構築完了できました!ただ、複数オリジンのホワイトリストチェックって要件としては結構あり得るケースだと思うので、まだまだ痒い所に手が届かない部分もあります。しかしながら、今回の件を解決するにあたって公式に問い合わせしたときに、既に承知の内容だったので、待っていればそのうちカイゼンされるかもしれませんね。どんどんカイゼンされていくのもクラウドサービスの良さの1つだと思うので、もっと積極的に活用していきましょう!
執筆者プロフィール
- 社内の開発プロジェクトの技術支援や、Javaにおける社内標準フレームワークの開発を担当しています。Spring BootとTDDに手を出しつつ、LINE Botとかもいじったりしています。最近はマイクロサービスを勉強しつつ、クラウドアプリケーションを開発できるエンジニアの育成にも力を入れてます!