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?
Cross-Origin Resource Sharing (CORS) errors occur when a server doesn't return the HTTP headers required by the CORS standard. To resolve a CORS error from an API Gateway REST API or HTTP API, you must reconfigure the API to meet the CORS standard.
In order to fix CORS, you need to make sure that the API is sending proper headers (Access-Control-Allow-*). That's why it's not something you can fix in the UI, and that's why it only causes an issue in the browser and not via curl: because it's the browser that checks and eventually blocks the calls.
The solution is to pass the request Origin to Access-Control-Allow-Origin. Since I am using AWS Lambda functions with my API Gateway, I use the Lambda function to read the Origin, filter it and return it to API Gateway. First I setup API Gateway to respond with the required headers. Under Method Response add a 200 response.
If you are using proxy integration in API Gateway, then enabling CORS from API Gateway doesn't work. You have to set the Header 'Access-Control-Allow-Origin' from your Lambda code itself.
Since I am using AWS Lambda functions with my API Gateway, I use the Lambda function to read the Origin, filter it and return it to API Gateway. First I setup API Gateway to respond with the required headers. Under Method Response add a 200 response. Add the CORS headers to the 200 response.
Note: When you select these default options, API Gateway responds with the required CORS headers, even when a request doesn't reach the endpoint. For example, if a request includes an incorrect resource path, API Gateway still responds with a 403 "Missing Authentication Token" error.
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:
Refer
Header
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With