I can't really find any documentation on how to protect a public lambda function url. If it's open to the public, it seems like it would be very vulnerable to being spammed by some malicious actor. What is the best practice for protecting the lambda?
According to this page, there are two types of AuthType options.
The AuthType parameter determines how Lambda authenticates or authorizes requests to your function URL. When you configure your function URL, you must specify one of the following AuthType options:
AWS_IAM – Lambda uses AWS Identity and Access Management (IAM) to authenticate and authorize requests based on the IAM principal's identity policy and the function's resource-based policy. Choose this option if you want authenticated users and roles to invoke your function via the URL.
NONE – Lambda doesn't perform any authentication before invoking your function. However, your function's resource-based policy is always in effect and must grant public access before your function URL can receive requests. Choose this option to allow public, unauthenticated access to your function URL.
I am guessing you want to use the AWS_IAM option. If neither option suits your need, I suggest attaching it behind API Gateway.
Setup API Gateway in front of Lambda then Enable authorization on API Gateway. Malicious requests will not reach too your Lambda function.
Related
I am new to the AWS world of API Gateway, and am trying to limit access to my APIs by user group. To clarify I can already run authenticated API, the question is around authorization (limit a group of users to API-1, and another group to API-2). I am using HTTP-API and I do not wish to use others (to save cost).
I have created a Cognito user-pool and created two groups called 'regular' and 'admin'. I have also setup an app-client.
Next I have two API routes, that map to two different Lambda functions (just hello world). These work perfectly without authentication, and also with authentication. I am using JWT-auth with Cognito, and for multiple reasons, this is the right approach for my app.
The trouble is how do I restrict access to the API, for the relevant group of users. Hence, only the users in the admin-group should be able to use the admin-api. I believe the section in red can help, but I cant seem to find the right documentation. I read that I can also create a lambda function to authorize users, but that seems like a waste, why pay for another lambda function, if the restrictions can be applied here.
Would appreciate any help.
Application scopes are not what are looking for (they define application access, not user access).
This is broadly referred to as Role-based Access Control (RBAC). The built in authorizer doesn't have any support for it so you'll have to either check the role/group in you handler or opt for a custom authorizer.
Scopes are not the right thing to use here. Scopes work well if you have different app clients (actual applications using your API Gateway) to limit the scope of what endpoints they can access. They don't work for role based access.
As you say, you already use Cognito User Pool and have some user groups setup. A simple apporach would be to use a post confirmation Lambda trigger. This Lambda is triggered whenever a new user has been confirmed. It even gets triggered when you use an external identity provider and a user logs in the first time with Cognito hosted UI for instance. In the simplest case you add a user to your 'regular' group with the post confirmation Lambda.
For authentication and authorization on your API Gateway routes, you replace JWT authorizer with a custom authorizer. This means, you will use a Lambda function to do the authentication and authorization. Within the Lambda function you must verify the JWT token. If the JWT token or the request itself is invalid you throw an exception with the message "Unauthorized". API Gateway will translate this to a 401 "Unauthorized" response. If the JWT token is valid, you decode it and get the cognito:groups claim out of it. You can decode your token and look at the claims on jwt.io for testing. In you Lambda function, you then check in what groups your user is and if the group has access to a specific route. The route the user tries to access is also part of the event triggering your Lambda function.
# dummy python code
if route.startswith('/adminuser') and 'admin' in cognito_groups:
return { 'isAuthorized': True, 'context': { 'custom_key': 'custom_value' } }
# respond with 403 "Forbidden"
return { 'isAuthorized': False }
The payload for the Lambda input and output can be found in the HHTP API Lambda Authorizer documentation.
For a deeper dive, have a look into "building fine-grained authorization using Amazon Cognito, API Gateway, and IAM". For a more advanced look into authorization options, I recommend this video from the re:invent 2017.
One idea is to create a different Cognito Userpool for each group. Then within each pool, define a Resource Server and put the group name as a scope. This way the group is available in the scope. Then, make a combined Cognito authorizer as follows.
securitySchemes:
CognitoAuth:
type: apiKey
name: Authorization
in: header
x-amazon-apigateway-authtype: cognito_user_pools
x-amazon-apigateway-authorizer:
type: cognito_user_pools
providerARNs:
- 'arn:aws:cognito-idp:{region}:{account}:userpool/{pool1}'
- 'arn:aws:cognito-idp:{region}:{account}:userpool/{pool2}'
- 'arn:aws:cognito-idp:{region}:{account}:userpool/{pool3}'
You are then free to put the group names (as scopes) globally or in any path.
Furthermore, you will have more control over the policies (e.g., custom attributes, MFA, sign-up options) for each group by virtue of having used different pools.
Currently I have a single method on a resource that I want both non authenticated and authenticated (using Cognito User Pools) users to access. To my understanding, API gateway only allows methods to be either authenticated or non authenticated so if I use a standard Cognito User Pool authorizer it will just deny access to all non authenticated users. My idea right now is to write a custom lambda authorizer that validates an ID token passed and forwards the claim or allows non authenticated users to pass through. Is there an easier way of accomplishing this?
Here's a strategy you could explore: Setup two endpoints: one goes through Cognito and the other allows traffic through unauthenticated but have them target the same Lambda function. Alter the API Gateway response on the authenticated endpoint to redirect on missing authentication. You'd then point the redirect to the unauthenticated endpoint.
Here's an AWS Guide on setting up an API Gateway Response.
I have a RESTful API being accessed via AWS API Gateway. I want both human users (authenticated via Cognito) AND other (internal) services to be able to call the API. There exists plenty of documentation of how to do either, but not for how to do both.
I have tried using unauthenticated Cognito federated identities to authorize services. I have also used Cognito user pools to authenticate people. However, API Gateway only allows for a single authorizer (in this case, either AWS_IAM or a Cognito authorizer). However, I don't see a way to do both.
I imagine I will need a custom Lambda authorizer, but I'm having a lot of trouble finding accurate information on how to write an authorizer using IAM or Cognito.
Surely we can't be the first people to want to allow both services and humans access to an API. The dearth of documentation on doing so leads me to believe I'm either missing something painfully obvious or that this is a use case that is best avoided (in which case, what IS best practice?).
Is there any way of doing both? Is there a Lambda authorizer example that can point me in the right direction? Is there something built-in that I'm missing?
We have rejected the idea of "pretending" that a Cognito user is a human, when in fact it's actually a service, as it seems to us to be a misuse of Cognito user pools and identity federation. Likewise, the opposite, where a Cognito user is federated to act as an IAM role shared by both users and services, seems like a misuse of user authentication.
Is there a canonical way to do this? If not, is there existing documentation of how to write a custom Lambda authorizer to do both?
The short answer is: you (probably) don't need to use API Gateway for either case.
API Gateway, as the name suggests, is just a gateway. It takes a request (often HTTP) and forwards it to another location (a Lambda function, an EC2 instance, another AWS service, or another HTTP endpoint). This is great if you need users who do not have an associated IAM role to access AWS services.
Users who are authenticated through Cognito have an associated IAM role through the associated Cognito Identity Pool. Just give them access to the AWS services this way, no need to go through API Gateway.
AWS services that need access to other AWS services don't need to go through API Gateway, they need to be given permission through their IAM role.
If for some reason you must use API Gateway for both your users and your services, make two API Gateways that point to the same resources that use different authentication methods.
I would like make a Lambda authorizer, that validates a jwt token issued by a third party IDP (Onelogin for instance), generate an IAM policy restricting a user access to specific API endpoints from the API gateway based on permissions extracted from the jwt token. I would like also to make the API Gateway cache the generated policy so that requests coming from the same user don't need to be passed to the authorizer and could be accepted or rejected based on the cached policy. Is such a scenario possible? If it is, I would appreciate any guidance on how to configure it.
Thank you!
I'm currently using a Cognito User Pool as an authorizer for an API Gateway endpoint, through to a Lambda function.
Can I pass the Integrated Request on to Lambda and SECURELY allow or deny from inside Lambda based on a custom attribute?
Mapping:
"administrator" : "$context.authorizer.claims['custom:administrator']",
Lambda handler:
boolean isAdmin = Boolean.parseBoolean(request.getContext().get("administrator"));
if(isAdmin) etc...
To be clear, a user that is NOT an administrator should not have access to the same API endpoints that and Administrator does..
Do I need to do anything else before/after this point?
I am sending the initial request to the API Gateway with Javascript after the user has logged into Cognito, by including the Authorization: JWToken header.
Do I need to verify the signature of the token in the Lambda function? I presume that API Gateway has already done that.
Is there a better way to manage this in terms of security?
Ideally I would like to be able to limit access to the API Endpoint based on GROUPS within the User Pool, however I don't think this is possible.
The Groups documentation talks about limiting access/permissions via AWS Identity and Access Management. If I go down this path, how do I make a request to the API Gateway? Do I still use the JWToken Authorization header, and use Cognito as the Authorizer in API Gateway?
The custom attribute/claim support included with Cognito user pools is secure and can be used for use cases such as this when used correctly. There are a couple of caveats.
First, ensure that users aren't able to modify the custom attribute themselves. When adding the customer attribute, do not mark the attribute as mutable. Also, custom attributes can be can be marked as readable or writable for each application. For this use case, you'll want to set the attribute as readable for the application the users have access to. Details about custom attributes can be found here.
The other caveat is to ensure that your request body can never by-pass your mapping template which could allow an attacker a way to directly set the administrator attribute being passed to your Lambda function. To do this, edit your integration request and set "Request body passthrough" to "Never".
There are other alternatives you could use for this use case. The cleanest approach is to provide a completely separate API for your administrators. Then you can use a separate Cognito user pool for your administrators, or you could use IAM users or groups.