App Sync Security For IAM_AUTH

0

Suppose I have the given graphql schema using AWS_IAM authentication:

type User{
   userId: ID
   name: String
   myConversationIds: [ID]
}

type Conversation {
    conversationId: ID
    messages: [Message]
}

type Message{
    content: String
    senderId: ID
    conversationId: ID
}

type Mutation{
    sendMessage(userId: ID, conversationId: ID, content: String) : Message
}

Would it be possible for a malicious user, to use curl or some other external program to call sendMessage, with another user's userId, or a conversation they are not invited to?

Would it be necessary to authenticate the user by checking their ID, and checking if they are invited to the given conversation before invoking sendMessage?

Or is it impossible for a malicious user to construct their own mutations, and can only query appsync through the authenticated application?

How does the appsync console queries page send requests to the api when authenticated with IAM?

OGDev
asked 5 years ago605 views
6 Answers
0

Would it be possible for a malicious user, to use curl or some other external program to call sendMessage, with another user's userId, or a conversation they are not invited to?

No, you would have an Auth type set on the API. So only the requests with proper Authorization headers are allowed to execute this query. AppSync provides you $context.identity inside the resolver mapping templates, so you can always use that to limit the "userId" in the query parameter.

Would it be necessary to authenticate the user by checking their ID, and checking if they are invited to the given conversation before invoking sendMessage?

This seems to be your business logic, so you can always add Auth rules inside your mapping templates to return $util.unauthorized() for any particular field in the query graph. You can check multiple utility functions from here: https://docs.aws.amazon.com/appsync/latest/devguide/resolver-util-reference.html

Or is it impossible for a malicious user to construct their own mutations, and can only query appsync through the authenticated application?

Again, your API is protected with the Auth type you decide, so no malicious user can get access to your API without proper authorization headers.

How does the appsync console queries page send requests to the api when authenticated with IAM?

AWS AppSync uses the IAM permissions of the logged in user to run your query on the console.

Edited by: Shankar-AWS on Feb 21, 2019 11:49 AM

AWS
answered 5 years ago
0

No, you would have an Auth type set on the API. So only the requests with proper Authorization headers are allowed to execute this query. AppSync provides you $context.identity inside the resolver mapping templates, so you can always use that to limit the "userId" in the query parameter.

Are these headers, encrypted so that someone using wireshark or another network analyzer can't determine what the authorized header should be and use curl to execute malicious commands?

Maybe this example will better articulate what I am wondering.

// guaranteed to be my UserId
String myUserId = getMyUserId();
// guaranteed to return conversations I am autorized to talk in. 
String[] conversationIds = getAllMyConversationIds(myUserId);

for(String convId : conversationIds){
    // Preconditon: myUserId is the callers userId, and the caller is invited to convId
    // Does the precondition hold against malicious users?
    // Is it nessasary to include Auth Checks inside sendMessage given the precondition?
    sendMessage(myUserId, convId, "Hello World");
}

To add auth checks to send message, I would need to transform it to a pipeline resolver, where I would first need to check the users table (hash: cognitoIdentityId) if the source has the given userId, and then add another function to check the conversations table to see if the user has been invited to the conversation. This would triple the calls needed to dynamodb in order to send a single message. Is this necessary?

Thank you for the help!

OGDev
answered 5 years ago
0

I think you are talking about 2 different things here:

  1. API level Auth - AWS AppSync supports Cognito User Pools, IAM, API_KEY and OIDC. Each request to the service has a valid Authorization header, otherwise AppSync will not process these requests. This is vended over an HTTPS connection, so your requests are encrypted. This is similar to any public APIs (like API Gateway).

  2. You can use Cognito group level auth to secure your GraphQL fields. This is purely your business logic, and you should set these rules in your Request and Response mapping templates of your field resolvers. AppSync exposes $context.identity object so you can have these conditions set on the resolver.

You don't necessarily need to have a pipeline resolver to plugin Cognito auth validations for your GraphQL fields.

AWS
answered 5 years ago
0

I see that makes more sense.

Is it safe to expose cognitoIdentityId?

Currently, I have a dynamodb users table with cognitoIdentityId hash key and a userId GSI. To authenticate users, I query the GSI with the provided userId, and then check if the source.cognitoIdentityId equals the cognitoIdentityId stored in dynamodb.

This is not ideal because I have a GSI and have to frequently use pipeline resolvers for auth checks. It would be much better if I could expose cognitoIdentityIds and not need userIds at all.

I don't see any way out of making an additional dynamodb call to the conversations table to check if the user is associated with the given conversationId.

Therefore to send a message I would need to use the pipeline:

  • AuthUserbyId
  • AuthUserConversation
  • SendMessage

Is there any way to cut down the amount of dynamodb calls for sending a single message? Possibly Cognito groups, although I must use IAM auth.

I noticed that the "Chat App" sample project has none of these checks, is it vulnerable to attacks? I may be overthinking things, I just want to make sure my application has no security vulnerabilities.

I really appreciate the help, thank you!

OGDev
answered 5 years ago
0

If you're asking about directly keying and querying on the cognitoIdentityId that's pretty much what Cognito recommends. Do you mean exposing the cognitoIdentityId in your client app?

The cognitoIdentityId in your app can for example correspond to an unauthenticated identity in which case you just need to make sure that the unauthenticated identity only has access to the relevant information in your app. You can restrict access by specifying the policy in the unauthenticated role to only allow access to the appropriate resources.

If it corresponds to a user that federated into the Cognito Identity Pool, when obtaining credentials, Cognito verifies the token the user federated with, issues credentials against the token and identity id and when using those credentials against AppSync, AppSync rehydrates the identity id based on the supplied credentials and you can use the identity id in your templates.

Which part of the process are you unsure of?

answered 5 years ago
0

That pretty much clears it up. I was concerned with exposing cognitoIdentityIds to the client app, so that users can query information directly by using:

getUserByCognitoId(cognitoIdentityId: String): User

Opposed to creating a separate userId field, and a GSI in the users table to create the query:

getUserById(userId: String): User

But it seems like as long as my unauth role is highly restrictive. (Analytics/Cognito Identity) it should be okay.

Thanks!

Edited by: OGDev on Mar 1, 2019 11:49 AM

OGDev
answered 5 years ago

You are not logged in. Log in to post an answer.

A good answer clearly answers the question and provides constructive feedback and encourages professional growth in the question asker.

Guidelines for Answering Questions