I want to be able, using javascript, to reload the currently loaded resource, but customising some header variables, before doing so.
This method is needed/should be used in 2 real cases:
As a way, to handle login via real HTTP-login, which means reloading the resource with attached authentication data
To get different media-type representations of the same resource, which means modifying the accept header, before re-sending the request.
As I would need to leave any other header fields unchanged, as the browser would send it natively, I cannot simply construct my own XMLHttpRequest, using the document.URL value. Rather I would need to get hold of a XMLHttpRequest representation of the request, the browser sent for the current resource.
Is there a way to accomplish that in JS? Preferably using pure JS without any jQuery magic.
If it was loaded via GET, you can just create an XMLHttpRequest and use document.location.href as the URL.
The browser will specify the usual cookie headers, so your request headers should be largely the same unless the GET changed cookies in non-idempotent ways.
If the current resource was loaded via POST, then you are out of luck. The POST body of the current resource is not available to JavaScript so unless you would need some other way to get it or infer it from page content.
Related
I need to access a page on another domain, which returns as response a JSON array:
[{"name":"value","x":"y"},{"name":"value","x":"y"}]
Due to the cross-origin rule, I can't get this data through an XMLHttpRequest. Apparently I have to use a JSONP-like procedure:
Add script element to the page's body, with the src attribute poiting to the page
The browser does the HTTP request and gets the data
I can not edit the remote page/the response. The remote page is not meant to be accessed that way (so I can't use a JSONP callback parameter).
The remote page also requires the use of a specific cookie.
How can I access the data that has been just retrieved?
That's exactly the case the same origin policy exists for. Any possible solution would mean a security hole. If the data server unable to wrap it in your callback function, you have to proxy this through your own server-side app.
Forged POST requests can be constructed by untrusted websites by creating a form and posting it to the target site. However, the raw contents of this POST will be encoded by the browser to be in the format:
param1=value1¶m2=value2
Is it possible for untrusted websites to construct forged POSTs which contain arbitrary raw content -- such as stringified JSON?
{param1: value1, param2: value2}
Put another way: Can websites cause the browser to POST arbitrary content to third-party domains?
The POST body of an HTML form’s request is always either application/x-www-form-urlencoded, multipart/form-data, or text/plain as these reflect the valid values for the enctype attribute. Especially text/plain one can be used to form valid JSON data. So form-based CSRF can be used here, however, it requires the server to accept it as text/plain.
Additionally, XHR-based CSRF can be used as the XMLHttpRequest API allows so send arbitrary POST data. The only remaining obstacle with this is the Same-Origin Policy: Only if both have the same origin or your server supports Cross-Origin Request Sharing and allows resource sharing, such valid POST requests can be forged.
Yes!, a POST request is nothing more than text with a specific format sent to a web server. You can use IE or Chrome developer tools to look at what each requests looks like.
So yes, you can create a forged POST request and change whatever you want, however if the request is not well-formed most web servers will reject it.
https://www.rfc-editor.org/rfc/rfc2616
The client side code of a web site would have difficulties to forge a request like that, but the server side code could very easily do that.
As your web site can't tell if the request comes from a browser or a server that behaves just like a browser, the limitations in the browser is no protection.
You can create valid JSON via a regular form post. It's just a matter of creatively naming the form parameters. In particular, parameter names can contain quotes.
http://blog.opensecurityresearch.com/2012/02/json-csrf-with-parameter-padding.html
In the case of pure HTML forms, yes it will always be encoded according to the spec. But there are other encoding schemes such as MIME multipart. There is also the question of Javascript and XMLHttpRequest. Encoding is specifically mentioned in only one case. This strongly implies that there is no encoding applied in the other cases.
I'm jusing jQuery's $.getJSON() function to return a short set of JSON data.
I've got the JSON data sitting on a url such as example.com.
I didn't realize it, but as I was accessing that same url, the JSON data couldn't be loaded. I followed through the console and found that XMLHttpRequest couldn't load due to Access-Control-Allow-Origin.
Now, I've read through, a lot of sites that just said to use $.getJSON() and that would be the work around, but obviously it didn't work. Is there something I should change in the headers or in the function?
Help is greatly appreciated.
It's simple, use $.getJSON() function and in your URL just include
callback=?
as a parameter. That will convert the call to JSONP which is necessary to make cross-domain calls. More info: http://api.jquery.com/jQuery.getJSON/
You may well want to use JSON-P instead (see below). First a quick explanation.
The header you've mentioned is from the Cross Origin Resource Sharing standard. Beware that it is not supported by some browsers people actually use, and on other browsers (Microsoft's, sigh) it requires using a special object (XDomainRequest) rather than the standard XMLHttpRequest that jQuery uses. It also requires that you change server-side resources to explicitly allow the other origin (www.xxxx.com).
To get the JSON data you're requesting, you basically have three options:
If possible, you can be maximally-compatible by correcting the location of the files you're loading so they have the same origin as the document you're loading them into. (I assume you must be loading them via Ajax, hence the Same Origin Policy issue showing up.)
Use JSON-P, which isn't subject to the SOP. jQuery has built-in support for it in its ajax call (just set dataType to "jsonp" and jQuery will do all the client-side work). This requires server side changes, but not very big ones; basically whatever you have that's generating the JSON response just looks for a query string parameter called "callback" and wraps the JSON in JavaScript code that would call that function. E.g., if your current JSON response is:
{"weather": "Dreary start but soon brightening into a fine summer day."}
Your script would look for the "callback" query string parameter (let's say that the parameter's value is "jsop123") and wraps that JSON in the syntax for a JavaScript function call:
jsonp123({"weather": "Dreary start but soon brightening into a fine summer day."});
That's it. JSON-P is very broadly compatible (because it works via JavaScript script tags). JSON-P is only for GET, though, not POST (again because it works via script tags).
Use CORS (the mechanism related to the header you quoted). Details in the specification linked above, but basically:
A. The browser will send your server a "preflight" message using the OPTIONS HTTP verb (method). It will contain the various headers it would send with the GET or POST as well as the headers "Origin", "Access-Control-Request-Method" (e.g., GET or POST), and "Access-Control-Request-Headers" (the headers it wants to send).
B. Your PHP decides, based on that information, whether the request is okay and if so responds with the "Access-Control-Allow-Origin", "Access-Control-Allow-Methods", and "Access-Control-Allow-Headers" headers with the values it will allow. You don't send any body (page) with that response.
C. The browser will look at your response and see whether it's allowed to send you the actual GET or POST. If so, it will send that request, again with the "Origin" and various "Access-Control-Request-xyz" headers.
D. Your PHP examines those headers again to make sure they're still okay, and if so responds to the request.
In pseudo-code (I haven't done much PHP, so I'm not trying to do PHP syntax here):
// Find out what the request is asking for
corsOrigin = get_request_header("Origin")
corsMethod = get_request_header("Access-Control-Request-Method")
corsHeaders = get_request_header("Access-Control-Request-Headers")
if corsOrigin is null or "null" {
// Requests from a `file://` path seem to come through without an
// origin or with "null" (literally) as the origin.
// In my case, for testing, I wanted to allow those and so I output
// "*", but you may want to go another way.
corsOrigin = "*"
}
// Decide whether to accept that request with those headers
// If so:
// Respond with headers saying what's allowed (here we're just echoing what they
// asked for, except we may be using "*" [all] instead of the actual origin for
// the "Access-Control-Allow-Origin" one)
set_response_header("Access-Control-Allow-Origin", corsOrigin)
set_response_header("Access-Control-Allow-Methods", corsMethod)
set_response_header("Access-Control-Allow-Headers", corsHeaders)
if the HTTP request method is "OPTIONS" {
// Done, no body in response to OPTIONS
stop
}
// Process the GET or POST here; output the body of the response
Again stressing that this is pseudo-code.
I need to send data to a remote server using javascript. How do I do this?
Background info:
There's a webpage from which I extract some information using JS, and I need to send it back to another server for processing. Response is not neccesary. The data is XML, Which I've URLencode'd.
How would one do this?
EDIT
The server I'm requesting the data from is not the same that receives the data. Just to clarify.
One of the most common ways to do this is AJAX. Here's how you perform an AJAX post request using jQuery:
<script type="text/javascript">
$.post('/remote-url', {xml: yourXMLString });
</script>
On the server side you process it like any other POST request. If you're using PHP it's $xml = $_POST['xml'];
The biggest limitation of AJAX is that you're only allowed to make requests to the same domain the document has been loaded from (aka cross-domain policy). There are various ways to overcome this limitation, one of the easiest one is JSONP.
UPD. For cross-domain requests an extremely simple (though not universal) solution would be:
(new Image).src = 'http://example.com/save-xml?xml=' + escape(yourXMLString)
This will issue a GET request (which cannot exceed 2KB in Internet Explorer). If you absolutely need a POST request or support for larger request bodies you can either use an intermediate server-side script on your domain or you can post a dynamically created html form to iframe.
submit a form using POST. That is working on all browsers cross domains. Have the server process the post. the form can be submitted to a hidden frame if you want to simulate AJAX
Use Cross Domain Resource Sharing (MDC) (IE XDR)
use a web bug (create an image, set the source to the url you want - smallish GET requests only)
var img = new Image();
img.src="http://www.otherserver.com/getxml?xml="+encodeURIComponent(yourXML);
(Oops, I see Lebedev did more or less the same in his update)
use a proxy, i.e. have your server talk to the other server for you
Look into Javascript's XMLHTTPRequest method -- or start with a Google search for AJAX. There's lots of ways to do this -- including some very easy ways through JS libraries like jQuery -- but a more specific answer would require some more specifics on the specific technologies you're using.
EDIT: You can set up the AJAX request to post to a server-side script (acting as a proxy) on your own domain, and have that script turn around and post the data to your remote server.
I need to make an AJAX request from a website to a REST web service hosted in another domain.
Although this is works just fine in Internet Explorer, other browsers such as Mozilla and Google Chrome impose far stricter security restrictions, which prohibit cross-site AJAX requests.
The problem is that I have no control over the domain nor the web server where the site is hosted. This means that my REST web service must run somewhere else, and I can't put in place any redirection mechanism.
Here is the JavaScript code that makes the asynchronous call:
var serviceUrl = "http://myservicedomain";
var payload = "<myRequest><content>Some content</content></myRequest>";
var request = new XMLHttpRequest();
request.open("POST", serviceUrl, true); // <-- This fails in Mozilla Firefox amongst other browsers
request.setRequestHeader("Content-type", "text/xml");
request.send(payload);
How can I have this work in other browsers beside Internet Explorer?
maybe JSONP can help.
NB youll have to change your messages to use json instead of xml
Edit
Major sites such as flickr and twitter support jsonp with callbacks etc
The post marked as the answer is erroneous: the iframes document is NOT able to access the parent. The same origin policy works both ways.
The fact is that it is not possible in any way to consume a rest based webservice using xmlhttprequest. The only way to load data from a different domain (without any framework) is to use JSONP. Any other solutions demand a serverside proxy located on your own domain, or a client side proxy located on the remote domain and som sort of cross-site communication (like easyXDM) to communicate between the documents.
The fact that this works in IE is a security issue with IE, not a feature.
Unfortunately cross-site scripting is prohibited, and the accepted work around is to proxy the requests through your own domain: do you really have no ability to add or modify server side code?
Furthermore, the secondary workaround - involving the aquisition of data through script tags - is only going to support GET requests, which you might be able to hack with a SOAP service, but not so much with the POST request to a RESTful service you describe.
I'm really not sure an AJAX solution exists, you might be back to a <form> solution.
The not very clear workaround (but works) is using iframe as container for requests to another sites. The problem is, the parent can not access iframe's content, can only navigate iframe's "src" attribut. But the iframe content can access parent's content.
So, if the iframe's content know, they can call some javascript content in parent page or directly access parent's DOM.
EDIT:
Sample:
function ajaxWorkaroung() {
var frm = gewtElementById("myIFrame")
frm.src = "http://some_other_domain"
}
function ajaxCallback(parameter){
// this function will be called from myIFrame's content
}
Make your service domain accept cross origin resource sharing (CORS).
Typical scenario: Most CORS compliant browsers will first send an OPTIONS header, to which, the server should return information about which headers are accepted. If the headers satisfy the service's requirements for the request provided (Allowed Methods being GET and POST, Allowed-Origin *, etc), the browser will then resend the request with the appropriate method (GET, POST, etc.).
Everything this point forward is the same as when you are using IE, or more simply, if you were posting to the same domain.
Caviots: Some service development SDK's (WCF in particular) will attempt to process the request, in which case you need to preprocess the OPTIONS Method to respond to the request and avoid the method being called twice on the server.
In short, the problem lies server-side.
Edit There is one issue with IE 9 and below with CORS, in that it is not fully implemented. Luckily, you can solve this problem by making your calls from server-side code to the service and have it come back through your server (e.g. mypage.aspx?service=blah&method=blahblah&p0=firstParam=something). From here, your server side code should implement a request/response stream model.
Just use a server side proxy on your origin domain. Here is an example: http://jquery-howto.blogspot.com/2009/04/cross-domain-ajax-querying-with-jquery.html
This can also be done using a webserver setup localy that calls curl with the correct arguments and returns the curl output.
app.rb
require 'sinatra'
require 'curb'
set :views,lambda {"views/"+self.name.to_s.downcase.sub("controller","")}
set :haml, :layout => :'../layout', :format => :html5, :escape_html=>true
disable :raise_errors
get '/data/:brand' do
data_link = "https://externalsite.com/#{params[:brand]}"
c = Curl::Easy.perform(data_link)
c.body_str
end
Sending an ajax request to localhost:4567/data/something will return the result from externalsite.com/something.
Another option would be to setup a CNAME record on your own domain to "Mask" the remote domain hostname.