Canvas tainted by CORS data and S3 - image

My application is displaying images stored in AWS S3 (in a private bucket for security reasons).
To allow users to see the images from their browser I generate signed URLs like https://s3.eu-central-1.amazonaws.com/my.bucket/stuff/images/image.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=...&X-Amz-Date=20170701T195504Z&X-Amz-Expires=900&X-Amz-Signature=bbe277...3358e8&X-Amz-SignedHeaders=host.
This is working perfectly with <img src="S3URL" />: the images are correctly displayed.
I can even directly view the images in another tab by copy/pasting their URL.
I'm also generating PDFs embedding these images which need to be transformed before with a canvas: resized and watermarked.
But the library I use for resizing is having some troubles:
Failed to execute 'getImageData' on 'CanvasRenderingContext2D':
The canvas has been tainted by cross-origin data.
Indeed we are in a CORS context but I've setup everything so that the images can be displayed to the user and indeed it's working.
So I'm not sure to understand the reason of this error: is this another CORS security layer: the browser fears that I might change the image in a malicious purpose?
I've tried to set a permissive CORS configuration on the S3 bucket:
<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
<AllowedOrigin>*</AllowedOrigin>
<AllowedMethod>GET</AllowedMethod>
<AllowedMethod>POST</AllowedMethod>
<AllowedMethod>PUT</AllowedMethod>
<MaxAgeSeconds>3000</MaxAgeSeconds>
<AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>
And img.crossOrigin = "" or img.crossOrigin = "Anonymous" on the client-side but then I get:
Access to Image at 'https://s3.eu-central-1.amazonaws.com/...'
from origin 'http://localhost:5000' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://localhost:5000' is therefore not allowed access.
Which AWS/S3-side and/or client-side configuration could be missing?

One workaround here is to prevent the browser from caching the downloaded object. This seems to stem from arguably incorrect behavior on the part of S3 that interacts with the way Chrome handles cached objects. I recently answered a similar question on Server Fault, and you can find additional details, there.
The problem seems to arise when you fetch an object from S3 from simple HTML (like an <img> tag) and then fetch the same object again in a cross-origin context.
Chrome caches the result of the first request, and then uses that cached response instead of making a new request the second time. When it examines the cached object, there's no Access-Control-Allow-Origin header, because it was cached from a request that wasn't subject to CORS rules... so when that first request was made, the browser didn't send an Origin header. Because of that, S3 didn't respond with an Access-Control-Allow-Origin header (or any CORS-related headers).
The root of the problem seems related to the HTTP Vary: response header, which is related to caching.
A web server (S3 in this case) can use the Vary: response header to signal to the browser that the server is capable of producing more than one representation of the object being returned -- and if the browser would vary an attribute of the request, the response might differ. When the browser is considering using a cached object, it should check whether the object is valid in the new context, before concluding that the cached object is suited to the current need.
Indeed, when you send an Origin request header to S3, you get a response that includes Vary: Origin. This tells the browser that if the origin sent in the request had been a different value, the response might also have been different -- for example, because not all origins might be allowed.
The first part of the underlying problem is that S3 -- arguably -- should always return Vary: Origin whenever CORS is configured on the bucket, even if the browser didn't send an origin header, because Vary can be specified against a header you didn't actually include in the request, to tell you that if you had included it, the response might have differed. But, it doesn't do that, when Origin isn't present.
The second part of the problem is that Chrome -- when it consults its internal cache -- sees that it already has a copy of the object. The response that seeded the cache did not include Vary, so Chrome assumes this object is also perfectly valid for the CORS request. Clearly, it isn't, since when Chrome tries to use the object, it finds that the cross-origin response headers are missing. Presumably, had Chrome received a Vary: Origin response from S3 on the original request, it would have realized that its provisional request headers for the second request included Origin:, so it would correctly go and fetch a different copy of the object. If it did that, the problem would go away -- as we have illustrated by setting Cache-Control: no-cache on the object, preventing Chrome from caching it. But, it doesn't.
So, we work around this by setting Cache-Control: no-cache on the object in S3, so that Chrome won't cache the first one, and will make the correct CORS request for the second one, instead of trying to use the cached copy, which will fail.
Note that if you want to avoid updating your objects in S3 to include the Cache-Control: no-cache response, there is another option for solving this without actually adding the header to the objects at rest in S3. Actually, there are two more options:
The S3 API respects a value passed in the query string of response-cache-control=no-cache. Adding this to the signed URL will direct S3 to add the header to the response, regardless of the Cache-Control metadata value stored with the object (or lack thereof). You can't simply append this to the query string -- you have to add it as part of the URL signing process. But once you add that to your code, your objects will be returned with Cache-Control: no-cache in the response headers.
Or, if you can generate these two signed URLs for the same object separately when rendering the page, simply change the expiration time on one of the signed URLs, relative to the other. Make it one minute longer, or something along those lines. Changing the expiration time from one to the other will force the two signed URLs to be different, and two different objects with two different query strings should be interpreted by Chrome as two separate objects, which should also eliminate the incorrect usage of the first cached object to serve the other request.

I already have allow any origin on S3 even though I am only fetching from exactly one origin, yet this problem continued so I don't see how this can actually be a CORS problem. As others have said, it is mostly a browser bug. The problem appears if you have to fetch the image from an tag AND at any point later also do a javascript fetch for it...you'll get the cache bug. The easiest solution for me was to put query parameters at the end of the URL that would be used by javascript.
https://something.s3.amazonaws.com/something.jpg?stopGivingMeHeadaches=true

Related

CloudFront / S3 ETag: Possible for CloudFront to send updated S3 Object before the CF TTL has expired?

I have a question in regard to how CloudFront will use an S3 object's ETag to determine if it needs to send a refreshed object or not.
I know that the ETag will be part of the Request to the CloudFront distribution, in my case I'm seeing the "weak" (shortened) version:
if-none-match: W/"eabcdef4036c3b4f8fbf1e8aa81502542"
If this ETag being sent does not match the S3 Object's current ETag value, then the CloudFront will send the latest version.
I'm seeing this work as expected, but only after the CloudFront's cache policy has been reached. In my case it's been set to 20 mins.
CloudFront with a Cache Policy:
Minimum TTL: 1
Maximum TTL: 1200 <-- (20 mins)
Default TTL: 900
Origin Request Policy is not set
S3 Bucket:
Set to only allow access via its corresponding CloudFront
distribution above.
Bucket and objects not public
The test object (index.html) in this case has only one header set:
Content-Type = text/html
While I am using the CloudFront's Cache Policy, I've also tested
using the S3 Object header of Cache-Control = max-age=6000
This had no affect on the refresh of the "index.html" object in
regard to the ETag check I'm asking about.
The Scenario:
Upon first "putObject" to that S3 bucket, the "index.html" file has an ETag of:
eabcdef4036c3b4f8fbf1e8aa81502542
When I hit the URL (GET) for that "index.html" file, the cache of 20 mins is effectively started.
Subsequent hits to the "index.html" URL (GET) has the Request with the value
if-none-match: W/"eabcdef4036c3b4f8fbf1e8aa81502542"
I also see "x-cache: Hit from cloudfront" in the Response coming back.
Before the 20 mins is up, I'll make a change to the "index.html" file and re-upload via a "putObject" command in my code.
That will then change the ETag to:
exyzcde4099c3b4f8fuy1e8aa81501122
I would expect then that the next Request to CloudFront, before the 20-minute TTL and with the old "if-none-match" value, would then prompt the CloudFront to see the ETag is different and send the latest version.
But in all cases/tests it doesn't. CloudFront will seem to ignore the ETag difference and continue to send the older "index.html" version.
It's only after the 20 mins (cache TTL) is up that the CloudFront sends the latest version.
At that time the ETag in the Request changes/updates as well:
if-none-match: W/"exyzcde4099c3b4f8fuy1e8aa81501122"
Question (finally, huh?):
Is there a way to configure CloudFront to listen to the incoming ETag, and if needed, send the latest Object without having to wait for the Cache Policy TTL to expire?
UPDATE:
Kevin Henry's response explains it well:
"CloudFront doesn't know that you updated S3. You told it not to check with the origin until the TTL has expired. So it's just serving the old file until the TTL has expired and it sees the new one that you uploaded to S3. (Note that this doesn't have anything to do with ETags)."
So I decided to test how the ETag would be used if I turned the CloudFront Caching Policy to a TTL of 0 for all three CloudFront settings. I know that this defeats the purpose, and one of the strengths, of CloudFront, but I'm still wrapping my head around certain key aspects of CDN caching.
After setting the cache to 0, I'm seeing a continual "Miss from CloudFront" in the Response coming back.
I expected this, and in the first response I see a HTTP status of 200. Note the file size being returned is 128KB for this test.
Subsequent calls to this same file return a HTTP status of 304, with a file size being returned around 400B.
As soon as I update the "index.html" file in the S3 bucket, and call that same URL, the status code is 200 with a file size of 128KB.
Subsequent calls return a status of 304, again with an average of 400B in file size.
Looking again at the definition of an HTTP status of 304:
https://httpstatuses.com/304
"A conditional GET or HEAD request has been received and would have resulted in a 200 OK response if it were not for the fact that the condition evaluated to false.
In other words, there is no need for the server to transfer a representation of the target resource because the request indicates that the client, which made the request conditional, already has a valid representation; the server is therefore redirecting the client to make use of that stored representation as if it were the payload of a 200 OK response."
So am I correct in thinking that I'm using the Browser's cache at this point?
The calls to the CloudFront will now pass the requests to the Origin, where the ETag is used to verify if the resource has changed.
As it hasn't, then a 304 is returned and the Browser kicks in and returns its stored version of "index.html".
Would this be a correct assumption?
In case you're wondering, I can't use the invalidation method for clearing cache, as my site could expect several thousand invalidations a day. I'm hosting a writing journal site, where the authors could update their files daily, therefore producing new versions of their work on S3.
I would also rather not use the versioning method, with a timestamp or other string added as a query to the page URL. SEO reasons for this one mainly.
My ideal scenario would be to serve the same version of the author's work until they've updated it, at which time the next call to that same page would show its latest version.
This research/exercise is helping me to learn and weigh my options.
Thanks again for the help/input.
Jon
"I would expect then that the next Request to CloudFront, before the 20-minute TTL and with the old if-none-match value, would then prompt the CloudFront to see the ETag is different and send the latest version."
That is a mistaken assumption. CloudFront doesn't know that you updated S3. You told it not to check with the origin until the TTL has expired. So it's just serving the old file until the TTL has expired and it sees the new one that you uploaded to S3. (Note that this doesn't have anything to do with ETags).
CloudFront does offer ways to invalidate the cache, and you can read more about how to combine that with S3 updates in these answers.
We can enable bucket versioning and object with new etag is picked up by the cloudfront

API instagram can't get data [duplicate]

tl;dr; About the Same Origin Policy
I have a Grunt process which initiates an instance of express.js server. This was working absolutely fine up until just now when it started serving a blank page with the following appearing in the error log in the developer's console in Chrome (latest version):
XMLHttpRequest cannot load https://www.example.com/
No 'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:4300' is therefore not allowed access.
What is stopping me from accessing the page?
tl;dr — When you want to read data, (mostly) using client-side JS, from a different server you need the server with the data to grant explicit permission to the code that wants the data.
There's a summary at the end and headings in the answer to make it easier to find the relevant parts. Reading everything is recommended though as it provides useful background for understanding the why that makes seeing how the how applies in different circumstances easier.
About the Same Origin Policy
This is the Same Origin Policy. It is a security feature implemented by browsers.
Your particular case is showing how it is implemented for XMLHttpRequest (and you'll get identical results if you were to use fetch), but it also applies to other things (such as images loaded onto a <canvas> or documents loaded into an <iframe>), just with slightly different implementations.
The standard scenario that demonstrates the need for the SOP can be demonstrated with three characters:
Alice is a person with a web browser
Bob runs a website (https://www.example.com/ in your example)
Mallory runs a website (http://localhost:4300 in your example)
Alice is logged into Bob's site and has some confidential data there. Perhaps it is a company intranet (accessible only to browsers on the LAN), or her online banking (accessible only with a cookie you get after entering a username and password).
Alice visits Mallory's website which has some JavaScript that causes Alice's browser to make an HTTP request to Bob's website (from her IP address with her cookies, etc). This could be as simple as using XMLHttpRequest and reading the responseText.
The browser's Same Origin Policy prevents that JavaScript from reading the data returned by Bob's website (which Bob and Alice don't want Mallory to access). (Note that you can, for example, display an image using an <img> element across origins because the content of the image is not exposed to JavaScript (or Mallory) … unless you throw canvas into the mix in which case you will generate a same-origin violation error).
Why the Same Origin Policy applies when you don't think it should
For any given URL it is possible that the SOP is not needed. A couple of common scenarios where this is the case are:
Alice, Bob, and Mallory are the same person.
Bob is providing entirely public information
… but the browser has no way of knowing if either of the above is true, so trust is not automatic and the SOP is applied. Permission has to be granted explicitly before the browser will give the data it has received from Bob to some other website.
Why the Same Origin Policy applies to JavaScript in a web page but little else
Outside the web page
Browser extensions*, the Network tab in browser developer tools, and applications like Postman are installed software. They aren't passing data from one website to the JavaScript belonging to a different website just because you visited that different website. Installing software usually takes a more conscious choice.
There isn't a third party (Mallory) who is considered a risk.
* Browser extensions do need to be written carefully to avoid cross-origin issues. See the Chrome documentation for example.
Inside the webpage
Most of the time, there isn't a great deal of information leakage when just showing something on a webpage.
If you use an <img> element to load an image, then it gets shown on the page, but very little information is exposed to Mallory. JavaScript can't read the image (unless you use a crossOrigin attribute to explicitly enable request permission with CORS) and then copy it to her server.
That said, some information does leak so, to quote Domenic Denicola (of Google):
The web's fundamental security model is the same origin policy. We
have several legacy exceptions to that rule from before that security
model was in place, with script tags being one of the most egregious
and most dangerous. (See the various "JSONP" attacks.)
Many years ago, perhaps with the introduction of XHR or web fonts (I
can't recall precisely), we drew a line in the sand, and said no new
web platform features would break the same origin policy. The existing
features need to be grandfathered in and subject to carefully-honed
and oft-exploited exceptions, for the sake of not breaking the web,
but we certainly can't add any more holes to our security policy.
This is why you need CORS permission to load fonts across origins.
Why you can display data on the page without reading it with JS
There are a number of circumstances where Mallory's site can cause a browser to fetch data from a third party and display it (e.g. by adding an <img> element to display an image). It isn't possible for Mallory's JavaScript to read the data in that resource though, only Alice's browser and Bob's server can do that, so it is still secure.
CORS
The Access-Control-Allow-Origin HTTP response header referred to in the error message is part of the CORS standard which allows Bob to explicitly grant permission to Mallory's site to access the data via Alice's browser.
A basic implementation would just include:
Access-Control-Allow-Origin: *
… in the response headers to permit any website to read the data.
Access-Control-Allow-Origin: http://example.com
… would allow only a specific site to access it, and Bob can dynamically generate that based on the Origin request header to permit multiple, but not all, sites to access it.
The specifics of how Bob sets that response header depend on Bob's HTTP server and/or server-side programming language. Users of Node.js/Express.js should use the well-documented CORS middleware. Users of other platforms should take a look at this collection of guides for various common configurations that might help.
NB: Some requests are complex and send a preflight OPTIONS request that the server will have to respond to before the browser will send the GET/POST/PUT/Whatever request that the JS wants to make. Implementations of CORS that only add Access-Control-Allow-Origin to specific URLs often get tripped up by this.
Obviously granting permission via CORS is something Bob would only do only if either:
The data was not private or
Mallory was trusted
How do I add these headers?
It depends on your server-side environment.
If you can, use a library designed to handle CORS as they will present you with simple options instead of having to deal with everything manually.
Enable-Cors.org has a list of documentation for specific platforms and frameworks that you might find useful.
But I'm not Bob!
There is no standard mechanism for Mallory to add this header because it has to come from Bob's website, which she does not control.
If Bob is running a public API then there might be a mechanism to turn on CORS (perhaps by formatting the request in a certain way, or a config option after logging into a Developer Portal site for Bob's site). This will have to be a mechanism implemented by Bob though. Mallory could read the documentation on Bob's site to see if something is available, or she could talk to Bob and ask him to implement CORS.
Error messages which mention "Response for preflight"
Some cross-origin requests are preflighted.
This happens when (roughly speaking) you try to make a cross-origin request that:
Includes credentials like cookies
Couldn't be generated with a regular HTML form (e.g. has custom headers or a Content-Type that you couldn't use in a form's enctype).
If you are correctly doing something that needs a preflight
In these cases then the rest of this answer still applies but you also need to make sure that the server can listen for the preflight request (which will be OPTIONS (and not GET, POST, or whatever you were trying to send) and respond to it with the right Access-Control-Allow-Origin header but also Access-Control-Allow-Methods and Access-Control-Allow-Headers to allow your specific HTTP methods or headers.
If you are triggering a preflight by mistake
Sometimes people make mistakes when trying to construct Ajax requests, and sometimes these trigger the need for a preflight. If the API is designed to allow cross-origin requests but doesn't require anything that would need a preflight, then this can break access.
Common mistakes that trigger this include:
trying to put Access-Control-Allow-Origin and other CORS response headers on the request. These don't belong on the request, don't do anything helpful (what would be the point of a permissions system where you could grant yourself permission?), and must appear only on the response.
trying to put a Content-Type: application/json header on a GET request that has no request body the content of which to describe (typically when the author confuses Content-Type and Accept).
In either of these cases, removing the extra request header will often be enough to avoid the need for a preflight (which will solve the problem when communicating with APIs that support simple requests but not preflighted requests).
Opaque responses (no-cors mode)
Sometimes you need to make an HTTP request, but you don't need to read the response. e.g. if you are posting a log message to the server for recording.
If you are using the fetch API (rather than XMLHttpRequest), then you can configure it to not try to use CORS.
Note that this won't let you do anything that you require CORS to do. You will not be able to read the response. You will not be able to make a request that requires a preflight.
It will let you make a simple request, not see the response, and not fill the Developer Console with error messages.
How to do it is explained by the Chrome error message given when you make a request using fetch and don't get permission to view the response with CORS:
Access to fetch at 'https://example.com/' from origin 'https://example.net' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
Thus:
fetch("http://example.com", { mode: "no-cors" });
Alternatives to CORS
JSONP
Bob could also provide the data using a hack like JSONP which is how people did cross-origin Ajax before CORS came along.
It works by presenting the data in the form of a JavaScript program that injects the data into Mallory's page.
It requires that Mallory trust Bob not to provide malicious code.
Note the common theme: The site providing the data has to tell the browser that it is OK for a third-party site to access the data it is sending to the browser.
Since JSONP works by appending a <script> element to load the data in the form of a JavaScript program that calls a function already in the page, attempting to use the JSONP technique on a URL that returns JSON will fail — typically with a CORB error — because JSON is not JavaScript.
Move the two resources to a single Origin
If the HTML document the JS runs in and the URL being requested are on the same origin (sharing the same scheme, hostname, and port) then the Same Origin Policy grants permission by default. CORS is not needed.
A Proxy
Mallory could use server-side code to fetch the data (which she could then pass from her server to Alice's browser through HTTP as usual).
It will either:
add CORS headers
convert the response to JSONP
exist on the same origin as the HTML document
That server-side code could be written & hosted by a third party (such as CORS Anywhere). Note the privacy implications of this: The third party can monitor who proxies what across their servers.
Bob wouldn't need to grant any permissions for that to happen.
There are no security implications here since that is just between Mallory and Bob. There is no way for Bob to think that Mallory is Alice and to provide Mallory with data that should be kept confidential between Alice and Bob.
Consequently, Mallory can only use this technique to read public data.
Do note, however, that taking content from someone else's website and displaying it on your own might be a violation of copyright and open you up to legal action.
Writing something other than a web app
As noted in the section "Why the Same Origin Policy only applies to JavaScript in a web page", you can avoid the SOP by not writing JavaScript in a webpage.
That doesn't mean you can't continue to use JavaScript and HTML, but you could distribute it using some other mechanism, such as Node-WebKit or PhoneGap.
Browser extensions
It is possible for a browser extension to inject the CORS headers in the response before the Same Origin Policy is applied.
These can be useful for development but are not practical for a production site (asking every user of your site to install a browser extension that disables a security feature of their browser is unreasonable).
They also tend to work only with simple requests (failing when handling preflight OPTIONS requests).
Having a proper development environment with a local development server
is usually a better approach.
Other security risks
Note that SOP / CORS do not mitigate XSS, CSRF, or SQL Injection attacks which need to be handled independently.
Summary
There is nothing you can do in your client-side code that will enable CORS access to someone else's server.
If you control the server the request is being made to: Add CORS permissions to it.
If you are friendly with the person who controls it: Get them to add CORS permissions to it.
If it is a public service:
Read their API documentation to see what they say about accessing it with client-side JavaScript:
They might tell you to use specific URLs
They might support JSONP
They might not support cross-origin access from client-side code at all (this might be a deliberate decision on security grounds, especially if you have to pass a personalized API Key in each request).
Make sure you aren't triggering a preflight request you don't need. The API might grant permission for simple requests but not preflighted requests.
If none of the above apply: Get the browser to talk to your server instead, and then have your server fetch the data from the other server and pass it on. (There are also third-party hosted services that attach CORS headers to publically accessible resources that you could use).
Target server must allowed cross-origin request. In order to allow it through express, simply handle http options request :
app.options('/url...', function(req, res, next){
res.header('Access-Control-Allow-Origin', "*");
res.header('Access-Control-Allow-Methods', 'POST');
res.header("Access-Control-Allow-Headers", "accept, content-type");
res.header("Access-Control-Max-Age", "1728000");
return res.sendStatus(200);
});
As this isn't mentioned in the accepted answer.
This is not the case for this exact question, but might help others that search for that problem
This is something you can do in your client-code to prevent CORS errors in some cases.
You can make use of Simple Requests.
In order to perform a 'Simple Requests' the request needs to meet several conditions. E.g. only allowing POST, GET and HEAD method, as well as only allowing some given Headers (you can find all conditions here).
If your client code does not explicit set affected Headers (e.g. "Accept") with a fix value in the request it might occur that some clients do set these Headers automatically with some "non-standard" values causing the server to not accept it as Simple Request - which will give you a CORS error.
This is happening because of the CORS error. CORS stands for Cross Origin Resource Sharing. In simple words, this error occurs when we try to access a domain/resource from another domain.
Read More about it here: CORS error with jquery
To fix this, if you have access to the other domain, you will have to allow Access-Control-Allow-Origin in the server. This can be added in the headers. You can enable this for all the requests/domains or a specific domain.
How to get a cross-origin resource sharing (CORS) post request working
These links may help
This CORS issue wasn't further elaborated (for other causes).
I'm having this issue currently under different reason.
My front end is returning 'Access-Control-Allow-Origin' header error as well.
Just that I've pointed the wrong URL so this header wasn't reflected properly (in which i kept presume it did). localhost (front end) -> call to non secured http (supposed to be https), make sure the API end point from front end is pointing to the correct protocol.
I got the same error in Chrome console.
My problem was, I was trying to go to the site using http:// instead of https://. So there was nothing to fix, just had to go to the same site using https.
This bug cost me 2 days. I checked my Server log, the Preflight Option request/response between browser Chrome/Edge and Server was ok. The main reason is that GET/POST/PUT/DELETE server response for XHTMLRequest must also have the following header:
access-control-allow-origin: origin
"origin" is in the request header (Browser will add it to request for you). for example:
Origin: http://localhost:4221
you can add response header like the following to accept for all:
access-control-allow-origin: *
or response header for a specific request like:
access-control-allow-origin: http://localhost:4221
The message in browsers is not clear to understand: "...The requested resource"
note that:
CORS works well for localhost. different port means different Domain.
if you get error message, check the CORS config on the server side.
In most housing services just add in the .htaccess on the target server folder this:
Header set Access-Control-Allow-Origin 'https://your.site.folder'
I had the same issue. In my case i fixed it by adding addition parameter of timestamp to my URL. Even this was not required by the server I was accessing.
Example yoururl.com/yourdocument?timestamp=1234567
Note: I used epos timestamp
"Get" request with appending headers transform to "Options" request. So Cors policy problems occur. You have to implement "Options" request to your server. Cors Policy about server side and you need to allow Cors Policy on your server side. For Nodejs server:details
app.use(cors)
For Java to integrate with Angular:details
#CrossOrigin(origins = "http://localhost:4200")
You should enable CORS to get it working.

Single Page Application with Lambda#Edge

So I have a SPA served from AWS S3 by AWS Cloudfront. I have configured the following Error Pages behaviour:
404: Not Found -> /index.html with HTTP code 200
This is needed to be able to handle routing on the client-side.
Now I've got a Lambda#Edge function which is triggered by the viewer-response event in Cloudfront and sets some custom headers like HSTS and X-Frame. The function is being invoked and works as intended on all the resources besides the actual /index.html. I'm inclined to think that this is because it's being handled by the aforementioned error pages behaviour in Cloudfront, as the actual GET request for the html is being handled by the error pages configuration in Cloudfront.
What would be a practical approach on solving this?
I'm not sure why the redirect doesn't trigger the lambda function. Is there any way to implement the same logic as the error pages configuration in lambda#edge?
update: The behavior of the service has changed.
https://aws.amazon.com/about-aws/whats-new/2017/12/lambda-at-edge-now-allows-you-to-customize-error-responses-from-your-origin/
The answer, below, was correct at the time it was posted, but is no longer applicable. Origin errors now trigger the Lambda#Edge function as expected in Origin Response triggers (but not Viewer Response triggers).
Note, that you can generate a custom response body in an Origin Response trigger, but you don't have programmatic access to read the original response body returned from the origin, if there is one. You can replace it, or leave it as it is -- whatever it is. This is because Lambda#Edge Origin Response triggers do not wait to fire after CloudFront receives the entire response from the origin -- they appear to fire as soon as the origin finishes returning complete, valid response headers back to CloudFront.
When you’re working with the HTTP response, note that Lambda#Edge does not expose the HTML body that is returned by the origin server to the origin-response trigger. You can generate a static content body by setting it to the desired value, or remove the body inside the function by setting the value to be empty. If you don’t update the body field in your function, the original body returned by the origin server is returned back to viewer.
https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-updating-http-responses.html
Important reminders: Any time you are testing changes on CloudFront, remember that your changes tend to start working sooner than you would expect -- before the distribution state changes back to Deployed, but you may need to do a cache invalidation to make your changes fully live and visible. Invalidations should include the path actually requested by the browser, not the path being requested from the origin (if different), or /* to invalidate everything. When viewing a response from CloudFront, if there is an Age: response header, you are viewing a cached response. Remember, also, that errors use a different set of timers for caching responses. These are configured separately from the TTL values in the cache behavior. See my answer to Amazon CloudFront Latency for an explanation of how to change the Error Caching Minimum TTL, which defaults to 5 minutes and does not generally respect Cache-Control headers. This is a protective measure to prevent excessive errors from reaching your origin (or triggering your Lambda functions) but is confusing during testing and troubleshooting if you aren't aware of its impact.
(original answer follows)
CloudFront doesn't execute Lambda functions for origin response and viewer response events if the origin returns HTTP status code 400 or higher.
http://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/lambda-requirements-limits.html
What this means is that an unhandled error results in no response triggers firing.
However, when an origin error is handled by a custom error response document, the origin triggers do fire on the fallback request, including Origin Response if the error document renders successfully, and here is where you'll find your solution.
You code will run if you implement it as an Origin Response trigger instead of a Viewer Response trigger, because when fetching /index.html (the substitute error page) the origin returns 200, which invokes the Origin Response trigger -- but the Viewer Response trigger still doesn't fire. This behavior does not appear to be fully documented but testing reveals Origin Request and Response triggers firing separately on successful error document fetches, as long as the Cache Behavior with the path that matches the error document is configured with the triggers.
In fact, it seems like an Origin Response trigger makes more sense for your application anyway, because it will be able to modify the response before it goes into the cache, and the added headers will be cached along with the response -- which should result in an overall reduction in the number of times the trigger actually needs to fire.
You can add it as an Origin Response trigger, wait for the distribution to return to Deployed, then do a cache invalidation for /* (so that you don't serve any pages that were cached without the headers added), and after the invalidation is complete, remove the Viewer Response trigger.
Aside: I submitted a feature request to support firing response triggers on errors but I don't know whether this is something they are considering adding, or not and apparently I wasn't the only one, since the feature was implemented and released, as described in the revised answer.

What are the security benefits of CORS preflight requests?

I've been working on a classic SPA where the front end app lives on app.example.com while the API lives on api.example.com, hence requiring the use of CORS requests. Have setup the server to return the CORS header, works fine.
Whenever an AJAX request is not simple, the browser makes an extra OPTIONS request to the server to determine if it can make the call with the payload. Find Simple Requests on MDN
The question is: What are the actual benefits of doing the OPTIONS request, especially in regards to security?
Some users of my app have significant geographical latency and since the preflight cache doesn't last long, the preflight requests cause latencies to be multiplied.
I'm hoping to make POST requests simple, but just embedding the Content-Type of application/json negates that. One potential solution is to "hack" it by using text/plain or encoding in the url. Hence, I hope to leave with a full understanding of what CORS preflight requests do for web security. Thanks.
As noted on the article you linked to:
These are the same kinds of cross-site requests that web content can
already issue, and no response data is released to the requester
unless the server sends an appropriate header. Therefore, sites that
prevent cross-site request forgery have nothing new to fear from HTTP
access control.
Basically it was done to make sure CORS does not introduce any extra means for cross-domain requests to be made that would otherwise be blocked without CORS.
For example, without CORS, the following form content types could only be done cross-domain via an actual <form> tag, and not by an AJAX request:
application/x-www-form-urlencoded
multipart/form-data
text/plain
Therefore any server receiving a request with one of the above content-types knows that there is a possibility of it coming from another domain and knows to take measures against attacks such as Cross Site Request Forgery. Other content types such as application/json could previously only be made from the same domain, therefore no extra protection was necessary.
Similarly requests with extra headers (e.g. X-Requested-With) would have previously been similarly protected as they could have only come from the same domain (a <form> tag cannot add extra headers, which was the only way previously to do a cross-domain POST). GET and POST are also the only methods supported by a form. HEAD is also listed here as it performs identically to GET, but without the message body being retrieved.
So, in a nutshell it will stop a "non simple" request from being made in the first place, without OPTIONS being invoked to ensure that both client and server are talking the CORS language. Remember that the Same Origin Policy only prevents reads from different origins, so the preflight mechanism is still needed to prevent writes from taking place - i.e. unsafe methods from being executed in a CSRF scenario.
You might be able to increase performance using the Access-Control-Max-Age header. Details here.

No expires header sent, content cached, how long until browser makes conditional GET request?

Assume browser default settings, and content is sent without expires headers.
user visits website, browser caches images etc.
user does not close browser, or refresh page.
user continues to surf site normally.
assume the browse doesn't dump the cache for any reason.
The browser will cache images etc as the user surfs, but it's unclear when it will issue a conditional GET request to ask about content freshness (apart from refreshing the page). If this is a browser specific setting, where can I see it's value (for browsers like: safari, IE, FireFox, Chrome).
[edit: yes - I understand that you should always send expires headers. However, this research is aimed at understanding how the browser works with content w/o expires headers.]
From the the HTTP caching spec (section 13.4): Unless specifically constrained by a cache-control (section 14.9) directive, a caching system MAY always store a successful response (see section 13.8) as a cache entry, MAY return it without validation if it is fresh, and MAY return it after successful validation. This means that a user agent is free to do whatever it wants if no cache control header is sent. Most browsers use a combination of user settings and heuristics to determine whether (and how long) to cache in this situation.
HTTP/1.1 defines a selection of caching mechanisms; the expires header is merely one, there is also the cache-control header.
To directly answer your question: for a resource returned with no expires header, you must consider the returned cache-control directives.
HTTP/1.1 defines no caching behaviour for a resource served with no cache-related headers. If a resource is sent with no cache-control or expires headers you must assume the client will make a regular (non-conditional) request the next time the same resources is requested.
Any deviation from this behaviour qualifies the client as being not a fully conformant HTTP client, in which case the question becomes: what behaviour is to be expected from a non-conformant HTTP client? There is no way to answer that.
HTTP caching is complex, to fully understand what a conformant client should do in a given scenario, read and understand the HTTP caching spec.
Unless you send an expires header, most browsers will make a GET request for each subsequent refresh and will either get HTTP 200 OK (it will download the content again) or HTTP 304 Not Modified (and use the data in cache).

Resources