Send custom cookies to another domain using JavaScript POST in Chrome - javascript

I want to send custom cookie in POST request to another domain (my domain) from localhost
I set cookie by document.cookie="test=test";
and i can see its set correctly by console.log(document.cookie) , Now When i use following code cookie is not sent though.
$.ajax({
url: 'https://secure.domain.com',
type: 'POST',
data: "hi",
cache: false,
contentType: false,
processData: false,
xhrFields: {
withCredentials: true
},
crossDomain: true
});
I even disabled chrome security by running following command
-args --disable-web-security --user-data-dir
Only following headers are sent
Accept: */*
Content-Type: text/plain;charset=UTF-8
Origin: http://localhost:8888
Referer: http://localhost:8888
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.30 Safari/537.36
Note : This is only for my personal use , so i can disable chrome security or modify anything for my use.

modify chromium source code is bad idea, for this task you can just create extension to modify request headers, and no need argument -disable-web-security
Create folder with name like headers_ext and add the following files
manifest.json
{
"manifest_version": 2,
"name": "Modify Request Headers",
"version": "1.0",
"permissions": [
"webRequest",
"webRequestBlocking",
"<all_urls>",
"tabs",
"webNavigation"
],
"background": {
"scripts": ["background.js"]
}
}
backround.js
function modifyRequestHeaders(request) {
for (var headers = request.requestHeaders, i = 0; i < headers.length; ++i) {
if (headers[i].name.toLowerCase() == 'accept') {
// set Cookie from 'Accept' header value
headers.push({"name" : "Cookie", "value" : headers[i].value});
// normalize 'Accept' header value
headers[i].value = '*/*';
}
}
return {requestHeaders: headers};
}
function modifyResponseHeaders(response) {
for (var headers = response.responseHeaders, i = 0; i < headers.length; ++i) {
if (headers[i].name.toLowerCase() == 'access-control-allow-origin') {
headers.splice(i, 1);
break;
}
}
// Allow cross domain
headers.push({"name": "Access-Control-Allow-Origin", "value": "*"});
return {responseHeaders: headers};
}
var webRequestOptions = {urls: ["<all_urls>"], types: ["xmlhttprequest"]};
chrome.webRequest.onBeforeSendHeaders.addListener(modifyRequestHeaders,
webRequestOptions, ["blocking", "requestHeaders", 'extraHeaders']);
chrome.webRequest.onHeadersReceived.addListener(modifyResponseHeaders,
webRequestOptions, ["blocking", "responseHeaders"]);
Now, in Chrome extension page click Load unpacked extension and locate the directory.
the extension above will only modify xmlhttprequest request headers and use Accept header value for Cookie value, It also modify response header to allow cross domain request by adding header Access-Control-Allow-Origin: *.
It seem for Chrome that DPR, Downlink, Save-Data, Viewport-Width, Width headers is not yet in safe-listed so I use Accept header instead to avoid OPTIONS or Preflight request, because many website doesn't support this. And extraHeaders is filter to allow modify or create Cookie.
For more CORS information read here
Make sure you're using latest Chrome and create request like this
$.ajax({
url: 'https://example.com',
type: 'POST', // or GET or HEAD
headers: {
// it will used for 'Cookie' value by extension
'Accept': "cookieName=cookieValue"
}
});

This behaviour depends on the client - in case of Chrome, Cookie header is forbidden when used with a XMLHttpRequest and it seems that it cannot be overriden by any command line flag.
Looking at Chromium source code, this is the fragment responsible for it:
// "5. Terminate these steps if |name| is a forbidden header name."
// No script (privileged or not) can set unsafe headers.
if (FetchUtils::IsForbiddenHeaderName(name)) {
LogConsoleError(GetExecutionContext(),
"Refused to set unsafe header \"" + name + "\"");
return;
}
This method will be called whenever you will call XMLHttpRequest.setRequestHeader(header, value) with Cookie as header parameter and this is what jQuery's $.ajax({}) uses under the hood.
For more information on why this behavior may be disabled by some clients, see this answer.
Here is the complete list of forbidden header names:
ForbiddenHeaderNames::ForbiddenHeaderNames()
: proxy_header_prefix_("proxy-"), sec_header_prefix_("sec-") {
fixed_names_ = {
"accept-charset",
"accept-encoding",
"access-control-request-headers",
"access-control-request-method",
"connection",
"content-length",
"cookie",
"cookie2",
"date",
"dnt",
"expect",
"host",
"keep-alive",
"origin",
"referer",
"te",
"trailer",
"transfer-encoding",
"upgrade",
"user-agent",
"via",
};
}

Related

Content Script - Empty Response from Regular Axios Call in Chrome Extension

When making an axios call in my google content script that's being executed on http://example.com, the response status is a 200 and I'd expect a json response but instead the data is an empty string like this:
response = {data: "", status: 200, statusText: "", headers: {…}, config: {…}, …}
content.bundle.js
const url = "https://jsonplaceholder.typicode.com/todos/1";
var config = {
headers: {
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest',
},
}
axios.get(url, config).then((response) => {
debugger;
console.log(response);
});
manifest.json
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.bundle.js"],
"css": ["content-styles.css"]
}
],
If you're interested in the headers
Request URL: https://jsonplaceholder.typicode.com/todos/1
Request Method: GET
Status Code: 200
Remote Address: 104.24.99.239:443
Referrer Policy: no-referrer-when-downgrade
access-control-allow-credentials: true
Provisional headers are shown
Accept: application/json, text/plain, */*
Referer: http://example.com/
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.80 Safari/537.36
X-Requested-With: XMLHttpRequest
I'm able to get around the issue by sending a message to the background script, ideally could avoid this extra step.
Thoughts? Happy to provide more info if needed.
Thanks

How can I remove the CORB warning?

Chrome was working until version 73. Now it is throwing me a CORB warning and stopping my chrome extension from running.
Here is my ajax jquery code, nothing special
$.ajax({
url: this.url + "api/users",
type: 'get',
data: { account_id: this.account_id(), user_id: this.user_id(), person_id: person_id },
success: function (data) {
//do stuff
}
});
I did notice that if I remove the x-content-type-options header so that it no longer reads "nosniff" I can get some Ajax requests to be returned but not others. Not sure if this means anything but I noticed that the json requests that returned an array worked but others did not.
remove_keys = %w(X-Content-Type-Options)
response.headers.delete_if{|key| remove_keys.include? key}
[{'id' : '123'}] <-worked
{'id' : '123'} <- did not work (not sure if means anything)
Full error from chrome
Cross-Origin Read Blocking (CORB) blocked cross-origin response https://ideas.test/api/users?token=W9BDdoiKcXLWSHXWySnwdCV69jz2y&account_id=3098355&user_id=john%40gmail.com&person_id=21046915&sync=false&new=true with MIME type application/json. See https://www.chromestatus.com/feature/5629709824032768 for more details.
Headers from response
Access-Control-Allow-Credentials: true
Access-Control-Allow-Headers: x-auth_token
Access-Control-Allow-Methods: GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD
Access-Control-Allow-Origin: chrome-extension://mhikhjencpecbhelhjgdcgpdhlhdlhjh
Access-Control-Expose-Headers:
Access-Control-Max-Age: 1728000
Request Headers
Provisional headers are shown
Accept: */*
Origin: chrome-extension://mhikhjencpecbhelhjgdcgpdhlhdlhjh
Referer: https://3.basecamp.com/
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6)
AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.75 Safari/537.36
How can I get the response body to be returned without chrome removing the body due to CORB?
I found a workaround. Might be an overkill for someone, but it took me 15 mins to fix everythiung. In your content script wrap all your ajax calls into a function:
Add ajaxGet function to your content script:
function ajaxGet(data){
return new Promise(function (resolve, reject) {
chrome.runtime.sendMessage({action: 'ajaxGet', data: data}, function (response) {
console.log(response)
if(response&&!response.statusText){//Might need some work here
resolve(response);
} else {
reject(response)
}
});
});
}
And in your background.js add a listener:
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
if(request.action=="ajaxGet"){
$.ajax(request.data).then(sendResponse,sendResponse)
return true //telling chrome to wait till your ajax call resolves
}
})
in stead of
$.ajax({
url: this.url + "api/user_boards",
type: 'get',
data: { account_id: this.account_id()}
})
call
ajaxGet({
url: this.url + "api/user_boards",
type: 'get',
data: { account_id: this.account_id()}
}).then(onSuccess, onError) //handle response from here
If you don't want to use jquery in your background.js you can make Xhr call in stead. Something like this:
var data = JSON.stringify(false);
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.addEventListener("readystatechange", function () {
if (this.readyState === this.DONE) {
console.log(this.responseText);
sendResponse(this.responseText)
} else {
//handle errors
}
});
xhr.open("GET", request.data.url);
xhr.send(data);
You'll have to work around headers on your own.
It looks like you're putting the CORS headers in the request. You need to put them in the response instead.
Chrome 73 inject some new security. Just try to move your xHTTP requests to your background script with chrome.runtime.sendMessage and get response with SendResponse callback.
In content or popup script replace ajax with :
chrome.runtime.sendMessage(
{ action: "check", data: {/* params for url */}},
// callback with url response
function(response) {
if( response.success ) {
var myDataFromUrl = response.data;
...
} else {
console.log('Error with `check`,', response.data);
}
}
);
From background script:
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
var url = 'https://mysyte.com/';
if(request.action === 'check' ) {
url = url + 'check'
ajax( url, request.data,
success: function( d ) {
sendResponse({success: true, data: d});
},
error : function( d ) {
sendResponse({success: false, data: d});
}
);
}
});
function ajax( url, params, cbSuccess, cbError ) { ... }
After fixing the CSP & CORS issues, I was still getting the warning on the OPTIONS method call (which is done for cross-domain calls).
I fixed it on the server by setting the content-type for the OPTIONS method call (which doesn't return any data) to "application/octet-stream". No more warnings!

Sending 'Authorization' header in Ext JS Ajax Request, Chrome vs. Firefox

In code the request is sent as:
var ajaxOptions = {
url: 'someUrl',
method: 'POST',
success: function(response) {
...
},
failure: function(response) {
console.log(response);
}
};
var form = this.getView().getForm();
var submitAction = Ext.create('Ext.form.action.Submit', { form: form });
var formInfo = submitAction.buildForm();
ajaxOptions.form = formInfo.formEl;
if (form.hasUpload()) {
ajaxOptions.isUpload = true;
}
Ext.Ajax.request(ajaxOptions);
When a request is sent via Chrome, the 'Authorization' header presents:
Authorization:Basic YWRtaW46YWRtaW4=
When it is sent via Firefox, the header is not included.
Explicitely I don't set user/password. So it's not clear, why and how chrome sends such header. Are there any known issues?
The second, how to force firefox to send such header? Is it possible?
UPDATED
JavaScript does not now anything about login/password. The main question, how Chrome can use them, but other browsers cannot send such pair. So the question is how to force other browsers to send this cookie as Chrome does without appling headers manually via JavaScript.
On the server side, the Servlet API is used. in web.xml:
<login-config>
<auth-method>BASIC</auth-method>
<realm-name>someName</realm-name>
</login-config>
if it does matter
Per the docs, just add a headers property to your ajaxOptions:
ajaxOptions = {
//...
headers : {
'Authorization': 'Basic YWRtaW46YWRtaW4='
}
}

File upload with blueimp jQuery file upload, doesn't work in IE9

have create an upload functionality with jQuery-file-upload, my upload work in Firefox, Chrome and IE version more than 9.
I have try to force iframe transport, recommended by doc of jQuery-fileupload plugin.
Fileupload do his tasks, but he send an empty object. So, symfony send an error about file.
My upload process work like this: Backbone.js + require.js -> symfony -> rackspace
$(selector).fileupload({
dataType: 'json',
autoUpload: true,
url: 'http://' + IM('site').get('domain') + '/' + window.PadawanSite.envPath + '/upload/adpost/img',
forceIframeTransport: true,
add: function (e, data) {
var id = e.target.id.substring(e.target.id.length - 2),
jqXHR = data.submit()
.success(function (result, textStatus, jqXHR) {
images.push({
id: id,
original: result.publicUrl.original,
large : result.publicUrl.large,
medium : result.publicUrl.medium,
small : result.publicUrl.small
});
})
.error(function (jqXHR, textStatus, errorThrown) {
$('#picture_callback_' + id).removeClass('glyph icon-spinner2 icon-refresh-animate').addClass('glyph-05 icon-x-empty');
})
.complete(function (result, textStatus, jqXHR) {
if (textStatus === "success") {
var ad = IM('ad').toJSON();
ad.images = images;
IM('ad').clear().set(ad);
IM('ad').save(ad);
if (IM('ad').toJSON()) {
$('#adpost_picture_img_' + id).attr("src", result.responseJSON.publicUrl.small);
$('#picture_callback_' + id).removeClass('glyph icon-spinner2 icon-camera icon-x-empty icon-refresh-animate').addClass('glyph-05 icon-v');
$('#adpost_picture_visualize').removeAttr('disabled');
}
}
});
$('#adpost_picture_visualize').attr('disabled', 'disabled');
$('#picture_callback_' + id).removeClass('glyph-05 icon-camera icon-x-empty').addClass('glyph icon-spinner2');
$('#picture_callback_' + id).addClass('icon-refresh-animate');
$('#adpost_picture_name_' + id).empty();
$('#adpost_picture_name_' + id).append(data.files[0].name);
}
});
My request headers, it sent to symfony. But you can see a content-length equal to 0. So it's the problem, but why he do that ?
X-Requested-With: XMLHttpRequest
Host: sjus.local
Request: POST /app_dev.php/upload/adpost/img HTTP/1.1
Cache-Control no-cache
User-Agent Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko
Connection Keep-Alive
Referer http://sjus.local/app_dev.php#postad/picture
Accept-Encoding gzip, deflate
Accept-Language en-us
Content-Length 0
Accept */*
This is a know restriction on IE8/9 when you're doing a cross-domain file upload as the plugin's documentation site says.
I faced this problem once, specifically I was getting the "Permission denied" error when the plugin was trying to retrieve the response from the iframe, this was because in my case the site was a subdomain in the same domain as the api. So I was able to use the option "initialIframeSrc" to specify the document.domain to be used in the iframe (lucky me). I did something like this:
dataType: 'text',
forceIframeTransport: true,
initialIframeSrc: "javascript:document.write('<script>document.domain=\"mydomain.com\";</script>')",
This way the plugin was able to retrieve the response from the iframe contents.
Hope this helps.

Jquery File Upload - Not sending headers in IE9

I'm using jQuery Fileupload to upload files. Its not sending headers that I set to the server. Why is the Authorization header missing only in IE but passed in chrome?
Here is the code:
upload_photo: function(){
var url = '/api/v1/upload';
$('#photoupload').fileupload({
url: url,
dataType: 'json',
paramName: 'uploadFile',
beforeSend: function ( xhr ) {
setHeader(xhr);
$("#check_progress").html('true');
},
done: function (e, responseJSON) {
var id = responseJSON.result.id;
url = responseJSON.result.url;
var photo_ids = $("#photo_ids");
var val = photo_ids.val();
photo_ids.val(val + id.toString() + ",");
$(".photothumb-wapper").append('<div class=\"photothumb\" id="post_photo_'+id+'"><div><img src=\"'+url+'\" /></div><img class=\"thumb-delete photo_delete\" id=\"'+id+'\" title=\"Remove\" src=\"/assets/delete-red.png\"></div>');
$("#check_progress").html("");
},
start: function (e, data) {
$(".photothumb-wapper").append('<div class="photothumb photoprogress" style="border:none"><img src="/assets/ajax-loader.gif" /></div>');
},
always: function (e, data) {
$(".photoprogress").remove();
}
});
}
var setHeader = function (xhr) {
xhr.setRequestHeader('Authorization', 'Bearer fdf49c4f1cfgc176eb952f18eeefaec3e7');
};
Headers passed in IE:
Request : POST /api/v1/upload HTTP/1.1
Accept : text/html, application/xhtml+xml, \*/\*
Referer : url
Accept-Language : en-US
User-Agent :Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Trident/5.0)
Content-Type :multipart/form-data; boundary=---------------------------7de2dfe037204f6
Accept-Encoding :gzip, deflate
Host :url
Content-Length :776595
DNT :1
Connection :Keep-Alive
Cache-Control :no-cache
Cookie :sitecookies
Headers passed in Chrome:
ResponseHeaders
date : Tue, 04 Mar 2014 07:32:20 GMT
Connection: Keep-Alive
content-length:225
content-type:application/json; charset=utf-8
cache-control:no-cache
RequestHeaders
Accept: application/json, text/javascript, \*/\*; q=0.01
Authorization: Bearer fdf49c4f1cfgc176eb952f18eeefaec3e7
X-Requested-With: XMLHttpRequest
Why is the Authorization header missing in IE?
This answers my question,
Only browsers with support for XHR file upload support setting custom headers.
As a workaround in old browsers like our dear IE, you could set a cookie with the authentication token when the user authenticate and then get it in the server and verify it the same way you verify the header one. I know that it is not the most elegant solution but it works.

Categories

Resources