Sécurité
Rubriques
Cette section décrit les options dont vous disposez pour configurer la sécurité et la protection des données pour vos applications.
Il existe quatre méthodes permettant d'autoriser vos applications à interagir avec votre API AWS AppSync GraphQL. Vous indiquez le type d'autorisation que vous utilisez en spécifiant l'une des valeurs de type d'autorisation suivantes dans votre API AWS AppSync ou dans un appel via l'interface de ligne de commande :
-
-
API_KEY
-
Pour utiliser des clés API.
-
-
-
AWS_IAM
-
Pour utiliser les autorisations AWS Identity and Access Management (IAM
).
-
-
-
OPENID_CONNECT
-
Pour utiliser votre fournisseur OpenID Connect.
-
-
-
AMAZON_COGNITO_USER_POOLS
-
Pour utiliser un groupe d'utilisateurs Amazon Cognito.
-
Ces types d'autorisation de base fonctionnent pour la plupart des développeurs. Pour
les cas d'utilisation plus avancés, vous pouvez ajouter des modes d'autorisation supplémentaires
via la console, l'interface de ligne de commande et AWS CloudFormation. Pour les modes
d'autorisation supplémentaires, AppSync fournit un type d'autorisation qui prend les
valeurs répertoriées ci-dessus (c'est-à-dire API_KEY
, AWS_IAM
, OPENID_CONNECT
, AMAZON_COGNITO_USER_POOLS
).
Lorsque vous spécifiez API_KEY
ou AWS_IAM
comme type d'autorisation principal ou par défaut, vous ne pouvez pas les spécifier
à nouveau comme l'un des modes d'autorisation supplémentaires. De même, vous ne pouvez
pas dupliquer API_KEY
ni AWS_IAM
à l'intérieur des modes d'autorisation supplémentaires. Vous pouvez utiliser plusieurs
groupes d'utilisateurs Amazon Cognito et fournisseurs OpenID Connect. Cependant, vous
ne pouvez pas utiliser des groupes d'utilisateurs Amazon Cognito ou des fournisseurs
OpenID Connect dupliqués entre le mode d'autorisation par défaut et l'un des modes
d'autorisation supplémentaires. Vous pouvez spécifier différents clients pour votre
groupe d'utilisateurs Amazon Cognito ou fournisseur OpenID Connect à l'aide de l'expression
regex de configuration correspondante.
Autorisation API_KEY
Les API non authentifiées nécessitent des limitations plus strictes que les API authentifiées. Pour contrôler les limitations des points de terminaison GraphQL non authentifiés, vous pouvez utiliser des clés API. Une clé API est une valeur codée en dur dans votre application, qui est générée par le service AWS AppSync quand vous créez un point de terminaison GraphQL non authentifié. Vous pouvez effectuer la rotation des clés API à partir de la console, de l'interface de ligne de commande ou de la Référence de l'API AWS AppSync.
Les clés API sont configurables pour une durée maximale de 365 jours et vous pouvez prolonger une date d'expiration existante de 365 jours maximum à partir de cette date. Les clés API sont recommandées pour le développement ou pour les cas d'utilisation où il est possible d'exposer une API publique en toute sécurité.
Sur le client, la clé API est spécifiée par l'en-tête x-api-key
.
Par exemple, si votre API_KEY
a pour valeur 'ABC123'
, vous pouvez envoyer une requête GraphQL via curl
comme suit :
$ curl -XPOST -H "Content-Type:application/graphql" -H "x-api-key:ABC123" -d '{ "query": "query { movies { id } }" }' https://YOURAPPSYNCENDPOINT/graphql
Autorisation AWS_IAM
Ce type d'autorisation applique le Processus de signature AWS Signature Version 4 sur l'API GraphQL. Vous pouvez associer les stratégies d'accès Identity and Access
Management (IAM
Si vous souhaitez un rôle pouvant effectuer toutes les opérations de données :
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "appsync:GraphQL" ], "Resource": [ "arn:aws:appsync:us-west-2:123456789012:apis/YourGraphQLApiId/*" ] } ] }
Vous trouverez l'élément YourGraphQLApiId
sur la page principale de liste des API de la console AppSync, directement sous le
nom de votre API. Vous pouvez aussi récupérer cet élément via l'interface de ligne
de commande : aws appsync list-graphql-apis
Si vous souhaitez limiter l'accès à un nombre limité d'opérations GraphQL, vous pouvez
le faire pour les champs racine Query
, Mutation
et Subscription
.
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "appsync:GraphQL" ], "Resource": [ "arn:aws:appsync:us-west-2:123456789012:apis/YourGraphQLApiId/types/Query/fields/<Field-1>", "arn:aws:appsync:us-west-2:123456789012:apis/YourGraphQLApiId/types/Query/fields/<Field-2>", "arn:aws:appsync:us-west-2:123456789012:apis/YourGraphQLApiId/types/Mutation/fields/<Field-1>", "arn:aws:appsync:us-west-2:123456789012:apis/YourGraphQLApiId/types/Subscription/fields/<Field-1>" ] } ] }
Par exemple, supposons que vous ayez le schéma suivant et que vous souhaitiez limiter l'accès à l'obtention de tous les billets de blog :
schema { query: Query mutation: Mutation } type Query { posts:[Post!]! } type Mutation { addPost(id:ID!, title:String!):Post! }
La stratégie IAM correspondante pour un rôle (que vous pouvez lier à un groupe d'identités Amazon Cognito, par exemple) se présente comme suit :
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "appsync:GraphQL" ], "Resource": [ "arn:aws:appsync:us-west-2:123456789012:apis/YourGraphQLApiId/types/Query/fields/posts" ] } ] }
Autorisation OPENID_CONNECT
Ce type d'autorisation applique les jetons OpenID Connect
La seule valeur de configuration obligatoire que vous fournissez à AWS AppSync est
une URL d'auteur (par exemple, https://auth.example.com
). Cette URL doit être adressable via HTTPS. AWS AppSync ajoute /.well-known/openid-configuration
à l'URL d'auteur et localise la configuration OpenID à l'adresse https://auth.example.com/.well-known/openid-configuration
conformément à la spécification OpenID Connect Discoveryjwks_uri
qui pointe vers le document JSON Web Key Set (JWKS) avec les clés de signature.
AWS AppSync nécessite le JWKS pour contenir les champs JSON de alg
, kty
et kid
.
AWS AppSync prend en charge RS256 RS384 RS512 comme algorithmes de signature. Les
jetons émis par le fournisseur doivent inclure l'heure d'émission du jeton (iat
) et peuvent inclure son heure d'authentification (auth_time
). Vous pouvez fournir des valeurs de durée de vie (TTL) pour l'heure d'émission (iatTTL
) et l'heure d'authentification (authTTL
) dans votre configuration OpenID Connect afin de renforcer la validation. Si votre
fournisseur autorise plusieurs applications, vous pouvez également fournir une expression
régulière (clientId
) qui est utilisée pour les autorisations par ID client.
Pour valider plusieurs ID de clients, utilisez l'opérateur de pipeline (« | ») qui est un « ou » dans regex. Par exemple, si votre application OIDC a quatre clients avec des ID clients tels que 0A1S2D, 1F4G9H, 6GS5MG, 1J6L4B, pour valider uniquement les trois premiers ID clients, vous devez placer 1F4G9H|1J6L4B|6GS5MG dans le champ ID client.
Autorisation AMAZON_COGNITO_USER_POOLS
Ce type d'autorisation applique les jetons OIDC fournis par les groupes d'utilisateurs Amazon Cognito. Votre application peut mettre à profit les utilisateurs et les groupes dans vos groupes d'utilisateurs et les associer à des champs GraphQL pour le contrôle d'accès.
Lorsque vous utilisez des groupes d'utilisateurs Amazon Cognito, vous pouvez créer les groupes auxquels les utilisateurs appartiennent. Ces informations sont codées dans un jeton JWT que votre application envoie à AWS AppSync dans un en-tête d'autorisation lors de l'envoi des opérations GraphQL. Vous pouvez utiliser les directives GraphQL sur le schéma pour contrôler les groupes pouvant appeler des résolveurs sur un champ, ainsi que les résolveurs pouvant être appelés, ce qui permet à vos clients de bénéficier d'un accès plus contrôlé.
Supposons, par exemple, que vous ayez le schéma GraphQL suivant :
schema { query: Query mutation: Mutation } type Query { posts:[Post!]! } type Mutation { addPost(id:ID!, title:String!):Post! } ...
Si vous avez deux groupes dans les groupes d'utilisateurs Amazon Cognito (blogueurs et lecteurs) et que vous souhaitez limiter les lecteurs afin qu'ils ne puissent pas ajouter de nouvelles entrées, votre schéma doit se présenter comme suit :
schema { query: Query mutation: Mutation }
type Query { posts:[Post!]! @aws_auth(cognito_groups: ["Bloggers", "Readers"]) } type Mutation { addPost(id:ID!, title:String!):Post! @aws_auth(cognito_groups: ["Bloggers"]) } ...
Notez que vous pouvez omettre la directive @aws_auth
si vous souhaitez utiliser par défaut une stratégie d'octroi ou de refus spécifique
pour l'accès. Vous pouvez spécifier cette stratégie d'octroi ou de refus dans la configuration
du groupe d'utilisateurs lors de la création de votre API GraphQL via la console ou
via la commande suivante de l'interface de ligne de commande :
$ aws appsync --region us-west-2 create-graphql-api --authentication-type AMAZON_COGNITO_USER_POOLS --name userpoolstest --user-pool-config '{ "userPoolId":"test", "defaultEffect":"ALLOW", "awsRegion":"us-west-2"}'
Utilisation de modes d'autorisation supplémentaires
Lorsque vous ajoutez des modes d'autorisation supplémentaires, vous pouvez configurer
directement le paramètre d'autorisation au niveau de l'API GraphQL AWS AppSync (c'est-à-dire,
le champ authenticationType
que vous pouvez configurer directement sur l'objet GraphqlApi
) et il agit en tant que valeur par défaut sur le schéma. Cela signifie que tout type
n'ayant pas de directive spécifique doit transmettre le paramètre d'autorisation au
niveau de l'API.
Au niveau du schéma, vous pouvez spécifier des modes d'autorisation supplémentaires
à l'aide de directives sur le schéma. Vous pouvez spécifier des modes d'autorisation
sur des champs spécifiques du schéma. Par exemple, pour l'autorisation API_KEY
, vous devez utiliser @aws_api_key
sur les définitions/champs de type d'objet de schéma. Les directives suivantes sont
prises en charge sur les champs de schéma et les définitions de types d'objet :
-
@aws_api_key
: permet de spécifier que le champ a l'autorisationAPI_KEY
. -
@aws_iam
: permet de spécifier que le champ a l'autorisationAWS_IAM
. -
@aws_oidc
: permet de spécifier que le champ a l'autorisationOPENID_CONNECT
. -
@aws_cognito_user_pools
: permet de spécifier que le champ a l'autorisationAMAZON_COGNITO_USER_POOLS
.
Vous ne pouvez pas utiliser la directive @aws_auth
avec des modes d'autorisation supplémentaires. @aws_auth
fonctionne uniquement dans le contexte de l'autorisation AMAZON_COGNITO_USER_POOLS
sans mode d'autorisation supplémentaire. Cependant, vous pouvez utiliser la directive
@aws_cognito_user_pools
à la place de la directive @aws_auth
, en utilisant les mêmes arguments. La principale différence entre les deux est que
vous pouvez spécifier @aws_cognito_user_pools
sur n'importe quelle définition de champ et de type d'objet.
Pour comprendre comment fonctionnent les modes d'autorisation supplémentaires et comment ils peuvent être spécifiés sur un schéma, examinons le schéma suivant :
schema { query: Query mutation: Mutation } type Query { getPost(id: ID): Post getAllPosts(): [Post] @aws_api_key } type Mutation { addPost( id: ID! author: String! title: String! content: String! url: String! ): Post! } type Post @aws_api_key @aws_iam { id: ID! author: String title: String content: String url: String ups: Int! downs: Int! version: Int! } ...
Pour ce schéma, supposons que AWS_IAM
est le type d'autorisation par défaut sur l'API GraphQL AWS AppSync. Cela signifie
que les champs qui n'ont pas de directive sont protégés à l'aide de AWS_IAM
. Par exemple, c'est le cas pour le champ getPost
sur le type Query
. Les directives de schéma vous permettent d'utiliser plusieurs modes d'autorisation.
Par exemple, vous pouvez avoir configuré API_KEY
comme mode d'autorisation supplémentaire sur l'API GraphQL AWS AppSync et vous pouvez
marquer un champ à l'aide de la directive @aws_api_key
(par exemple, getAllPosts
dans cet exemple). Les directives fonctionnent au niveau du champ. Vous devez donc
également accorder à API_KEY
l'accès au type Post
. Vous pouvez le faire en marquant chaque champ du type Post
avec une directive ou en marquant le type Post
avec la directive @aws_api_key
.
Pour limiter davantage l'accès aux champs du type Post
, vous pouvez utiliser des directives sur les champs individuels du type Post
, comme illustré ci-après.
Par exemple, vous pouvez ajouter un champ restrictedContent
au type Post
et limiter l'accès à celui-ci à l'aide de la directive @aws_iam
. Les demandes AWS_IAM
authentifiées pourront accéder à restrictedContent
, mais les demandes API_KEY
ne pourront pas y accéder.
type Post @aws_api_key @aws_iam{ id: ID! author: String title: String content: String url: String ups: Int! downs: Int! version: Int! restrictedContent: String! @aws_iam } ...
Contrôle précis des accès
Les informations précédentes montrent comment limiter ou accorder l'accès à certains champs GraphQL. Si vous voulez définir des contrôles d'accès sur les données en fonction de certaines conditions (par exemple, en fonction de l'utilisateur effectuant l'appel et du fait qu'il est ou non propriétaire des données), vous pouvez utiliser des modèles de mappage dans vos résolveurs. Vous pouvez également utiliser une logique métier plus complexe, dont vous trouverez la description dans Filtrage des informations.
Cette section explique comment définir les contrôles d'accès sur vos données à l'aide d'un modèle de mappage de résolveur DynamoDB.
Avant d'aller plus loin, si vous n'êtes pas familiarisé avec les modèles de mappage dans AWS AppSync, vous pouvez consulter les documents Référence du modèle de mappage des résolveurs et Référence du modèle de mappage des résolveurs pour DynamoDB.
Dans l'exemple suivant qui utilise DynamoDB, supposons que vous utilisiez le schéma de billet de blog précédent et que seuls les utilisateurs ayant créé un billet soient autorisés à le modifier. Le processus d'évaluation devrait consister pour l'utilisateur à obtenir des informations d'identification dans son application, à l'aide de groupes d'utilisateurs Amazon Cognito, par exemple, puis à transmettre ces informations d'identification dans le cadre d'une opération GraphQL. Le modèle de mappage remplace alors une valeur des informations d'identification (comme le nom utilisateur) dans une instruction conditionnelle qui sera ensuite comparée à une valeur dans votre base de données.

Pour ajouter cette fonctionnalité, ajoutez un champ GraphQL editPost
comme suit :
schema { query: Query mutation: Mutation } type Query { posts:[Post!]! } type Mutation { editPost(id:ID!, title:String, content:String):Post addPost(id:ID!, title:String!):Post! } ...
Le modèle de mappage de résolveur pour editPost
(illustré dans un exemple à la fin de cette section) doit effectuer une vérification
logique par rapport à votre magasin de données afin que seul l'utilisateur ayant créé
un billet puisse le modifier. Dans la mesure où il s'agit d'une modification, elle
correspond à une opération UpdateItem
dans DynamoDB. Vous pouvez effectuer une vérification conditionnelle avant d'exécuter
cette action, en utilisant le contexte transmis pour la validation de l'identité de
l'utilisateur. Ce dernier se trouve dans un objet Identity
qui a les valeurs suivantes :
{ "accountId" : "12321434323", "cognitoIdentityPoolId" : "", "cognitoIdentityId" : "", "sourceIP" : "", "caller" : "ThisistheprincipalARN", "username" : "username", "userArn" : "Sameasabove" }
Pour utiliser cet objet dans un appel DynamoDBUpdateItem
, vous devez stocker les informations d'identité d'utilisateur dans la table pour
la comparaison. Tout d'abord, votre mutation addPost
doit contenir le créateur. Ensuite, votre mutation editPost
doit effectuer la vérification conditionnelle avant la mise à jour.
Voici un exemple de modèle de mappage de demande pour une mutation addPost
qui contient l'identité d'utilisateur sous la forme d'une colonne Author
:
{ "version" : "2017-02-28", "operation" : "PutItem", "key" : { "postId" : $util.dynamodb.toDynamoDBJson($context.arguments.id) }, "attributeValues" : { "Author" : $util.dynamodb.toDynamoDBJson($context.identity.username) #foreach( $entry in $context.arguments.entrySet() ) #if( $entry.key != "id" ) ,"${entry.key}" : $util.dynamodb.toDynamoDBJson($entry.value) #end #end }, "condition" : { "expression" : "attribute_not_exists(postId)" } }
Notez que l'attribut Author
est renseigné à partir de l'objet Identity
, qui provient de l'application.
Enfin, voici un exemple de modèle de mappage de demande pour une mutation editPost
, qui met à jour le contenu du billet de blog uniquement si la demande provient de
l'utilisateur auteur du billet :
{ "version" : "2017-02-28", "operation" : "PutItem", "key" : { "id": $util.dynamodb.toDynamoDBJson($ctx.args.id), }, "attributeValues" : $util.dynamodb.toMapValuesJson($ctx.args), "condition" : { "expression" : "contains(#author,:expectedOwner)", "expressionNames" : { "#author" : "Author" }, "expressionValues" : { ":expectedOwner" : $util.dynamodb.toDynamoDBJson($context.identity.username) } } }
Cet exemple utilise un PutItem
qui écrase toutes les valeurs plutôt qu'un UpdateItem
, ce qui serait un peu plus détaillé dans un exemple, mais le même concept s'applique
au bloc d'instruction condition
.
Filtrage des informations
Il peut arriver que vous ne puissiez pas contrôler la réponse de votre source de données, mais que vous ne souhaitiez pas envoyer des informations inutiles aux clients sur la réussite d'une opération de lecture ou d'écriture sur la source de données. Dans ce cas, vous pouvez filtrer les informations à l'aide d'un modèle de mappage de réponse.
Supposons, par exemple, que vous n'ayez pas d'index approprié sur votre table DynamoDB
de billet de blog (par exemple, un index sur Author
). Vous pouvez exécuter une requête GetItem
avec le modèle de mappage suivant :
{ "version" : "2017-02-28", "operation" : "GetItem", "key" : { "postId" : $util.dynamodb.toDynamoDBJson($ctx.args.id) } }
Cette requête renvoie toutes les réponses, même si l'appelant n'est pas l'auteur du billet. Pour éviter cela, vous pouvez effectuer le contrôle d'accès sur le modèle de mappage de réponse comme suit :
{ #if($context.result["Author"] == "$context.identity.username") $utils.toJson($context.result); #end }
Si l'appelant ne correspond pas à ce contrôle, seule une réponse null est renvoyée.
Accès à une source de données
AWS AppSync communique avec les sources de données en utilisant les rôles et les stratégies
d'accès de gestion des identités et des accès (IAM
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "appsync.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }
Il est important de définir la stratégie d'accès en fonction du rôle, afin de n'avoir que les autorisations nécessaires pour agir sur l'ensemble minimal de ressources nécessaires. Lorsque vous utilisez la console AppSync pour créer une source de données et un rôle, cette opération est effectuée automatiquement pour vous. Cependant, lorsque vous utilisez un modèle d'exemple intégré de la console IAM pour créer un rôle en dehors de la console AWS AppSync, les permissions ne seront pas automatiquement limitées à une ressource et vous devez effectuer cette action avant de déplacer votre application en production.