I was reading the spec on CORS requests, and I found this about preflight requests:
These are requests to a non same origin URL with an HTTP request
method other than GET that first need to be authorized using either a
preflight result cache entry or a preflight request.
I had thought the purpose of preflight requests was to check whether a request was allowed before making it, in case it (illegitimately) changed server state.
But HEAD and OPTIONS don't modify server state. I must misunderstand the reason for preflight check.
What is the purpose (aka the reason, motivation, or rationale) for doing a preflight check for HEAD and OPTIONS but not GET? What is special about GET?
The primary intent of preflighting is to ensure that servers aren't suddenly sent cross-origin browser-based requests that they could have never received before the CORS spec was implemented.
Before the CORS spec, it was impossible to send any browser-based cross-origin requests other than GET or POST. The browser simply would not allow you to fire up an XHR instance, set the method to PUT (for example) and send it off to an endpoint on a different origin. You couldn't send GET or POST cross-origin requests via XHR either, but you COULD send a cross-origin GET or POST via a form submit, or a cross-origin GET via an <img> or <script> tag, for example (which made JSONP the only option pre-CORS). Once browsers implemented the CORS spec, this changed. Now it IS possible to send any cross-origin ajax request, provided the server opts-in.
The CORS spec defines "simple" methods (GET and POST) along with "simple" request headers. These correspond to the types of cross-origin requests that you could already send from the browser pre-CORS spec. Non-simple cross-origin requests, such as PUT or POST/GET requests with an X-header (for example) could not be sent from a browser pre-CORS spec. So, for these types of requests, the concept of preflighting was written into the spec to ensure servers do not receive these types of non-simple cross-origin browser-based requests without explicitly opting in. In other words, if you don't want to allow these types of requests, you don't have to change your server at all. The preflight will fail, and the browser will never send the underlying request.
Directly addressing your question: HEAD requests do not normally result in a preflight. HEAD is considered a simple request method according to the CORS spec. As you know, HEAD requests are just GETs without a response payload. This is the most likely reason why HEAD and GET requests are treated the same, even though you could not send a cross-origin HEAD request pre-CORS from the browser. If your HEAD contains non-simple headers, it will be preflighted though, just like GET.
Related
I've read an article which used Cors-Anywhere to make an example url request, and it made me think about how easily the Same Origin Policy can be bypassed.
While the browser prevents you from accessing the error directly, and cancels the request altogether when it doesn't pass a preflight request, a simple node server does not need to abide to such rules, and can be used as a proxy.
All there needs to be done is to append 'https://cors-anywhere.herokuapp.com/' to the start of the requested url in the malicious script and Voila, you don't need to pass CORS.
And as sideshowbarker pointed out, it takes a couple of minutes to deploy your own Cors-Anywhere server.
Doesn't it make SOP as a security measure pretty much pointless?
The purpose of the SOP is to segregate data stored in browsers by their origin. If you got a cookie from domain1.tld (or it stored data for you in a browser store), Javascript on domain2.tld will not be able to gain access. This cannot be circumvented by any server-side component, because that component will still not have access in any way. If there was no SOP, a malicious site could just read any data stored by other websites in your browsers.
Now this is also related to CORS, as you somewhat correctly pointed out. Normally, your browser will not receive the response from a javascript request made to a different origin than the page origin it's running on. The purpose of this is that if it worked, you could gain information from sites where the user is logged in. If you send it through Cors-Anywhere though, you will not be able to send the user's session cookie for the other site, because you still don't have access, the request goes to your own server as the proxy.
Where Cors-Anywhere matters is unauthenticated APIs. Some APIs might check the origin header and only respond to their own client domain. In that case, sure, Cors-Anywhere can add or change CORS headers so that you can query it from your own hosted client. But the purpose of SOP is not to prevent this, and even in this case, it would be a lot easier for the API owner to blacklist or throttle your requests, because they are all proxied by your server.
So in short, SOP and CORS are not access control mechanisms in the sense I think you meant. Their purpose is to prevent and/or securely allow cross-origin requests to certain resources, but they are not meant to for example prevent server-side components from making any request, or for example to try and authenticate your client javascript itself (which is not technically possible).
I have an electron app set through the React CLI. It seems to work fine but when I try and do an external HTTP request though:
https.get('https://blockchain.info/q/24hrprice', (result) => {
console.log('RATE: ', result);
});
I get:
Fetch API cannot load https://blockchain.info/q/24hrprice.
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Origin 'http://localhost:3000' is therefore not allowed access.
If an opaque response serves your needs,
set the request's mode to 'no-cors' to fetch the resource with CORS disabled.
You're making a cross-origin request (your requesting domain is different than the API's domain) and browsers have special rules around that. In a Node context (i.e. via node's http/https modules), you don't have to worry about CORS and can in general do lower level work on network requests. Browsers, on the other hand, have a lot of security around CORS. When you use fetch your network request is going through Chromium's networking layer and so it's subject to those restrictions. When you use node's http/https, you're using node's. It's sort of a confusing point about Electron--a renderer process seems like a normal web context but you actually have access to all of node.js' APIs too, allowing you to things you can't do in a plain browser.
I would check and see if including an API key in that request changes the response of the API (maybe that triggers their API adding the appropriate CORS headers like Access-Control-Allow-Origin). Maybe the API wasn't meant to be called within a browser context and so a more node.js oriented approach is the way to go.
This is a great article on CORS and the ways to deal with it: https://medium.com/#baphemot/understanding-cors-18ad6b478e2b
Lets say you have an API that is primarily consumed by browsers from other origins.
Each customer has their own subdomain on the API, so .api.service.com
The service allows the customer to define which origins should be allowed to perform CORS-requests.
When a browser with an allowed origin performs a request, the server responds with the expected Access-Control-Allow-Origin header set to the same value as the Origin request header.
When a browser performs a request from an origin that is NOT allowed, the common way to handle this is to respond to the request with a 403 without specifying the Access-Control-Allow-Origin header, which will cause the browser to trigger an error on the request. The browser does not, however, expose any information that the error was caused by missing CORS-headers (although it logs a helpful error in the console, usually).
This makes it hard to programatically show a helpful "This origin is not allowed, please configure."-message, since there doesn't seem to be a good way to reliably decide whether the error was caused by a wifi glitch, a network error or an invalid/missing CORS-configuration.
My question is; when the server detects an origin that should not be allowed, instead of responding with no CORS-headers, could it respond with a 403 and include CORS-headers to allow the browser to read the error?
Since every request goes through this process on the API, I'm thinking this should be safe, but I might be overlooking something. Thoughts?
Most CSRF solutions seem to insist that the CSRF token is sent as part of the POST data.
In my situation the data being sent is json, and I don't control what is sent (and I don't want to start messing with the json). So, I'm thinking of sending the CSRF token as a header. However, there are legacy parts of my application that would still need to be able to send the token in the body (e.g. submits from html forms).
So my CSRF protection would have to allow the request if a valid CSRF token appeared in the body OR a header. Is this a security risk, compared with insisting that the token is in the body?
CSRF is about make a unsuspicious user post data to a server where the attacker believes the user is logged in.
The idea behind the protection, is that the server associate a token to your session, and sends it to you as a cookie and as payload requirement. Then when posting something you send the token in the payload, and as cookie. Therefore the attacker cannot guess what token is in the cookie or the session. If the server receives a post with two different tokens, it will be rejected.
I think it would be fine to put the payload token in a header, as long it is not "Cookie" or any other header that is "remembered" and sent automatically by the browser.
There won't be any security risk if you send a CSRF token in header. Just make sure that the value of this header changes everytime the client requests a page i.e it should be a random number. Also, your web application on client side should send this header back to the server, so that the server can match the value of header sent to the client with the value of the same header received from the client's response.
Sending CSRF in request header is more secure.
CORS doesn't check same-origin policy for the form tag requests, which means if somebody managed to get the CSRF token then he can send the post request by using form tag from different domain (origin)
but in case of sending the CSRF in request header, the form tag cannot send request header, he has to use javascript (fetch() or XmlHttpRequest()), in this case the CORS will prevent him because he is sending from different domain (origin).
This defense relies on the same-origin policy (SOP) restriction that only JavaScript can be used to add a custom header, and only within its origin. By default, browsers do not allow JavaScript to make cross origin requests with custom headers.
below, is quoted from https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#use-of-custom-request-headers
If this is the case for your system, you can simply verify the presence of this header and value on all your server side AJAX endpoints in order to protect against CSRF attacks. This approach has the double advantage of usually requiring no UI changes and not introducing any server side state, which is particularly attractive to REST services. You can always add your own custom header and value if that is preferred.
This technique obviously works for AJAX calls, but you still need to protect form tags with approaches described in this document such as tokens. Also, CORS configuration should also be robust to make this solution work effectively (as custom headers for requests coming from other domains trigger a pre-flight CORS check).
Can the JSON data, returned by a POST request be stolen by a cross-site request forgery attack?
It cannot be done using JS, but I'm not sure about Flash's cross-domain request.
In JS POST request can be made via forms and XMLHTTPRequest. You can't see result of a cross-domain form, so that's safe. XHR forbids cross-domain requests, so that's safe too.
Browsers do allow cross-domain inclusion of scripts via <script> element, but that is using GET only.