보안 - AWS AppSync

보안

이 단원에서는 애플리케이션에 대한 보안 및 데이터 보호 구성을 위한 옵션을 설명합니다.

AWS AppSync GraphQL API와 상호 작용하도록 애플리케이션을 다음과 같은 4가지 방법으로 승인할 수 있습니다. AWS AppSync API 또는 CLI를 호출할 때 다음과 같은 권한 부여 유형 값 중 하나를 지정하여 사용할 권한 부여 유형을 지정합니다.

  • API_KEY

    API 키를 사용하는 경우

  • AWS_IAM

    AWS Identity and Access Management(IAM) 권한을 사용하는 경우

  • OPENID_CONNECT

    OpenID Connect 공급자를 사용하는 경우

  • AMAZON_COGNITO_USER_POOLS

    Amazon Cognito 사용자 풀을 사용하는 경우

이러한 기본 권한 부여 유형은 대부분의 개발자에게 유용합니다. 추가 고급 사용 사례를 보려면 콘솔, CLI 및 AWS CloudFormation을 통해 추가 권한 부여 모드를 추가할 수 있습니다. 추가 권한 부여 모드의 경우 AppSync는 위에 나열된 값(즉, API_KEY, AWS_IAM, OPENID_CONNECT, AMAZON_COGNITO_USER_POOLS)을 가져오는 권한 부여 유형을 제공합니다.

API_KEY 또는 AWS_IAM을 주 또는 기본 권한 부여 유형으로 지정하면 추가 권한 부여 모드 중 하나로 다시 지정할 수 없습니다. 마찬가지로, 추가 권한 부여 모드 내부에서 API_KEYAWS_IAM을 중복할 수 없습니다. 여러 개의 Amazon Cognito 사용자 풀 및 OpenID Connect 공급자를 사용할 수 있습니다. 하지만 기본 권한 부여 모드와 추가 권한 부여 모드 중 하나 간에 중복 Amazon Cognito 사용자 풀 또는 OpenID Connect 공급자를 사용할 수 없습니다. 해당 구성 regex를 사용하여 Amazon Cognito 사용자 풀 또는 OpenID Connect 공급자에 대해 다른 클라이언트를 지정할 수 있습니다.

API_KEY 권한 부여

인증되지 않은 API에는 인증된 API보다 더 엄격한 조절이 필요합니다. 인증되지 않은 GraphQL 엔드포인트에 대한 조절을 제어하는 한 가지 방법은 API 키를 사용하는 것입니다. API 키는 애플리케이션의 하드 코딩된 값으로, 인증되지 않은 GraphQL 엔드포인트를 생성하는 경우 AWS AppSync 서비스에서 생성합니다. 콘솔, CLI 또는 AWS AppSync API 참조에서 API 키를 교체할 수 있습니다.

API 키는 최대 365일 동안 구성할 수 있으며 기존 만료 날짜를 해당일부터 최장 365일 더 연장할 수 있습니다. API 키는 개발용 또는 퍼블릭 API를 노출해도 안전한 사용 사례에 권장됩니다.

클라이언트에서 API 키는 헤더 x-api-key가 지정합니다.

예를 들어, API_KEY'ABC123'인 경우 다음과 같이 curl 을 통해 GraphQL 쿼리를 보낼 수 있습니다.

$ curl -XPOST -H "Content-Type:application/graphql" -H "x-api-key:ABC123" -d '{ "query": "query { movies { id } }" }' https://YOURAPPSYNCENDPOINT/graphql

AWS_IAM 권한 부여

이 권한 부여 유형은 GraphQL API에 대해 AWS Signature 버전 4 서명 프로세스를 적용합니다. IAM(Identity and Access Management) 액세스 정책을 권한 부여 유형과 연결할 수 있습니다. 애플리케이션에서는 액세스 키(액세스 키 ID와 보안 액세스 키로 구성됨)를 사용하거나 Amazon Cognito 연동 자격 증명에서 제공한 단기 임시 자격 증명을 사용하여 이러한 연결을 활용할 수 있습니다.

모든 데이터 작업을 수행하기 위한 액세스 권한이 있는 역할이 필요한 경우:

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "appsync:GraphQL" ], "Resource": [ "arn:aws:appsync:us-west-2:123456789012:apis/YourGraphQLApiId/*" ] } ] }

AppSync 콘솔의 기본 API 목록 페이지에 있는 API 이름 바로 아래에서 YourGraphQLApiId를 찾을 수 있습니다. 또한 CLI aws appsync list-graphql-apis를 사용하여 검색할 수도 있습니다.

특정 GraphQL 작업으로 액세스를 제한하려는 경우 루트 Query, MutationSubscription 필드에 대해 액세스를 제한할 수 있습니다.

{ "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>" ] } ] }

예를 들어, 다음 스키마를 가정하고 모든 게시글 가져오기에 대한 액세스를 제한하려고 합니다.

schema { query: Query mutation: Mutation } type Query { posts:[Post!]! } type Mutation { addPost(id:ID!, title:String!):Post! }

(예를 들어 Amazon Cognito 자격 증명 풀에 연결할 수 있는) 역할에 대한 IAM 정책은 다음과 같습니다.

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "appsync:GraphQL" ], "Resource": [ "arn:aws:appsync:us-west-2:123456789012:apis/YourGraphQLApiId/types/Query/fields/posts" ] } ] }

OPENID_CONNECT 권한 부여

이 권한 부여 유형은 OIDC 호환 서비스에서 제공하는 OpenID Connect(OIDC) 토큰을 강제로 적용합니다. 애플리케이션에서는 OIDC 공급자가 액세스 제어를 위해 정의한 사용자 및 권한을 활용합니다.

발행자 URL은 AWS AppSync에 제공하는 유일한 필수 구성 값입니다(예: https://auth.example.com). 이 URL은 HTTPS를 통해 주소를 지정할 수 있어야 합니다. AWS AppSync에서는 /.well-known/openid-configuration을 발행자 URL에 추가하고 OpenID Connect Discovery 사양에 따라 https://auth.example.com/.well-known/openid-configuration에서 OpenID 구성을 찾습니다. 이 URL에서 RFC5785 호환 JSON 문서를 검색해야 합니다. 이 JSON 문서에는 서명 키와 함께 JWKS(JSON 웹 키 집합) 문서를 가리키는 jwks_uri 키가 포함되어 있어야 합니다.

AWS AppSync에서는 JWKS에 JSON 필드 alg, kty, kid가 포함되어야 합니다.

AWS AppSync에서는 서명 알고리즘으로 RS256, RS384 및 RS512를 지원합니다. 공급자가 발행하는 토큰에는 토큰이 발행된 시간(iat)이 포함되어 있어야 하며 토큰이 인증된 시간(auth_time)이 포함될 수 있습니다. 추가 확인을 위해 발행된 시간에 대한 TTL 값(iatTTL)과 OpenID Connect 구성의 인증 시간(authTTL)을 제공할 수 있습니다. 공급자가 여러 애플리케이션을 인증하면 클라이언트 ID별로 인증하는 데 사용되는 정규식(clientId)도 제공할 수 있습니다.

여러 클라이언트 ID를 확인하려면 정규식에서는 "or"인 파이프라인 연산자("|")를 사용합니다. 예를 들어 OIDC 애플리케이션에 클라이언트 ID가 각각 0A1S2D, 1F4G9H, 1J6L4B, 6GS5MG인 4개의 클라이언트가 있는 경우, 앞쪽의 3개 클라이언트 ID만 검증하려면 클라이언트 ID 필드에 1F4G9H|1J6L4B|6GS5MG를 배치합니다.

AMAZON_COGNITO_USER_POOLS 권한 부여

이 권한 부여 유형은 Amazon Cognito 사용자 풀에서 제공한 OIDC 토큰을 적용합니다. 애플리케이션은 사용자 풀의 사용자 및 그룹을 활용할 수 있고 액세스 제어를 위해 이러한 사용자 및 그룹을 GraphQL 필드와 연결할 수 있습니다.

Amazon Cognito 사용자 풀을 사용하는 경우 사용자가 속한 그룹을 생성할 수 있습니다. GraphQL 작업을 전송할 때 애플리케이션이 권한 부여 헤더에 넣어 AWS AppSync에 전송하는 JWT 토큰에 이 정보가 인코딩되어 있습니다. 스키마에 GraphQL 명령을 사용하여 어떤 그룹이 필드의 어떤 해석기를 호출할 수 있는지 제어하여 고객에게 보다 정확하게 제어되는 액세스 권한을 제공할 수 있습니다.

예를 들어 다음과 같은 GraphQL 스키마가 있다고 가정하겠습니다.

schema { query: Query mutation: Mutation } type Query { posts:[Post!]! } type Mutation { addPost(id:ID!, title:String!):Post! } ...

Amazon Cognito 사용자 풀에 블로거 및 독자라는 두 가지 그룹이 있고 새 항목을 추가할 수 없도록 독자를 제한하려는 경우 스키마는 다음과 같아야 합니다.

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"]) } ...

액세스에 대해 특정 권한 부여 또는 거부 전략을 기본적으로 지정하려는 경우 @aws_auth 명령을 생략할 수 있습니다. 콘솔 또는 다음 CLI 명령을 통해 GraphQL API를 생성하는 경우 사용자 풀 구성에서 권한 부여 또는 거부 전략을 지정할 수 있습니다.

$ 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"}'

추가 권한 부여 모드 사용

추가 권한 부여 모드를 추가하는 경우 AWS AppSync GraphQL API 수준(즉, GraphqlApi 객체에서 직접 구성할 수 있는 authenticationType 필드)에서 권한 부여 설정을 직접 구성할 수 있으며 이 설정은 스키마에서 기본값으로 작용합니다. 다시 말해서, 특정 지시문이 없는 모든 유형은 API 수준 권한 부여 설정을 전달해야 합니다.

스키마 수준에서 스키마에 대한 지시문을 사용하여 추가 권한 부여 모드를 지정할 수 있습니다 스키마의 개별 필드에서 권한 부여 모드를 지정할 수 있습니다. 예를 들어, API_KEY 권한 부여의 경우 스키마 객체 유형 정의/필드에서 @aws_api_key를 사용합니다. 스키마 필드 및 객체 유형 정의에서는 다음 지시문이 지원됩니다.

  • @aws_api_key - 필드에 API_KEY 권한이 부여되도록 지정합니다.

  • @aws_iam - 필드에 AWS_IAM 권한이 부여되도록 지정합니다.

  • @aws_oidc - 필드에 OPENID_CONNECT 권한이 부여되도록 지정합니다.

  • @aws_cognito_user_pools - 필드에 AMAZON_COGNITO_USER_POOLS 권한이 부여되도록 지정합니다.

@aws_auth 지시문을 추가 권한 부여 모드와 함께 사용할 수 없습니다. @aws_auth는 추가 권한 부여 모드가 없는 AMAZON_COGNITO_USER_POOLS 권한 부여의 컨텍스트에서만 작동합니다. 하지만 샘플 인수를 사용하여 @aws_auth 지시문 대신에 @aws_cognito_user_pools 지시문을 사용할 수 있습니다. 두 지시문 간의 주요 차이점은 모든 필드와 객체 유형 정의에서 @aws_cognito_user_pools를 지정할 수 있다는 것입니다.

추가 권한 부여 모드가 작동하는 방식과 스키마에서 추가 권한 부여 모드를 지정할 수 있는 방법을 이해하기 위해 다음 스키마를 살펴보겠습니다.

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! } ...

이 스키마의 경우 AWS AppSync GraphQL API에서 AWS_IAM이 기본 권한 부여 유형이라고 가정합니다. 다시 말해서, 지시문이 없는 필드는 AWS_IAM을 사용하여 보호됩니다. 예를 들어, Query 유형에서 getPost 필드의 경우에도 마찬가지입니다. 스키마 지시문을 사용하면 두 개 이상의 권한 부여 모드를 사용할 수 있습니다. 예를 들어, AWS AppSync GraphQL API에서 추가 권한 부여 모드로 API_KEY를 구성했을 수 있으며, @aws_api_key 지시문을 사용하여 필드를 표시할 수 있습니다(예를 들면 이 경우 getAllPosts). 지시문은 필드 수준에서 작동하므로 API_KEYPost 유형에 대한 액세스 권한도 부여해야 합니다. 지시문으로 Post 유형의 각 필드에 표시하거나 @aws_api_key 지시문으로 Post 유형에 표시하여 이렇게 할 수 있습니다.

Post 유형의 필드에 대한 액세스를 한층 더 제한하려면 다음과 같이 Post 유형의 개별 필드에 대해 지시문을 사용할 수 있습니다.

예를 들어, @aws_iam 지시문을 사용하여 restrictedContent 필드를 Post 유형에 추가하고 이 필드에 대한 액세스를 제한할 수 있습니다. AWS_IAM 권한이 있는 요청은 restrictedContent에 액세스할 수 있지만, API_KEY 요청은 액세스할 수 없습니다.

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 } ...

Fine-Grained Access Control

앞선 정보는 특정 GraphQL 필드에 대한 액세스 제한 또는 부여 방법을 설명합니다. 특정 조건을 기준으로(예를 들어, 호출하는 사용자 및 해당 사용자가 데이터를 소유하는지 여부를 기준으로) 데이터에 대한 액세스 제어를 설정하려는 경우 해석기에서 매핑 템플릿을 사용할 수 있습니다. 또한 필터링 정보에서 설명하는 보다 복잡한 비즈니스 로직을 수행할 수도 있습니다.

이 단원에서는 DynamoDB 해석기 매핑 템플릿을 사용하여 데이터에 대한 액세스 제어를 설정하는 방법을 보여줍니다.

계속 진행하기 전에 AWS AppSync의 매핑 템플릿에 대해 잘 모르는 경우 해석기 매핑 템플릿 참조DynamoDB에 대한 해석기 매핑 템플릿 참조를 검토하면 좋습니다.

DynamoDB를 사용하는 다음 예에서는 이전 블로그 게시 스키마를 사용하고 게시글을 작성한 사용자만 해당 게시글을 편집할 수 있도록 허용한다고 가정합니다. 평가 프로세스는 사용자가 예를 들어, Amazon Cognito 사용자 풀을 사용하여 애플리케이션에서 자격 증명을 얻은 다음 GraphQL 작업의 일부로 해당 자격 증명을 전달하도록 하는 과정입니다. 그런 다음 매핑 템플릿은 조건문에서 자격 증명(예: 사용자 이름)의 값을 대체하고 이 값을 데이터베이스의 값과 비교합니다.

이 기능을 추가하려면 다음과 같이 GraphQL 필드 editPost를 추가합니다.

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! } ...

editPost의 해석기 매핑 템플릿(이 단원의 끝에 나오는 예 참조)은 데이터 스토어에 대한 로직 점검을 수행해 게시글을 생성한 사용자만 해당 게시글을 편집하도록 해야 합니다. 이 작업은 편집 작업이기 때문에 DynamoDB에서는 UpdateItem에 해당합니다. 이 작업을 수행하기 전에 사용자 자격 검증을 통해 전달된 컨텍스트로 조건 검사를 수행할 수 있습니다. 이 컨텍스트는 다음 값을 가지는 Identity 객체에 저장됩니다.

{ "accountId" : "12321434323", "cognitoIdentityPoolId" : "", "cognitoIdentityId" : "", "sourceIP" : "", "caller" : "ThisistheprincipalARN", "username" : "username", "userArn" : "Sameasabove" }

DynamoDBUpdateItem 호출 시 이 객체를 사용하려면 비교를 위해 테이블에 사용자 자격 증명 정보를 저장해야 합니다. 먼저, addPost 변형이 생성자를 저장해야 합니다. 그 다음으로, editPost 변형이 업데이트 전에 조건 검사를 수행해야 합니다.

다음은 사용자 자격 증명을 addPost 열로 저장하는 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)" } }

Author 속성은 애플리케이션에서 가져온 Identity 객체의 속성으로 채워집니다.

마지막으로, 다음은 게시글을 작성한 사용자가 요청한 경우에만 블로그 게시글의 내용을 업데이트하는 editPost의 요청 매핑 템플릿의 예입니다.

{ "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) } } }

이 예에서는 예로써 사용하기에는 지나치게 상세할 수 있는 UpdateItem보다는 모든 값을 덮어쓰는 PutItem을 사용하지만, 동일한 개념이 condition 문 블록에 적용됩니다.

필터링 정보

데이터 원본의 응답을 제어할 수 없는 경우가 있을 수 있지만 데이터 원본에 쓰기 또는 읽기 성공에 대한 불필요한 정보를 클라이언트에게 보내고 싶지 않습니다. 이러한 경우 응답 매핑 템플릿을 사용하여 정보를 필터링할 수 있습니다.

예를 들어, 블로그 게시물 DynamoDB 테이블에 대한 적절한 인덱스(예: Author에 대한 인덱스)가 없다고 가정해 보겠습니다. 다음 매핑 템플릿을 사용하여 GetItem 쿼리를 실행할 수 있습니다.

{ "version" : "2017-02-28", "operation" : "GetItem", "key" : { "postId" : $util.dynamodb.toDynamoDBJson($ctx.args.id) } }

호출자가 게시글을 작성한 작성자가 아니더라도 이 쿼리는 값 응답을 모두 반환합니다. 이렇게 되지 않도록 하기 위해 이 경우 다음과 같이 응답 매핑 템플릿에 대한 액세스 검사를 수행할 수 있습니다.

{ #if($context.result["Author"] == "$context.identity.username") $utils.toJson($context.result); #end }

호출자가 이 검사와 일치하지 않는 경우 null 응답만 반환됩니다.

데이터 원본 액세스

AWS AppSync는 Identity and Access Management(IAM) 역할 및 액세스 정책을 사용하여 데이터 원본과 통신합니다. 기존 역할을 사용할 경우, AWS AppSync에서 해당 역할을 수임하도록 신뢰 정책을 추가해야 합니다. 신뢰 관계는 아래와 같아야 합니다.

{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "Service": "appsync.amazonaws.com" }, "Action": "sts:AssumeRole" } ] }

해당 역할에 대한 액세스 정책을 축소하여 권한을 필요한 최소한의 원본 집합에만 허용하는 것이 중요합니다. AppSync 콘솔을 사용하여 데이터 원본 및 역할을 생성하는 경우, 이 과정은 자동으로 수행됩니다. 그러나 IAM 콘솔에서 내장된 샘플 템플릿을 사용하여 AWS AppSync 콘솔 외부에 역할을 생성하는 경우, 원본에 대한 권한이 자동으로 축소되지 않으므로 애플리케이션을 프로덕션으로 이동시키기 전에 축소 조치해야 합니다.