🛥️

AppSyncの認可をAPI_KEYからIAMにセキュアな舵を切る

2023/06/08に公開

はじめに

Webアプリケーションを開発する際、APIをパブリックに公開する必要があるケースが頻繁にあります。例えば、ホームページやポートフォリオサイトなど、ユーザーがログインすることなくアクセス可能なページなどです。
会社のホームページの場合、「広報記事」や「採用記事」など動的なコンテンツを取得するためにAPIにアクセスする必要が出てきます。

そこで、未認証ユーザー(例えば、会社に興味を持ってホームページを訪れたログインする必要がない人)がAPIにアクセスするための第一の選択肢としてAPI_KEYが挙げられますが、セキュリティ上のリスクがあります。例えば、このAPIキーが漏洩すると不正アクセスを許してしまう可能性があります。
また、有効期限が最大で365日間であるため、その都度更新する手間もかかります(こちらは分かりやすく面倒ですよね)。
そのため、APIキーの使用を避けることが望ましいところです。

AWS AppSyncは、これらのシチュエーションに対応するためのソリューションを提供しています。AppSyncにはIAM認証の設定をすると、APIは認証されていないユーザー(unauthenticated users)がアクセスできるようになります。

この記事では、AppSyncをIAM認証の未認証ユーザー(public)として設定し、パブリックAPIをセキュアに公開する方法について詳しく説明します。

注意点とかもろもろ

ユーザーがログインする必要はありませんが、AppSyncを公開(public)するためには認証プロバイダーが必要になります。

このとき初めて、認証されていないユーザー(unauthenticated users)がAppSyncにアクセスできるようになります。

紹介すること

この記事では、上記の設定を実現する方法を紹介します。

そもそもIAM認証の仕組みとは

AWS Identity and Access Management (IAM) は、AWSリソースへのアクセスをセキュアに制御するためのサービスです。IAMを使用すると、特定のAWSリソースへ(ここではAppSync)のアクセス許可を詳細に制御し、AWSリソースを使用するための認証 (AWS アカウントとIAMユーザーに対する) を管理することができます。

AppSyncにおけるIAM認証は、リクエストを行うユーザーまたはロールがリソースに対して持っている権限を用いて、認証と認可のプロセスを行います。ユーザーやロールはAWSのIAMポリシーを通じてこれらの権限を付与されます(ここではユーザーのみ)。

また、IAMを用いて未認証(公開)アクセスを許可する場合、Cognito Identity Poolsを利用して「未認証ID」を生成し、そのIDに紐づくIAMロールを用いて認証と認可を行います。 これにより、公開アクセスを許可しつつも、APIキーを公にするリスクを回避することができます。

その上で、APIへのアクセスをさらに細かく制御したい場合、AppSyncの@authディレクティブを利用します。このディレクティブを用いることで、GraphQLスキーマレベルでアクセス制御のルールを設定することが可能となります。

開発環境の前提

早く本題に移りたいところですが、本記事では以下の前提条件が必要となります。指定されたコマンドがすでに実行されていることを確認してください。

  • amplify initが実行されている
    • このコマンドはAmplifyのプロジェクトを初期化します。ここで、プロジェクトの名前や使用するエディタ、プログラミング言語の種類などを設定します。
  • amplify add apiが実行されている
    • このコマンドはAmplifyプロジェクトにAPIを追加します。ここで、APIの種類(RESTかGraphQL)、認証方法、スキーマの設定などを行います。
  • amplify pushが実行されている
    • このコマンドはローカルで設定したAmplifyの設定をAWSに反映します。具体的には、設定したリソース(ここではAPI)をAWSにデプロイします。
  • npm i @aws-amplifyが実行されている
    • このコマンドはAWS AmplifyのJavaScriptライブラリをプロジェクトにインストールします。このライブラリを使用することで、AWSのサービスをJavaScriptから簡単に使用することができます。

これらのコマンドが事前に実行されていることで、本記事で解説する設定を進めることが可能となります。

amplify update api を実行

amplify update apiはAmplify CLIのコマンドの一つで、既存のAPIの設定を更新するために使用されます。以下に、このコマンドの実行フローについて説明します。

$ amplify update api
? Please select from one of the below mentioned services
> GraphQL

? Choose the default authorization type for the API
> Amazon Cognito User Pool

? Do you want to configure advanced settings for the GraphQL API
> Yes, I want to make some additional changes.

? Choose the additional authorization types you want to configure for the API
[x] IAM
  1. amplify update apiを実行すると、最初にどのサービスを更新するかを聞かれます。ここでは、GraphQLを選択します。

  2. 次にデフォルトの認証タイプを選択します。この例では、Amazon Cognito User Poolが選択されています。これにより、APIへのデフォルトのアクセス制御がCognitoユーザープールに設定されます(が、先述の通り実際はユーザープールは使いません)。

  3. 次に、GraphQL APIの詳細設定を行うか尋ねられます。Yes, I want to make some additional changes.を選択すると、追加の設定を行うことができます。

  4. 最後に、追加の認証タイプを選択します。この例では、IAMがチェックされています。これにより、デフォルトの認証タイプに加えて、IAMを使った認証も許可されます。

この操作を行うことで、既存のAPIの認証設定が更新され、Cognito User PoolとIAMの両方で認証が可能になります。これが完了したら、GraphQL schema compiled successfully.と表示され、スキーマのコンパイルが成功したことを確認できます。

以上が、amplify update apiコマンドを実行する際の詳細な説明です。

上記コマンドで設定したAmazon CognitoとIAMの役割について


ログインユーザー(Cognito)と未ログインユーザー(IAM)双方からのアクセスを許可することができる図

Cognito

Amazon Cognito User Poolは、アプリケーションユーザーの認証とプロファイル情報の管理を行います。ユーザー名とパスワードを使った認証の他、ソーシャルログイン(FacebookやGoogleなど)やSAMLベースのIDプロバイダもサポートしています。Cognito User Poolを認証タイプとして選択すると、ユーザーが自己登録しログインすることで、APIに対して認証されたアクセスを行うことが可能になります(が、ユーザープールは使いません)。

IAM

IAMはAWSリソースへのアクセスを制御するためのサービスです。IAMを使用して、特定のユーザーが特定のリソースに対してどのような操作を行えるかを制御することができます。また、IAMを用いた認証ではCognito Identity Poolsを利用し、認証されたユーザーだけでなく未認証ユーザー(公開)に対してもアクセス許可を与えることが可能です。これにより、ログインしなくても特定の操作を許可したAPIへのアクセスを可能にすることができます。

つまり、この例では、Cognito User Poolをデフォルトの認証方法として使用し、加えてIAMを追加の認証方法として選択しています。これにより、ログインユーザー(Cognito)と未ログインユーザー(IAM)双方からのアクセスを許可することができます(が、ここでは未ログインユーザー(IAM)からのアクセスのみを想定)。

schema.graphql を修正する

例えば、元のschema.graphqlファイルは次のようになっています。

type Post 
  @model 
  @auth(rules: [
    { allow: public }
  ]) 
{
  id: ID!
  title: String!
  img: String!
  date: AWSDate!
  showFlag: Boolean!
}

ここで@authディレクティブは、このPost型へのアクセスをパブリック(public)に設定しています。つまり、ログインしていないユーザーもPost型のデータにアクセスできます。

しかし、この設定だけでは問題があります。それは、AppSync APIがデフォルトでAPIキーを使用してパブリックアクセスを許可するからです。APIキーが漏洩すると、不正利用のリスクがあるため、これを避ける必要があります。

そのため、@authディレクティブのルールを修正して、パブリックアクセスをIAM認証に切り替えます。また、IAM認証を使用する場合、アクセスを許可する操作も指定する必要があります。ここでは、読み取り(read)操作のみを許可します。

以下が修正後のschema.graphqlです。

type Post 
  @model 
  @auth(rules: [
-   { allow: public }
+   { allow: public, provider: iam, operations: [read] }
  ])
{
  id: ID!
  title: String!
  img: String!
  date: AWSDate!
  showFlag: Boolean!
}

こうすることで、IAM認証を使用したパブリックアクセスを許可するようになり、APIキーを使用せずに未認証ユーザーがPost型のデータを読み取ることができます

クエリの呼び出し方を修正する

修正前のクエリ呼び出しは次のようになっていました。

const chatMessage = await API.graphql(graphqlOperation(listChats))

これは、APIからlistChatsクエリを実行するためのコードです。ここでは、API.graphqlメソッドにgraphqlOperationを渡して、GraphQLのクエリを実行しています。

しかし、上記のamplify update apiで行ったように、AppSyncはデフォルトでCognito User Pool認証を使用しています。 したがって、ログインしていないユーザー(未認証ユーザー)がAPIを呼び出す場合、認証エラーが発生します。

この問題を解決するために、APIの呼び出し方を修正する必要があります。具体的には、 API.graphqlメソッドにオプションを追加して、IAMを認証プロバイダーとして指定します。

以下は、修正後のクエリ呼び出しの例です。

const chatMessage = await API.graphql({
  query: listChats,
  authMode: 'AWS_IAM'
})

ここでは、authModeプロパティを使用して、認証モードをAWS_IAMに設定しています。これにより、このAPI呼び出しはIAM認証を使用することになります。

この修正により、未認証ユーザーでもlistChatsクエリを実行できるようになります。このクエリはschema.graphqlで設定した@authディレクティブのルールに従い、未認証ユーザーは読み取り(read)操作のみを実行できるように動いているはずです。

おわりに

この記事では、AWS AppSyncで公開APIを設定し、未認証ユーザーがAPIにアクセスできるようにする方法を紹介しました。これにより、ユーザーがログインすることなくアプリケーションにアクセスできます。

参考

ありがとうございました
https://zenn.dev/foxtail88/articles/9580ec6c5c2164

https://qiita.com/shinnoki/items/8a976cb2e7958cef2e9a

Discussion