Send custom cookies to another domain using JavaScript POST in Chrome

Viewed 1420

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.

2 Answers

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