Why does the browser allow xorigin POST but not PUT? - javascript

Consider the very simple example of using XMLHttpRequest.
The following posts properly ( you can see it in the network tab or by directing your browser to http://requestb.in/yckncpyc) although it prints a warning to the console
XMLHttpRequest cannot load http://requestb.in/yckncpyc. No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'null' is therefore not allowed access.
const method = "POST"
const req = new XMLHttpRequest()
req.open(method, 'http://requestb.in/yckncpyc')
req.send("foobar")
console.log("sent")
req.addEventListener('load', function() { console.log(req.status, req.response) })
Sure. I get that. What I don't get is why merely changing the verb used to a PUT results in something completely different. The request sent is an OPTIONS preflight request and prints
XMLHttpRequest cannot load http://requestb.in/yckncpyc. Response to
preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'null' is therefore not allowed access.
const method = "PUT"
const req = new XMLHttpRequest()
req.open(method, 'http://requestb.in/yckncpyc')
req.send("foobar")
console.log("sent")
req.addEventListener('load', function() { console.log(req.status, req.response) })
Why does the browser* treat these differently? It seems like something that would be done for security but that really makes no sense since an attacker can always use a POST instead of a PUT.
So what is the logic here?
Tried this in Chrome 52, Safari 9.1.2

GET, HEAD, and POST requests (with a couple other restrictions) can be made cross-origin with no additional communication. The responses cannot be examined, but the requests are allowed.
Anything else requires a preflight request to check the headers from the target site to see whether the request would be allowed.
The reason for such a setup is that GET, HEAD, and POST were historically allowed from browsers as a natural part of HTML semantics. Tags for scripts and CSS and images do GET requests, and forms do POSTs. When the CORS stuff was introduced, therefore, those were allowed under the assumption that sites were no more vulnerable to simple requests like that in an XHR world then they were in the simpler non-XHR world.
So simple requests are allowed, and the browser looks at the response headers to decide whether the requesting code in the cross-origin page should be allowed to see the response content. For other requests, the browser first sends an OPTIONS request to check the CORS response headers. Only if that looks OK (that is, if the response headers contain the appropriate "yes that's OK" headers) will the XHR be allowed to proceed.

Related

Cannot assign "Access-Control-Allow-Origin" in XmlHttpRequest

I am trying to make a request to ping my backend api with XMLHttpRequest.
Following is my code
var r = new XMLHttpRequest();
r.open("POST", 'domain:port/path/');
r.setRequestHeader("Access-Control-Allow-Origin", '*');
r.setRequestHeader("Accept", 'application/json ');
var data = {"key":"value"};
r.send(data);
But I always accept the following error message
Access to XMLHttpRequest at 'domain:port/path/' from origin
'http://localhost:8000' has been blocked by CORS policy: Response to
preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource.
Firstly; I think that's because I didn't set "Access-Control-Allow-Origin". But even I set it, it's not working.
How can I solve my problem?
Thanks.
From MDN
The Access-Control-Allow-Origin response header indicates whether the response can be shared with requesting code from the given origin
it is a response header, you need to set Access-Control-Allow-Origin on your server-side code
Access-Control-Allow-Origin is a response header, not a request header.
Your server decides what origins are allowed to access it. If it were that easy to bypass, would it really be a security measure? :-)
If what you truly want is for that endpoint to allow any domain to access it, then you have to add the header, there.

CORS redirect works unexpectedly

According to CORS specification (7.1.7 - Redirect Steps (for Simple Cross-Origin Request)):
If the request URL origin is not same origin with the original URL origin, set source origin to a globally unique identifier (becomes "null" when transmitted).
I have a scenario where javascript from a.blah.com makes a CORS request (i.e. Origin request header present) by sending browser to b.blah.com, which responds with a 302 and location = c.blah.com. If I am reading the spec correctly, this should result in the request to c.blah.com containing Origin header = "null". Instead, the Origin header is not present and thus the request to c.blah.com is not considered a CORS request.
The above behavior was experienced in Chrome 54. I have not confirmed the exact request contents in other browsers, but I have checked that my particular application flow works in Chrome 54, Firefox 37, and IE 11 browsers, which implies they never see Origin header set to "null" (my services will fail requests loudly if the receive an Origin = "null").
This all worries me because while my application is working, it actually shouldn't be, and I don't want to just ignore this fact. Am I misunderstanding the spec? Are there any caveats to the spec behavior that I've missed?
All traffic is HTTPS, not returning * (wildcard) in CORS response header, setting with-credentials flags/headers as appropriate, no proxies in use, all actors on separate machines so should not be a localhost gotcha...
Thanks.
In my original configuration, the request to b.blah.com was a form posted by js (not xhr). After some digging around, it seems that since the request was triggered by js, that warranted an Origin header on the request to b.blah.com, but the resulting redirect to c.blah.com was handled by the browser without any script/xhr intervention, so the redirect was not decorated with an Origin header.
I set up a test where the request to b.blah.com was xhr, and that did cause an Origin = "null" on the redirect to c.blah.com.
I suppose I need to better research the nuances of when same-origin policy is enforced.
Thanks.

Cross domain post json without preflight

I am trying to post some json to a sharepoint url, as in this example. The example uses node, but I am trying to do it in the browser.
I tried it with fetch first:
fetch("https://outlook.office365.com/webhook/...",
{
method: 'POST',
headers: {
'Content-Type': 'application/json;'
},
body: JSON.stringify(this.groupCardBody()),
})
From that i got the error:
Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource. Origin 'http://localhost:1234' 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.
But i don't have control over the response, and if i add mode: 'no-cors' into the fetch options as it suggests, it strips the content-type header and returns 415 Unsupported Media Type.
So i tried it with a simple xhttp request and that fails too as it does the preflight and doesn't get the right headers back:
let xhr = new XMLHttpRequest()
xhr.open("POST", "https://outlook.office365.com/webhook/...");
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xhr.send(JSON.stringify(this.groupCardBody()));
Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource.
The request library used in the example was quite difficult to get working in the browser and by no means lightweight (added almost 2MB to my webpacked script), so any suggestions about how to get round this are welcome I am pretty stuck, all my searches turn up answers for fixing the server and i don't have that option.
UPDATE
As suggested in the accepted answer, I solved it by posting the json back to the server and making the post from there, got it working with something as simple as the following:
Client:
fetch("PostGroupCard?json="+
encodeURI(JSON.stringify(this.groupCardBody())),
{credentials: "same-origin"}
)
Server:
Function PostGroupCard(json As String)
Dim wr = WebRequest.Create("https://outlook.office365.com/webhook/...")
wr.ContentType = "application/json"
wr.Method = "POST"
Using sw = New StreamWriter(wr.GetRequestStream())
sw.Write(json)
sw.Flush()
sw.Close()
End Using
Dim r = wr.GetResponse()
Using sr = New StreamReader(r.GetResponseStream())
Dim result = sr.ReadToEnd()
End Using
End Sub
Most of this answer is taken from the comments.
For security reasons, you cannot make an XMLHTTPRequest unless you are on the same origin or CORS header from the other domain says you can. If this were possible, any site could preform malicious actions like hack you accounts.
Two alternatives to consider would be JSONP and having your server act as a proxy to access the other domain.

No 'Access-Control-Allow-Origin' header is present on the requested resource on AJAX request

I'm using JQuery:
$('#myDiv').load('myApp/url',function(){ });
and it's giving No 'Access-Control-Allow-Origin' header is present on the requested resource By chrome, and firefox so far , any straight forward answer on how to fix this . I don't have control over server to make any configurations and I'm using PHP
This is a CORS issue (Cross Origin Resource Sharing), you are trying to request content via ajax from two different domains. Unless the domain from where you want to grab the data has properly set the CORS headers, browsers will cancel the request right away.
This occurs due to the communication between two different domains. The domain that will server your data, should have some headers set, this headers act as permissions, they tell which domains are allowed to ask for data from it, and which verbs/methods are allowed.
You can read more about this here and here
No, there won't be a straight forward answer to this because it will depend entirely on your system/server setup, and what you have access to. Here's what you need to know.
In the beginning -- AJAX requests had a very strict "same origin" policy. This meant if you made an ajax request FROM a website with the domain example.com, you could only make a request to a URL that was on example.com.
In more recent years browsers have loosened up on this. If the server that you're making a request to has an Access-Control-Allow-Origin header, and that header includes the URL/domain of the server you're making the request from, then the request will be allowed. Similar question/answer here.
So, how you set this header depends on the server you're making a request to. If you have control over this server, start your Googling there.
If you don't have control over this server, you need to make a request to php page on your server, and this PHP page should make a curl request to the server that had the information you don't. A curl request, happening outside the browser, isn't subject to the same cross domain issues.
The easy way is to do this by hand:
var script = document.createElement('script');
script.src = uri;
script.id = 'scriptid';
document.head.appendChild(script);
It may be some browser compatibility issues, but you get the power of CORS with no 'Access-Control-Allow-Origin' error

Understanding XMLHttpRequest over CORS (responseText)

For a project I'm looking at various HTML5 and Javascript elements and security around them and I'm trying to get my head around CORS just now.
Based on my testing, if I remove..
<?php
header("Access-Control-Allow-Origin: *");
header('Access-Control-Allow-Methods: GET, POST, OPTIONS');
?>
..from the page that is trying to be accessed I see the following in the console log on Chrome:
XMLHttpRequest cannot load http://www.bla.com/index.php. Origin http://bla2.com is not allowed by Access-Control-Allow-Origin.
I understand this to be correct, however Wireshark shows HTTP/1.1 200 OK in the return and in the data shows the source of the page being requested. So is it just the browser and Javascript that is blocking responseText from being used in any substantial way even though it's actually transferred?
The code is just as below:
function makeXMLRequest() {
xmlhttp=new XMLHttpRequest();
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState==4) {
alert(xmlhttp.responseText);
}
}
xmlhttp.open("GET","http://www.bla.com/index.php",true);
xmlhttp.send();
}
Thanks in advance.
For a "simple" HTTP verb like GET or POST, yes, the entire page is fetched, and then the browser decides whether JavaScript gets to use the contents or not. The server doesn't need to know where the requests comes from; it is the browser's job to inspect the reply from the server and determine if JS is permitted to see the contents.
For a "non-simple" HTTP verb like PUT or DELETE, the browser issues a "preflight request" using an OPTIONS request. In that case, the browser first checks to see if the domain and the verb are supported, by checking for Access-Control-Allow-Origin and Access-Control-Allow-Methods, respectively. (See the "Handling a Not-So-Simple Request" on the CORS page of HTML5 Rocks for more information.) The preflight response also lists permissible non-simple headers, included in Access-Control-Allow-Headers.
This is because allowing a client to send a DELETE request to the server could be very bad, even if JavaScript never gets to see the cross-domain result -- again, remember that the server is generally not under any obligation to verify that the request is coming from a legitimate domain (although it may do so using the Origin header from the request).
So is it just the browser and Javascript that is blocking responseText from being used in any substantial way even though it's actually transferred?
Yes. You can make any request you like with JS.
It is access to the data that the same origin policy prevents.
Requests which do malicious things (such as "POST http://bank.example/give/money?to=attacker" or "POST http://forum.example.com/post?message=spamspamspamspam") are called CSRF attacks and have to be defended against by the server.

Categories

Resources