What is the simplest way to secure my lambda's functionURL? - amazon-web-services

I have a lambda that needs to be accessed by a user. They must be able to visit the URL in a browser. What is the simplest way to secure the functionURL so that if the URL were to leak, there would be another layer of security that would need to be gone through?
I can't use AWS_IAM as the requirement is that the user clicks on the link and I can't expect them to deal with AWS SigV4.
A simple solution would be using the queryparams and adding an access code parameter that is checked for when the lambda is called but this seems like a bad idea for various reasons.

Lambda function URLs only support AWS_IAM and NONE for the AuthType parameter, as mentioned in the documentation.
I can't use AWS_IAM as the requirement is that the user clicks on the link and I can't expect them to deal with AWS SigV4.
Then you need to use an AWS API Gateway, and place your lambda function behind it.

Related

Restrict Lambda function URL access to CloudFront

AWS have recently released the Lambda function URLs feature which allows a function to be invoked via a URL.
I would like to allow my function to be invoked via a URL but only via CloudFront.
I don't want people to be able to bypass CloudFront and invoke the function directly.
Is there a way to configure this? I am aware that I can restrict the function URL by setting the auth type to AWS_IAM but am not clear on how I then allow CloudFront to call it.
Currently, the only option I see is quite similar to how you would protect an ALB in a way that access is restricted to CloudFront:
Configure CloudFront to add a custom HTTP header to requests that it sends to the Application Load Balancer lambda function URL.
Configure the Application Load Balancer Lambda to only forward process requests that contain the custom HTTP header.
My thoughts on approaches that may not work when using lambda function URLs:
IAM auth (since I see no way to sign these requests origination from CloudFront, maybe that will change in the future when lambda function URLs become a first class citizen like S3-origins)
restricting access via security groups (because there are no SGs for lambda func URLs)
Confirmed with AWS support that there is currently no way to do this: "[with the] current design of CloudFront, it is not possible for CloudFront to relay IAM authenticated requests to Lambda URL origin.." There is a feature request for this (but they did not provide a timeframe for implementation and release) but hopefully they provide a solution similar to, and as straight forward as, the CloudFront integration with S3 via the Origin Access Identity.
Here's what I did to make it work on my side :
go to the CloudFront page
click on create a new distribution
In section Origin domain you have to paste in your lambda function URL
Make sure to adjust the caching policy depending on what your lambda function consumes
You might want to create a dedicated policy in you want the cache key to depend on the query string, the cookies, etc...
For my use case I created a new policy to take into account the query string

Authorizing access to specific resources

I'm trying to build a REST API using AWS API Gateway that calls into a bunch of Lambda functions.
I have now set up API Gateway to use a Cognito user pool as the authorizer, but all that's really doing is authenticating the user since I've attached the user pool to all the endpoints. I wasn't able to figure out how to specifically allow certain methods on certain endpoints.
For example, if my user is 123 and belongs to group ABC, I would only like them to be able to GET /users?group=ABC or PATCH /users/123.
Is it possible to achieve this level of control or do I need to implement those checks in the Lambda function that API Gateway calls?
I am developing a similar setup and I faced the exact same issue. My team could not find a solution to this, so we contacted an AWS solutions architect. Here is a summary of their approach to this problem.
Unfortunately there are no default built-in solutions to this specific problem. However you can use some alternative solutions.
It is possible to trigger Lambda functions during Cognito user pool workflows. You may use an extra Lambda function to authorize users by checking their user-group relationships.
Customizing User Pool Workflows with Lambda Triggers
You can use API Gateway Lambda authorizer. In this case you will have to give up the Cognito integration and apply your custom authorization logic inside the authorizer Lambda.
Use API Gateway Lambda authorizers
You can implement the authorization logic inside the lambda functions with an extra query by checking the user-group relationship. You can check the identity of the requesting (Cognito) user through requestContext.authorizer.claims.* in the Lambda event.
Hope this helps.
ofcourse yes u can do it. this will help you.. if u get stuck.. ping me back
https://aws.amazon.com/premiumsupport/knowledge-center/cognito-custom-scopes-api-gateway/

How to secure the APIGateway generated URL?

I have a serverless backend that operates with APIGateway and Lambda. Here is my architecture:
Currently, anyone with my APIGateway's URL can query or mutate the data. How do I protect the URL, so that only the client(react app) can access it. So, here is my concern, anyone can open the network tab in chrome console and get my APIGateway's URL and can use it using curl or postman. I want to prevent that.
Solutions I had in my mind:
Set up a CORS, so that only the origin can access it. But, I have a different lambda that invokes this URL. So, CORS wont work out.
I am sure there are some methods with the APIGateway itself. I am not getting right search term to get it from AWS documentation. I would also like to know what are the best practices to prevent accessing the backend URL apart from the Client(React App)
Update after #Ashan answer:
Thank you #Ashan for the answer. In my case, I use Auth0, so custom authoriser should work for me. I just came across this https://www.youtube.com/watch?v=n4hsWVXCuVI, which pretty much explains all the authorization and authentication possible with APIGateway. I am aware that authentication is possible either by Cognito/Auth0, but I have some simple websites, that has form, whose backend is handled by APIGateway. I can prevent the abuse from scraping bots using captcha, but once the attacker has got the URL, header and request parameters, he can invoke that million times. One thing, we can do is having an API-Key, but it is a static string with no expiration. Once the headers are with him, he can abuse it. So, any idea, how to prevent this in APIGateway. If not any other service apart from AWS that I can look for? Would be glad, If I get an answer for this.
Currently API Gateway does not support private urls, so it will be publicly available.
To restrict access you need to use a authorizer to authenticate and authorize the request using IAM policies. There are two options available at the moment.
IAM authorizer
Custom authorizer
If your authentication flow can directly (AWS STS, IAM user access keys or roles) or indirectly(Using AWS Cognito Userpools or any other SSO provider) can get temporary security credentials, then you can use IAM authorizer. From API Gateway side no code involved and its a matter of selecting the IAM check box for each API Gateway resource. You can use the API Gateway SDKs to invoke API Gateway requests where the SDK will handle the heavy liftings in setting up authentication headers.
If you use your own authentication mechanism, then you can write a seperate Lambda function to validate the tokens. This Lambda function name can be specified at API Gateway with the http hearder name to access the custom token to verify the requests.
To control API usage by authorized consumers, using API Key is the only way native to AWS at the moment.
Since you are using S3 for the react app hosting, you can further reduce the attack surface by using AWS WAF and CloudFront infront your application stack. The API Key can be added to CloudFront headers to forward to your APIGateway origin and since CloudFront and APIGateway communication happens using SSL, its nearly impossible for someone to find the API key. Using AWS WAF you can limit malicious access for common attacks. This includes rate based blocking to limit someone from repeatedly invoking the API.

API Gateway Custom Authorizer - Get Source IP Address

Is it possible to get the source IP address of a request to your API Gateway in a 'Custom Authorizer' lambda function?
This is definitely possible with the actual integration of your API Gateway to a lambda function. Though it does not seem to be possible to get the requester's IP address in a Custom Authorizer function.
My goal is to do rate based blocking directly in APIG. A similar solution is described here. However, as I am only restricting access to one or two APIG endpoints, I'd rather do this in a custom authorizer function which simply adds the source address to the deny policy of the APIG when it reaches a rate limit.
EDIT: To clarify some potential confusion. I understand that I could do this through the regular integration as mentioned above, and in this other post. But I am trying to utilize the custom authorizer functionality, so that I don't have to write the same rate limiting code in all of my lambda functions.
You should look at
event.requestContext.identity.sourceIp
it will contain the original client IP.
When creating the Authorizer on the "Identity Sources" section add
Context: identity.sourceIp
and enable caching (default is 300 sec).
That way your authorizer lambda will not be called for each request, because it will cache the returned policy for that IP.
You can experiment yourself if you add logging of passed event parameter (just don't forget about caching, not all calls to API Gateway fire the authorizer lambda).
BTW, don't use "X-Forwarded-For" look at my comment on another #binshi's answer.
Custom Authorizers can now use so-called Enhanced Context.
You should be able to use the appropriate context variable to get that information (e.g. $context.identity.sourceIp).
You may also be able to use API Mappings, but I haven't tested that.
You can get the source ip as well as any proxy server ip in
events['headers']['X-Forwarded-For']

Is it safe to authenticate a Cognito User through API Gateway to Lambda using a custom property?

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.