AWS AppSyncとAmplifyによるGraqhQL APIのセキュリティ

導入・スキーマでの認証認可定義
AWS AppSyncは、AWS上で手軽にサーバーレスのGraphQLバックエンドを設置し、その運用を支援する全面的な管理サービスです。AppSyncは、AWSがクラウドでの大規模システム運用の経験から得たセキュリティのベストプラクティスを活用し、GraphQL APIエンドポイント全体に対して組み込みのDDoS防御を提供します。
Amplifyは、開発速度を上げながらも、クラウドで安全かつスケーラブルなアプリケーションを構築するためのプラットフォームとフレームワークです。AmplifyのGraphQL Transform @authディレクティブを使って、Amplifyプロジェクトの一部としてAppSync APIでこれらのシナリオをサポートするGraphQLスキーマ定義と認証ルールを簡単に作成する方法を示します。
この記事では、AppSyncのセキュリティ機能についていくつか取り上げ、それらを皆さんのアプリケーションでいつ、どのように活用できるかをご紹介します。
AWS AppSyncを使うと、アプリケーションがインターネットを介してGraphQL APIと対話することができます。APIエンドポイントは公開されていますが、未認証のアクセスは一切許可されません。アクセスするためには、リクエストヘッダーのトークンや、リクエスト自体にAWSの認証情報で署名するといった認証方法が必ず必要です。
AppSyncは、以下の4つの異なる認証モードを提供しています:
- APIキー(API_KEY)
- Amazon Cognitoユーザープール(AMAZON_COGNITO_USER_POOLS)
- OpenID Connect(OPENID_CONNECT)
- AWS Identity and Access Management(AWS_IAM)
AppSync API には、グローバルに定義されたデフォルトの認証モードが必要です。同じ API 内に追加の認可モードを追加するだけでなく、これらのモードを混在させて、特定の認可プロバイダーを GraphQL スキーマのタイプ、フィールド、または操作にリンクすることも可能です。これを行うには、AppSync 固有のディレクティブを使用して、スキーマで直接データ定義レベルで認可とセキュリティを構成することができます。
例えば、デフォルトの認証モードとしてAPIキーを使用し、追加の認証モードとしてCognitoユーザープールを使用するAppSync APIがあるとしましょう。その場合、APIのGraphQLスキーマ内の次の型定義は、両方の認証モードに対してPost型へのアクセスを許可します。
type Post @aws_api_key @aws_cognito_user_pools {
id: ID!
description: String
}
こちらの別の例では、Cognitoユーザープールの認証モードのみにアクセスを許可します。
認可ディレクティブが型定義に追加されると、その型定義のすべてのフィールドがデフォルトでそのモードで利用できるようになります。
type Post @aws_cognito_user_pools {
id: ID!
description: String
}
制限したいフィールドにディレクティブを追加することで、特定のフィールドへのアクセスをさらに制限することができます。
以下の定義では、どちらの認証モードもtypeとidフィールどにアクセスできますが、descriptionフィールドにアクセスできるのはCognito User Pools権限モードのみです。(typeとidフィールドは並列に扱って良い?)
type Post @aws_api_key @aws_cognito_user_pools {
id: ID!
description: String @aws_cognito_user_pools
}
フィールドレベルの認可を有効にするためには、型とフィールドへのアクセスを許可しなければならないことに注意してください。(文脈が繋がらない)
Secondary authorization modesはスキーマ全体でデフォルトで拒否されており、関連する接続や操作に対してカスケード方式で明示的に構成する必要があります。( Secondary authorization modes?cascading manner?)
Amplify プロジェクトで AppSync API を定義およびデプロイするために GraphQL Transform を使用する場合、@auth ディレクティブを利用して、サポートされている各認証モードについて GraphQL スキーマに認証ルールを記述することができます。Amplify で userPools および oidc プロバイダーを使用する場合、追加のルール設定を指定して、きめ細かいアクセス制御ビジネスロジックを自動生成することができます。AppSyncの認可ディレクティブとAmplifyの@authディレクティブのルールがどのように関連しているかを紹介します
AppSync directives | Amplify @auth equivalent rule |
---|---|
@aws_api_key | { allow: public, provider: apiKey } |
@aws_iam | { allow: public/private, provider: iam} |
@aws_oidc | { allow: private/owner, provider: oidc} |
@aws_cognito_user_pools @aws_auth | { allow: private/owner/groups, provider: userPools } |
左側のAppSyncディレクティブは、AppSyncを直接操作する場合、例えばAWS ConsoleでGraphQL APIスキーマを編集する場合や、CloudFormationを使ってテンプレートでスキーマを定義する場合に使用するものとします。右側のルールは、Amplifyプロジェクトで@authディレクティブを使用してGraphQL API認可を定義する場合に使用する必要があります(例:Amplify CLIでamplify add apiを使用)。
次に、推奨されるユースケースと構成例を用いて、さまざまな認可モードについて詳しく見ていきます。その後、GraphQL APIからVPCリソースに安全にアクセスする方法について説明し、AppSyncのセキュリティとコンプライアンス基準について簡単に説明します。

API Key
このメソッドでは、HTTPヘッダーのx-api-keyでリクエストを承認するために使用する静的なAPIキーを定義できます。有効期限付きのキーを作成し、キーが存在し、有効期限が切れていない間は、AppSyncはAPIキーを使用するリクエストを受け付けます。APIキーは最大365日間設定可能で、既存の有効期限をその日からさらに最大365日間延長することができます。有効期限切れ後のキーは延長できません。
いつ使うべきか
APIキーは、お客様のアプリケーションでハードコードされた値です。APIキーは、開発目的や、特定の認証要件なしにAPIへのパブリックアクセスを提供することが安全であるユースケース(例:ゲストユーザー)に推奨されます。APIキーの使用は、API開発を始めたばかりで、素早く反復したい場合、より複雑な認証方法を心配したくない場合に推奨されます。アプリケーションの全部または一部が常にゲストアクセスをサポートするようなユースケースでない限り、長寿命で広く配布されることが予想されるアプリケーションは、APIキーを使用するべきではありません。
構成例
AppSyncでAPIキーを追加の認証モードとして使用している場合、スキーマで@aws_api_keyディレクティブを使用して、フィールドがAPI_KEY によって認可されることを指定できます。なお、以下の構成では、APIキーで認可されたリクエストに対してのみアクセスを許可しています。
type Query {
getPosts: [Post] @aws_api_key
}
@modelをバックエンドとするタイプにAmplifyの@auth変換を使用することで、同じ結果を得ることができます。
type Post
@model
@auth(rules: [{ allow: public, operations: [read] }]){
id: ID!
description: String
}
これにより、API_KEYアクセスメソッドを使用する際に、公開投稿の閲覧が可能となります。公開アクセスを許可する場合、AmplifyはデフォルトでAPI_KEYアクセスメソッドを使用します。このユースケースでは、プロバイダを指定することも可能ですが、必須ではありません。

Amazon Cognito User Pools
ここではAmazon Cognitoユーザープールを利用しています。Cognitoユーザープールでサインインし、JSONウェブトークン(JWT)を受け取った後、あなたのアプリケーションはそのトークンを使用してAppSync APIへのリクエストを認証します。ユーザーの情報、所属グループ、特定の属性やクレームは、AppSyncレイヤーで細かなアクセス制御を行うために利用できます。たとえば、ユーザーのアイデンティティやグループメンバーシップに基づいて特定のタイプへのアクセス(読み取りおよび/または書き込み)を制限することができます。(分かりにくい固有名詞多い)
いつ使うべきか
Amazon Cognitoユーザープールは、フルマネージドのユーザーディレクトリを提供します。また、Cognitoユーザープールを使用すると、ユーザーは自分のソーシャルプロファイル(例:Google、Facebook、Amazon、Apple)を使ってアプリケーションにサインインすることもできます。さらに、既存のSAMLアイデンティティプロバイダやOpenID Connect(OIDC)アイデンティティプロバイダとユーザープールを連携させることも可能です。
Cognitoはまた、Cognito Identity Poolsを使用してユーザープールからのJWTトークンを一時的なAWS認証情報と安全に交換する方法も提供しています。これにより、あなたのアプリケーションがJWTトークンを使用してAppSyncエンドポイントと対話するだけでなく、AWS認証情報を使用して他のAWSサービスとも対話する必要がある場合、Cognitoユーザープールは良い選択となります。
設定例
CognitoユーザープールがAppSync APIで定義された唯一の認証モードである場合、@aws_auth AppSyncディレクティブを使用して特定のCognitoグループへのアクセスを制限することができます。たとえば、以下のスキーマは、BloggersグループとReadersグループの一部であるユーザーへのアクセスを制限します。
type Query {
getPosts:[Post!]!
@aws_auth(cognito_groups: ["Bloggers", "Readers"])
}
追加の認証モードを使用する場合は、@aws_cognito_user_poolsディレクティブを使用して、そのフィールドがAMAZON_COGNITO_USER_POOLSによって認証されるべきであることを指定する必要があります。
type Query {
getPosts:[Post!]!
@aws_api_key @aws_cognito_user_pools(cognito_groups: ["Bloggers", "Readers"])
}
Amplifyプロジェクトで@modelまたは@searchableタイプを扱う際には、GraphQL Transformの@authディレクティブを使用して、ユーザーの身元やグループメンバーシップに基づいてAmazon DynamoDBテーブルやAmazon OpenSearch Service(Amazon Elasticsearch Serviceの後継)クラスタへのアクセスを許可するAppSyncの特定の認可ビジネスロジックを自動的に作成することができます。以下の例では、オーナー(Cognitoユーザープールのユーザー名で識別)は自分が所有する投稿を編集するための完全なアクセス権を持ち、"Bloggers"と"Readers"グループのメンバーは投稿を読むだけに制限されています。ユーザーが投稿を作成すると、AppSyncのリゾルバー内の認証ロジックがレコードにユーザー名を添付します。その後、ユーザー名とグループメンバーシップは次の操作でアクセスのための自動検証が行われます。
type Post
@model
@auth(
rules: [
{ allow: owner }
{ allow: groups, groups: ["Bloggers", "Readers"], operations: [read] }
]
) {
id: ID!
description: String
}
Cognitoユーザープールに組み込まれた静的なグループに加えて、各レコードとともに保存されたグループ情報に基づいて動的にグループアクセスを認可することもできます。レコードに対して操作を行うとき、ユーザーのグループメンバーシップは、DynamoDBに保存されているグループフィールドで定義されたグループと照らし合わせてチェックされます。
type Post
@model
@auth(
rules: [
{ allow: owner }
{ allow: groups, groupsField: "group", operations: [read] }
]
) {
id: ID!
description: String
group: [String] # or String for a single group
}