If we use javascript's http request function:
var request = new XMLHttpRequest();
to an https address, will this use any type of encryption or will a MITM be able to see all data we send?
Example:
function createAuthToken(baseRestURL, callback) {
var APIPath = "account/api/session";
var CORSPath = "https://cors-anywhere.herokuapp.com/";
var completeRestURL = CORSPath + baseRestURL + APIPath;
console.log("REST API URL: " + completeRestURL);
var method = "POST";
var postData = "{\"tokenId\": \"" + document.getElementById('api_key').value + "\",\"secret\": \"" + document.getElementById('secret').value + "\",\"loginMode\": 1,\"applicationType\": 35}";
var async = true;
var request = new XMLHttpRequest();
request.onreadystatechange = function() {
if (request.readyState == 4 && (request.status == 200 || request.status == 201)) {
console.log("ONLOAD");
var status = request.status; // HTTP response status, e.g., 200 for "200 OK"
console.log(status);
var response = JSON.parse(request.responseText);
console.log(response.session_token);
return callback(response.session_token);
}
}
request.open(method, completRestURL, async);
request.setRequestHeader("Content-Type", "application/json");
request.setRequestHeader("Accept", "application/json");
request.send(postData);
Follow up question: If not, is there a way to include encryption in our client side javascript that is safe? My thoughts was to use a webisite's public key to encrypt the request before sending it to the server but I can't find anyone else attempting client side encryption.
Rough example:
var CryptoJS = require("crypto-js");
var stackOverflowKey = "30 82 01 0a 02 82 01..."
var postData = "{\"tokenId\": \"" + document.getElementById('api_key').value + "\",\"secret\": \"" + document.getElementById('secret').value + "\",\"loginMode\": 1,\"applicationType\": 35}";
var encryptedPostData = cryptoJS.hmacSHA256(postData, stackOverflowKey)
//let's skip the callback and request headers as they are the same as above
var request = new XMLHttpRequest();
request.open();
request.send(encryptedPostData);
I didn't study computer science and couldn't find anything online about this. What are the generally accepted ways of doing this?
The HTTP in XMLHttpRequest, as is the XML part, is just a left over naming scheme. As the requests used can include more than just http protocol urls, and receive more than just an XML response body.
For instance the initial W3C working drafts introduced the XMLHttpRequest object by saying:
https://www.w3.org/TR/2006/WD-XMLHttpRequest-20060927/#introduction
The name of the object is XMLHttpRequest for compatibility with the web
as it doesn't make much sense otherwise. It supports the transport of
other data formats in addition to XML, some implementations support
other protocols besides HTTP (that functionality is not covered in
this specification though) and the API supports sending data as well.
Note the "some implementations" as this is a working draft back in 2006 so not everyone was using the same implementation.
The current whatwg spec for XMLHttpRequest has this to say about the name:
https://xhr.spec.whatwg.org/#introduction
The name XMLHttpRequest is historical and has no bearing on its
functionality.
So as long as the browser being used implements the XMLHttpRequest according to specs, the request/response will be treated as it would normally by the browser, ie encrypted for https.
Related
I followed some guides on how to send json objects to the server(written using node.js) and it doesn't work, I have no idea what is wrong. I know that my server works fine since I tested it on postman so it's my js code that's the problem, all the tutorials I see follow a similar XMLHttpRequest format.
this is my code
var ing = new Ingredient(name, date, qty, rp);
var url = "http://localhost:8081/addIngredient";
var xhr = new XMLHttpRequest();
xhr.open("POST", url, true);
//Send the proper header information along with the request
// application/json is sending json format data
xhr.setRequestHeader("Content-type", "application/json");
// Create a state change callback
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
// Print received data from server
result.innerHTML = this.responseText;
}
};
// Converting JSON data to string
var data = JSON.stringify(ing);
document.write(data);
// Sending data with the request
xhr.send(data);
I used document.write to check where the code stops working but everything passes (since the document.write prints something), I suspect that there is something wrong/missing from xhr.send(data) but I can't tell what. Finally, nothing gets printed from the callback.
It's better to use onload instead of onreadystatechange
xhr.onload = function() {
if (xhr.status == 200) {
console.log(`Response length = ${xhr.response.length}`);
// store xhr.response here somewhere
}
};
In our asp.net core application, I need to make an AJAX call to an IIS 8.0 Server.
The server URL is protected by CA SiteMinder SSO.
When I make Get requests using AJAX I get the desired response. But on the contrary, whenever a Put or Post request is made, I get a 302 response and the url which suggests SiteMinder is requesting credentials.
I was of the opinion, that as long as a User is authenticated by SiteMinder, then requests made to the same domain from the same browser session would not explicitly require users credentials.
As far as I can see, SiteMinder is requesting user credentials even when the user has been authenticated and the requests (PUT & POST) are being made to the same domain.
CA SiteMinder provides a cookie (HttpOnly), which I believe is used to authenticate requests made to the server. I can confirm that the SiteMinder cookie is being included in the requests headers.
My question is why does SiteMinder treat GET and POST/Put requests differently? Or is there any way I can make my POST/PUT request work(without getting a redirect from SiteMinder) with Siteminder using the XHR?
Here is the link to function that makes the XHR request.
function fixRecords() {
_buildingId = ($("input:hidden[id='buildingIdModal']").val());
_roomNo = $("#roomNoModal").val();
if ((_roomNo === "" || _roomNo == null) && _suggestedRoomNo) {
_roomNo = document.getElementById("roomNoModal").value;
}
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
if (this.readyState == 4 && this.status.toString() == 200) {
var message = "Record updated sucessfully.";
var jsonResponse = JSON.parse(this.responseText);
console.log(jsonResponse);
_roomNo = jsonResponse.roomNo;
_buildingId = jsonResponse.building;
_runId = jsonResponse.runId;
var _prevRoomId = _roomId;
_roomId = jsonResponse.roomId;
_recId = jsonResponse.recordId;
message = jsonResponse.comment;
_valid = jsonResponse.valid;
_suggestion = '<div class="glyphicon glyphicon-alert text-info">' + jsonResponse.suggestion+'</div>';
_suggestedRoomId = jsonResponse.suggestedRoomId;
_suggestedRoomNo = jsonResponse.suggestedRoomNo;
var _protocol = jsonResponse.protocol;
var _invAcct = jsonResponse.account;
var _pi = jsonResponse.pi;
displayInformationInFixModal(message + _suggestion, null);
$('#fixModal').on('show.bs.modal', inflateModal(message, _buildingId, _cageId, _roomId, _roomNo, _recId, _runId, _frequencyId, _frequencyType, _valid, _suggestedRoomId, _suggestedRoom, _suggestion, _protocol, _pi, _invAcct));
$('#fixModal').modal('show').on("hide", function () {
$('#fixModal').modal('hide');
});
//document.write(this.responseText);
}
$('#showLoadingImage').modal('hide');
return false;
};
if (_roomNo == null || _roomNo.trim()===''|| _roomNo==="Room Num Unknown") {
alert("Please enter a valid Room No.");
var message = "Please enter a valid Room No.";
$('#fixModal').on('show.bs.modal', populateModalMessage(message));
$('#fixModal').modal('show').on("hide", function () {
$('#fixModal').modal('hide');
});
}
if (_recId <=0) {
xhttp.open("POST", "/FileUploads/InsertFixRecordToDB?BuildingId=" + _buildingId );
}
else {
xhttp.open("PUT", "/FileUploads/FixARecord?BuildingId=" + _buildingId );
}
$('#showLoadingImage').modal('show');
$('#showLoadingImage').css('zIndex', 1500);
xhttp.send();
}
If the SiteMinder cookie is HttpOnly then it explicitly will not be sent by the ajax engine. That's what HttpOnly means.
Separately, SiteMinder (now called CA SSO) definitely distinguishes between different HTTP methods, so the SiteMinder rules can be different for GET POST and PUT. Your SiteMinder admin will need to check the rules applicable for your application to make sure they specifically cover GET POST and PUT (PUT especially is often not included).
HTH!
I am trying to achieve that when I call the JS function, a post request is send. In my browser I would send:
http://myuser:password#hc2:80/api/callAction?deviceID=185&name=turnOn
This works. Yet in my code it doesn't.
Important to note:
- Chrome does raise an Error: Request doesn't pass access control. If I disable this in Chrome, I doesn't display this error (yet no response from the server either).
<script type="text/javascript">
function changestate() {
var http = new XMLHttpRequest();
http.withCredentials = true;
var user = "bassie"
var pass = "password"
var url = "http://hc2/api/callAction";
var params = "deviceID=185&name=turnOff";
http.open("POST", url, true);
http.setRequestHeader("Authorization", "Basic " + user + ":" + pass);
//Send the proper header information along with the request
http.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
alert(http.responseText);
http.onreadystatechange = function() {//Call a function when the state changes.
if(http.readyState == 4 && http.status == 200) {
alert(http.responseText);
}
}
http.send(params);
}
</script>
The equivalent to putting the URL in the browser's location is a GET request, not POST.
Since you're sending a cross-domain request, you won't be able to read the response (unless you relay through a proxy on your origin server). So you can't read http.responseText, and can simply omit the onreadystatechange function; you'll just have to assume it
function changestate() {
var http = new XMLHttpRequest();
http.withCredentials = true;
var user = "bassie"
var pass = "password"
var url = "http://hc2/api/callAction";
var params = "deviceID=185&name=turnOff";
http.open("GET", url + "?" + params, true);
http.setRequestHeader("Authorization", "Basic " + user + ":" + pass);
http.send();
}
Eventually ended up creating a sort of like proxy. This was the main component. Not in the example (My script gets the HTTP requested) and gets the output. Below the gist of it:
req = urllib.request.Request('http://hc2:80/api/callAction?param1=1¶m2=2')
credentials = ('%s:%s' % ('user', 'password'))
encoded_credentials = base64.b64encode(credentials.encode('ascii'))
req.add_header('Authorization', 'Basic %s' %
encoded_credentials.decode("ascii"))
response = urllib.request.urlopen(req)
I can make GET calls fine, but I don't know how to make PUT requests. The error I'm getting is "Failed to load resource: too many HTTP redirects"
Code:
var url = "https://{{shop.domain}}/cart.js";
var xhrPut = new XMLHttpRequest();
xhrPut.open("PUT", url, true);
xhrPut.setRequestHeader("Content-Type", "application/json");
xhrPut.send("{\"total_price\":50}");
The JSON that I'm trying to change is this:
{"token":"a97c89f7e2c7c22becab8cfef40fa272","note":null,"attributes":{},"original_total_price":100,"total_price":100,"total_discount":0,"total_weight":0,"item_count":1,"items":[{"id":19673785606,"properties":null,"quantity":1,"variant_id":19673785606,"key":"19673785606:59d774c1f413525441dcb716e0c982e2","title":"Short Sleeve Shirt","price":100,"line_price":100,"original_line_price":100,"total_discount":0,"discounts":[],"sku":"","grams":0,"vendor":"Dev Store","product_id":6213990086,"gift_card":false,"url":"\/products\/short-sleeve-shirt?variant=19673785606","image":"https:\/\/cdn.shopify.com\/s\/files\/1\/1278\/7069\/products\/happyman.jpg?v=1462811058","handle":"short-sleeve-shirt","requires_shipping":true,"product_type":"","product_title":"Short Sleeve Shirt","product_description":"description","variant_title":null,"variant_options":["Default Title"]}],"requires_shipping":true}
The total_price variable is currently 100, I'm trying to set it to 50.
Editing this so I may get answers
I changed the code to the following and then get error 400: bad request
var url = "https://{{shop.domain}}/cart.json";
var xhrPut = new XMLHttpRequest();
xhrPut.open("PUT", url, false);
xhrPut.setRequestHeader("Content-Type", "application/json");
xhrPut.send('{"token":"a97c89f7e2c7c22becab8cfef40fa272","note":null,"attributes":{},"original_total_price":100,"total_price":50,"total_discount":0,"total_weight":0,"item_count":1,"items":[{"id":19673785606,"properties":null,"quantity":1,"variant_id":19673785606,"key":"19673785606:59d774c1f413525441dcb716e0c982e2","title":"Short Sleeve Shirt","price":100,"line_price":100,"original_line_price":100,"total_discount":0,"discounts":[],"sku":"","grams":0,"vendor":"Dev Store","product_id":6213990086,"gift_card":false,"url":"\\/products\\/short-sleeve-shirt?variant=19673785606","image":"https:\\/\\/cdn.shopify.com\\/s\\/files\\/1\\/1278\\/7069\\/products\\/happyman.jpg?v=1462811058","handle":"short-sleeve-shirt","requires_shipping":true,"product_type":"","product_title":"Short Sleeve Shirt","product_description":"description","variant_title":null,"variant_options":["Default Title"]}],"requires_shipping":true}"');
From my knowledge of how the PUT request works, this should overwrite the entire document, but the only changes made were:
The variable total_cost was reduced from 100 to 50
The escape character '\' was used before the characters '\' in the variable 'url' in the array 'item'
Here is a GET request that I am sending before this PUT request, which returns no errors and writes the expected values to console. Note that the errors with the PUT request occur whether this is run or commented out.
var totalCost;
var url = "https://{{shop.domain}}/cart.json";
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4 && xhr.status == 200) {
var resp = xhr.responseText;
console.log(resp);
var json = JSON.parse(resp);
console.log(json);
totalCost = json.total_price;
console.log(totalCost);
}
};
xhr.open("GET", url, false);
xhr.send();
console.log(totalCost);
The only reason this is synchronous is so that I can log the totalCost after the API call.
I'm trying to parse a bit.ly JSON response in javascript.
I get the JSON via XmlHttpRequest.
var req = new XMLHttpRequest;
req.overrideMimeType("application/json");
req.open('GET', BITLY_CREATE_API + encodeURIComponent(url)
+ BITLY_API_LOGIN, true);
var target = this;
req.onload = function() {target.parseJSON(req, url)};
req.send(null);
parseJSON: function(req, url) {
if (req.status == 200) {
var jsonResponse = req.responseJSON;
var bitlyUrl = jsonResponse.results[url].shortUrl;
}
I do this in a firefox addon. When I run I get the error "jsonResponse is undefined" for the line var bitlyUrl = jsonResponse.results[url].shortUrl;. Am I doing anything wrong in parsing JSON here? Or what is wrong with this code?
New ways I: fetch
TL;DR I'd recommend this way as long as you don't have to send synchronous requests or support old browsers.
A long as your request is asynchronous you can use the Fetch API to send HTTP requests. The fetch API works with promises, which is a nice way to handle asynchronous workflows in JavaScript. With this approach you use fetch() to send a request and ResponseBody.json() to parse the response:
fetch(url)
.then(function(response) {
return response.json();
})
.then(function(jsonResponse) {
// do something with jsonResponse
});
Compatibility: The Fetch API is not supported by IE11 as well as Edge 12 & 13. However, there are polyfills.
New ways II: responseType
As Londeren has written in his answer, newer browsers allow you to use the responseType property to define the expected format of the response. The parsed response data can then be accessed via the response property:
var req = new XMLHttpRequest();
req.responseType = 'json';
req.open('GET', url, true);
req.onload = function() {
var jsonResponse = req.response;
// do something with jsonResponse
};
req.send(null);
Compatibility: responseType = 'json' is not supported by IE11.
The classic way
The standard XMLHttpRequest has no responseJSON property, just responseText and responseXML. As long as bitly really responds with some JSON to your request, responseText should contain the JSON code as text, so all you've got to do is to parse it with JSON.parse():
var req = new XMLHttpRequest();
req.overrideMimeType("application/json");
req.open('GET', url, true);
req.onload = function() {
var jsonResponse = JSON.parse(req.responseText);
// do something with jsonResponse
};
req.send(null);
Compatibility: This approach should work with any browser that supports XMLHttpRequest and JSON.
JSONHttpRequest
If you prefer to use responseJSON, but want a more lightweight solution than JQuery, you might want to check out my JSONHttpRequest. It works exactly like a normal XMLHttpRequest, but also provides the responseJSON property. All you have to change in your code would be the first line:
var req = new JSONHttpRequest();
JSONHttpRequest also provides functionality to easily send JavaScript objects as JSON. More details and the code can be found here: http://pixelsvsbytes.com/2011/12/teach-your-xmlhttprequest-some-json/.
Full disclosure: I'm the owner of Pixels|Bytes. I thought that my script was a good solution for the original question, but it is rather outdated today. I do not recommend to use it anymore.
You can simply set xhr.responseType = 'json';
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts/1');
xhr.responseType = 'json';
xhr.onload = function(e) {
if (this.status == 200) {
console.log('response', this.response); // JSON response
}
};
xhr.send();
Documentation for responseType
Note: I've only tested this in Chrome.
it adds a prototype function to the XMLHttpRequest .. XHR2,
in XHR 1 you probably just need to replace this.response with this.responseText
Object.defineProperty(XMLHttpRequest.prototype,'responseJSON',{value:function(){
return JSON.parse(this.response);
},writable:false,enumerable:false});
to return the json in xhr2
xhr.onload=function(){
console.log(this.responseJSON());
}
EDIT
If you plan to use XHR with arraybuffer or other response types then you have to check if the response is a string.
in any case you have to add more checks e.g. if it's not able to parse the json.
Object.defineProperty(XMLHttpRequest.prototype,'responseJSON',{value:function(){
return (typeof this.response==='string'?JSON.parse(this.response):this.response);
},writable:false,enumerable:false});
I think you have to include jQuery to use responseJSON.
Without jQuery, you could try with responseText and try like eval("("+req.responseText+")");
UPDATE:Please read the comment regarding eval, you can test with eval, but don't use it in working extension.
OR
use json_parse : it does not use eval
Use nsIJSON if this is for a FF extension:
var req = new XMLHttpRequest;
req.overrideMimeType("application/json");
req.open('GET', BITLY_CREATE_API + encodeURIComponent(url) + BITLY_API_LOGIN, true);
var target = this;
req.onload = function() {target.parseJSON(req, url)};
req.send(null);
parseJSON: function(req, url) {
if (req.status == 200) {
var jsonResponse = Components.classes["#mozilla.org/dom/json;1"]
.createInstance(Components.interfaces.nsIJSON.decode(req.responseText);
var bitlyUrl = jsonResponse.results[url].shortUrl;
}
For a webpage, just use JSON.parse instead of Components.classes["#mozilla.org/dom/json;1"].createInstance(Components.interfaces.nsIJSON.decode