API Gateway CORS Issue - amazon-web-services

So I have CORS enabled going through the basic setup given by AWS Gateway. However for this API I need to allow Control Origins for all requests and allow credentials.
Here is what it looks like
The issue as you may have guessed is this setup is not allowed by CORS, you can not have a wildcard for Origin and have credentials as true. Normally the work around for this is to just grab the requesting domain and add it into the Origin Header. Which is more or less what I want to do. But I don't know how to get that information and add it as a mapping value. Where does API Gateway store that information and how do i get it?
UPDATE:
I have to pass through HTTP Header Host to my Lambda Function which I should have mentioned earlier, I have tried implementing the Answer below but I cannot access the header to pass it to the Lambda function using the instructions provided. Any more assistance with this is greatly appreciated.

Okay After hours of research and finding bits of information across the internet I have a solution and hopefully it is useful for other people.
To pass an HTTP Header that is not a default value provided by AWS API Gateway and then access that data via a Lambda Function and return that data in the Response Header follow the steps below
In "Method Request" go to "HTTP Request Headers" and add your desired header to capture. ie. if we want to get the host value of the API url you can enter "Host" here. if you want to get the website host of the caller use "Origin"
In "Integration Request" go to mapping templates and create a new template if an "application/json" does not exist, if it does just update it.
This is the important part, pass the header value you set in step 1. To do that write something similar to the following in the Template Box.
{
"origin" : "$input.params().header.Origin",
"host" : "$input.params().header.Host"
}
You can also pass in any url parameters you have defined in the same JSON.
Access the data from Lambda, The integration request passed the information into the "Event" parameter if using Node as the Lambda Backend code. to retrieve the value of any header just use the following within your handler.
event.origin;
When sending back your response from Lambda to API Gateway, it is best to format the response in JSON. Something similar to this.
{
"origin" : event.origin,
"host" : event.host,
"nonHeaderOutput" : "Hello World"
}
In "Integration Response" go to "Header Mappings", if the header you need is not listed you may add it in "Method Response" and it will then appear here. For this example I used "Access-Control-Allow-Origin" and edited the Mapping Value to be integration.response.body.origin
now go to "Mapping Templates and select the content type you want to use, and then edit the template to access the non header responses by adding this to the Template Box
$input.path("$.nonHeaderOutput")
That is it now the header that was sent to the API can be used in your method Response.

Jurgen from API Gateway here.
Thanks for reporting this issue. There is currently no simple way to set it up via the "Enable CORS" feature in the console. However, we are looking into it to improve the user experience for this use case.
As a potential workaround, you could passthrough the Origin header from the client to your backend and parse / create the value for the Access-Control-Allow-Origin header there. Then you would map the Header in the Integration Response from 'integration.response.header.Access-Control-Allow-Origin' to Access-Control-Allow-Origin and return it to the client.
Hope this helps.
Best,
Jurgen

Related

API Gateway request blocked by CORS

I am using AWS API Gateway put method to post an image to S3 bucket. In API Gateway settings when I add / in Binary Media Types I get the following error message
Access to fetch at ‘some_invokation_request/my_s3_bucket/image.png’ from origin ‘http://localhost:3001’ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: It does not have HTTP ok status.
When I remove / from Binary media types in settings I can post an image and when I check the posted image in S3 it's messed up.
I'll post the original image and S3 image
Apparently this is an open issue right now and you can find further details here
I believe you have two different errors present. One is CORS related and the other is the upload of an image getting messed up. I don't think they are necessarily related. I've hit the same issue with the image upload showing the tiny white square but faced no CORS issue.
For me, the problem turned out to be trying to combine the use of Convert to Binary (if needed) and a mapping template on the Integration Request. According to the documentation (and my experience as well), if a mapping template is defined, APIGW doesn't run the Convert to Binary passthrough functionality. What gets passed to the S3 bucket for saving is a base64 encoded string instead of actual binary, thus the messed up image.
Excerpt from the linked documentation:
"On the contentHandling property of the Integration resource, set CONVERT_TO_BINARY. Set WHEN_NO_MATCH as the passthroughBehavior property value without defining a mapping template. This enables API Gateway to invoke the passthrough template."

API Gateway Lambda CORS handler. Getting Origin securely

I want to implement CORS for multiple origins and I understand I need to do so via a lambda function as I cannot do that via the MOCK method
exports.handler = async (event) => {
const corsUrls = (process.env.CORS_URLS || '').split(',')
const requestOrigin = (event.headers && event.headers.origin) || ''
if (corsUrls.includes(requestOrigin)) {
return {
statusCode: 204,
headers: {
"Access-Control-Allow-Headers": 'Content-Type,X-Amz-Date,Authorization,X-Api-Key,X-Amz-Security-Token,X-Requested-With',
'Access-Control-Allow-Origin': requestOrigin,
'Access-Control-Allow-Methods': 'POST,DELETE,OPTIONS'
}
}
}
return {
statusCode: 403,
body: JSON.stringify({
status: 'Invalid CORS origin'
})
}
}
Firstly, does the above looks ok? Then I am getting origin from headers event.headers.origin. But I find that I can just set that header manually to "bypass" cors. Is there a reliable way to detect the origin domain?
Firstly, does the above looks ok?
Your code looks good to me at first glance, and other than your point But I find that I can just set that header manually to "bypass" cors, I don't see any major problems with it.
Then I am getting origin from headers event.headers.origin. But I find that I can just set that header manually to "bypass" cors. Is there a reliable way to detect the origin domain?
The code you are currently using is the only way I can think of how to detect the origin domain off the top of my head. Although as you said, you can just set that header manually, and there is 0 assurances that header is correct or valid. It shouldn't be used as a layer of trust for security. For browsers, they restrict how this header can be set (see Forbidden header name). But if you control the HTTP client (ex. curl, postman, etc.) you can easily can send whatever headers you want. There is nothing technology wise preventing me from sending any headers with whatever values I want to your web server.
Therefor, at the end of the day, it might not be a huge concern. If someone tampers with that header, they are opening themselves up to security risks and unexpected behavior. There are a ton of ways to bypass CORS, like this, or this, or this maybe. So at the end of the day, it's possible to bypass CORS, despite your best efforts to enforce it. Although all of those tricks are hacks, and probably won't be used by normal users. Same with changing the origin header, not likely to be done by normal users.
There are a few other tricks you could look into tho to try to enforce it a little bit more. You could look into the refer header, and see if that is the same as the origin header. Again, possible to send anything for any header, but will make it a bit harder and enforce what you want a little bit more.
If you assume that your origin header should always equal the domain of your API Gateway API then the other thing you can look into is the event.requestContext object that API Gateway gives you. That object has resourceId, stage, accountId, apiId, and a few other interesting properties attached to it. You could look into building a system that will also verify those and based on those values, determine which API in API Gateway is making the request. This might require ensuring that you have separated out each domain into a separate API gateway API.
I don't see anyway those values in the event.requestContext could be tampered with tho since AWS sets them before passing the event object off to you. They are derived from AWS and can not be easily tampered with by a user (unless the entire makeup of the request changes). For sure a lot less tamperproof than headers which are just sent with the request, and AWS passes through to you.
Of course you can combine multiple of those solutions together to create a solution that enforces your policy more. Remember, security is a spectrum, so how far down that spectrum you go is up to you.
I would also encourage you to remember that CORS is not totally meant to hide information on the internet. Those methods I shared about how you can bypass CORS with a simple backend system, or plugin, show that it's not completely foolproof and if someone really wants to fake headers they will be able to. But of course at the end of the day you can make it as hard as possible for that to be achieved. But that requires implementing and writing a lot of code and doing a lot of checks to make that happen.
You really have to ask yourself what the objectives and goals are. I think that really determines your next steps. You might determine that your current setup is good enough and no further changes are necessary. You might determine that you are trying to protect sensitive data from being sent to unauthorized origins, which in that case CORS probably isn't a solid solution (due to the ability to set that header to anything). Or you might determine that you might wanna lock things down a bit more and use a few other signals to enforce your policy a bit more.
tldr You can for sure set the Origin header to anything you want, therefor it should not be completely trusted. If you assume that your origin header should always equal the domain of your API Gateway API, you can try to use the event.requestContext object to get more information about the API in API Gateway to gain more information about the request. You could also look into the Refer header to see if you can compare that against the Origin header.
Further information:
Is HTTP Content-Length Header Safe to Trust? (disclaimer: I posted this question on Stack Overflow a while back)
Example Amazon API Gateway AWS Proxy Lambda Event
Bypass CORS
https://github.com/Rob--W/cors-anywhere
https://chrome.google.com/webstore/detail/allow-control-allow-origi/nlfbmbojpeacfghkpbjhddihlkkiljbi
https://www.thepolyglotdeveloper.com/2014/08/bypass-cors-errors-testing-apis-locally/
Refer Header
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referer
https://en.wikipedia.org/wiki/HTTP_referer
The only way to validate multiple origins is as you did, with your lambda read the Origin header, compare that to the list of domains you would like to allow, and if it matches, return the value of the Origin header back to the client as the Access-Control-Allow-Origin header in the response.
An information: the Origin header is one of the headers that are set automatically by the user agent.. so anyone can't altered programatically or through extensions. For more details, look at MDN.

Returning binary body and http headers from an AWS lambda through API gateway

I have a lambda that needs to return a binary object and some http headers (e.g. content-type) through an api gateway (using lambda integration) OR redirect to another URL. In the binary support examples (e.g. https://aws.amazon.com/blogs/compute/binary-support-for-api-integrations-with-amazon-api-gateway/) the lambda only returns the (base64 of the) binary object (the image). In my case, I also need to return a status code and http headers (or something equivalent). I struggle with how I can make this work with binary support in api gateway.
The lambda returns a json on this form:
{
"statusCode": 200,
"headers": {
"content-type": "image/jpeg"
},
"body": "/9j/4AAQS...gLDAoKCAwZK",
"isBase64Encoded": true
}
In the integration response I add body mappings for image/jpeg (etc) of the form:
$input.json('$.body')
And header mapping for 'content-type' like so:
integration.response.body.headers['content-type']
I've tried many variations of the above, but the result is consistently
Execution failed due to configuration error: Unable to transform response
How do I transform the json from the lambda into a form that can be converted to binary by the api gateway, with http headers and all? Can I get more debug logging out of the api gateway to show more specific what it is unhappy with?
Is there perhaps a way to get more debug logging out of the api gateway?
I recently got this working after facing a similar problem.
In my case, I was missing two things:
First, I needed to change the list of types AWS will send to the upstream in the "Accept" header"
"x-amazon-apigateway-binary-media-types" : [
"image/jpeg"
]
Secondly, I needed to set the Integration Response to "Convert to binary (if needed)":
"contentHandling": "CONVERT_TO_BINARY"
See this answer for the details, and sample config.
I also found that I wasn't being patient enough. Whenever I deployed the API, I was checking immediately, instead of waiting a few minutes for the changes to propagate.
I tired returning binary data with base64 encoding, but I couldn't manage to return it from Lambda function through API Gateway.
Therefore, I decided to redirect the URL. I changed the Method Response to 302 and added "Location" Response header. I also deleted response code 200 from Integration Response, selected 302 as response code and mapped integration.response.body.location value with Location header. My Lambda code returned location of redirect URL in this format:
{"location":"www.google.com/"}
Hope this helps.
I've been struggling to do the same for days, but couldn't find any documentation to back up that it's possible, instead I found this
https://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-payload-encodings-workflow.html
When converting a text payload to a binary blob, API Gateway assumes that the > text data is a Base64-encoded string and outputs the binary data as a
Base64-decoded blob. If the conversion fails, it returns a 500 response
indicating an API configuration error. You do not provide a mapping template
for such a conversion, although you must enable the passthrough behaviors on
the API.
When converting a binary payload to a text string, API Gateway always applies > a Base64 encoding on the binary data. You can define a mapping template for
such a payload, but can only access the Base64-encoded string in the mapping
template through $input.body, as shown in the following excerpt of an example > mapping template.
which sounds to me that mapping like this is only possible the other way around; what I ended up doing is that just return Base64Encoded binary string and hard code the content-type and cache- control in header mapping; for the complete implementation see my blog post https://mesfinmoges.com/dynamic-image-resizing-using-amazon-s3-aws-lambda-mazon-api-gateway-amazon-cloudfront/

API Gateway - Get StatusCode

This should have been such a simple issue and I don't understand why it hasn't come up through all my searching (maybe it's just been a long day).
I have an API Gateway API setup, and I am adding a Body Mapping Template to my Integration Response for a 400* error group: see image -
All I would like to get is the StatusCode of the current response (as this is a 400* group - e.g. 401 / 403 / 404 etc.)
The closest I came was through this site: AWS help documentation and I thought I would be able to use something like $context.statusCode - but no luck.
Am I going crazy, or is this just not something required often?
PS - Making changes to any Lambda functions being called, is not an option.
Thanks
There's currently no mapping template variable in API Gateway dedicated to the integration response status code.
We will certainly add this as a feature request.
At current time you are limited to hardcoding the status code value in your response templates. You would either need to define generic status codes (i.e "4XX") or define integration responses for every status code you want to capture. While this seems tedious, this could be managed relatively easily in a Swagger template.
At current time the only way to see the integration response status code is via CloudWatch Logs.
Thanks,
Ryan / Amazon API Gateway
If you are sending error codes from your server then you can easily map them.
I have done something similar but I have used different trick to do. I used to send my own error entities and codes from server.
You have to map those error entities and error codes coming from server to the response that comes from amazon servers. I will try and explain what I mean by this. Api Gateway doesn't send response coming from your own server to the client automatically. You have to map those responses. For example, map 200 as a SUCESS and response entity will be default, that is whatever coming from server.
Now, we default success response is managed but what about error codes and error entities. You have to map them manually.
There are two ways you can do this,
One is manual, go to your api. Create error entities or models. Map them manually for each response code.
This one uses swaggger,
Solution is to import swagger specification of error entities. Add response templates to the swagger specification and let amazon do their job.
I can help you more with swagger. It depends how you are setting up your api on amazon.
Visit this for amazon extenstions to swagger,
http://docs.aws.amazon.com/apigateway/latest/developerguide/api-gateway-swagger-extensions.html#api-gateway-swagger-extensions-integration-response

Can you statically set a Header and it's value when setting up an HTTP Proxy using AWS Api Gateway endpoint?

I am creating an http proxy using AWS Api Gateway. I would like to hard code some of the headers and their values to be forwarded as part of the request. I thought this might be possible in the 'Integration Request' portion of the proxy setup, but I can't seem to figure it out.
I'm trying to pass an Authorization header with an oauth key. I don't want to share this key with clients that have access to this service, since I will only provide a subset of access to users of this specific endpoint.
In the Integration Request, you can configure a static header value to be sent to the integration endpoint by putting the value inside of single quotes, e.g. 'my_static_header_value'.
Is it a problem to put those hardcoded headers in the request body ? It not, you could just use a template (in the integration request screen) :
{
"hardcoded_header": "$input.params('hardcoded_header')"
}
Hope this helps.