5

So i want to send a XMLHttpRequest POST request through an iFrame on page load. Reason for posting via an iFrame is to not show referrer.

Javascript:

function load() {
var http = new XMLHttpRequest();
var url = "action url here";
var params = "name1=one&name2=two";
http.open("POST", url, true);
http.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=UTF-8");
http.send(params);
}

HTML:

<body onload="load();">
<iframe name="f1" src="about:blank" id="noreferer" width="0px" height="0px" style="border: 0px none;"> </iframe>
</body>

How can i attach the Request to the iFrame. Any help would be appreciated.



UPDATE:

For anyone asking why i added and fired load(); in the HTML body, Below is a no referrer post request code connected to the f1 iframe via innerHTML that works in all browsers because the src is 'about blank'. But not a XMLHttpRequest and doesn't give ability to add headers.

Javascript:

function load() {
var postdata = '<form id=NoReferrerPost method=POST action=\'action url here\'>' +
                '<input type=hidden name=name1 value=one />' +
                '<input type=hidden name=name2 value=two />' +
                '</form>';
top.frames['f1'].document.body.innerHTML=postdata;
top.frames['f1'].document.getElementById('NoReferrerPost').submit();
}

So what is still needed is a way to attach a XMLHttpRequest to post in iframe f1 like my code above.


HERE ARE SOME SOLUTIONS THAT PARTIALLY WORKS:

The solution of @fedeghe HERE using src="data:text/html with no-referrer meta tag, could work on some browsers.

Julius
  • 387
  • 6
  • 15
  • 1
    if you call the `load` function from the body `onload` this means that the script is defined in the same page where that `body` tag lives... then I have one basic question... what exactly makes you think that the `iframe` has something to deal with the `load` function? – fedeghe Jul 17 '18 at 12:46
  • "How can i attach the Request to the iFrame." Why would you need to attach it to the iframe? – epascarello Jul 17 '18 at 13:37
  • @epascarello — Look at the first paragraph of the question. – Quentin Jul 17 '18 at 13:37
  • Totally missed that and I highly doubt doing it will work.... lol – epascarello Jul 17 '18 at 13:38
  • @fedeghe I just updated the question to answer your initial question on why i fired load function from the body onload. What was needed was possible a connector, hence the reason for the onload function. – Julius Jul 21 '18 at 12:55
  • @Julius maybe You could still set the src of the iframe to the complete `data:text/html ... ` huge attribute from within the onload handler? – fedeghe Jul 22 '18 at 21:01
  • @fedeghe Could you give a sample on what you meant by setting src of iframe to complete `data:text/html...`? – Julius Jul 23 '18 at 08:00
  • @Julius sure, l update my answer unfolding what I mean – fedeghe Jul 23 '18 at 08:45
  • @Julius sorry but "could work on some browsers." is a lot pessimistic cause it's not working just on opera mini....here the half glass is full, not empty – fedeghe Jul 24 '18 at 08:49
  • @fedeghe, Click on Show All. It's not just opera mini. For example, Windows 7 by default comes with IE8, but this won't work for IE10 downward. And not all users would update to IE11. – Julius Jul 24 '18 at 10:51
  • ohh got it.. just looked the first view :) – fedeghe Jul 24 '18 at 11:06

3 Answers3

2

Using the src="data:text/html,, you can do it but you have to take care at least about the encoding of the script

<iframe src="data:text/html,<html><head><meta name=%22referrer%22 content=%22no-referrer%22/></head><body><script>(function(){var http = new XMLHttpRequest(), url = %22http://www.yourTargetDomain.com%22, params = %22name1=one%26name2=two%22; http.open(%22POST%22, url, true); http.setRequestHeader(%22Content-type%22, %22application/x-www-form-urlencoded; charset=UTF-8%22); http.send(params);})(); </script></body></html>"
  width=0 height=0 style="display:none;"></iframe>

You can read more details about it here and here

UPDATE onload

in case you really need to do it at some point, userORbrow event driven you could do something like the following (replaced also the name attribute with id for the iframe tag, but doesn't really matter):

<script>
function load () {
    var targetDomain = "http://www.yourTargetDomain.com",
        params = "name1=one%26name2=two",
        html = '<html><head><meta name="referrer" content="no-referrer"/></head><body><script>(function(){var http = new XMLHttpRequest(), url = "' + targetDomain + '", params = "' + params + '"; http.open("POST", url, true); http.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=UTF-8"); http.send(params);})(); <\/script><\/body><\/html>',
        src = "data:text/html;charset=utf-8,"+ escape(html);
    document.getElementById('f1').src= src;
}
</script>
<body onload="load();">
    <iframe id="f1" src="about:blank" width="0px" height="0px" style="border: 0px none;"> </iframe>
</body>

...at that point I suggest that Your function could easily: first create the iframe (about:blank src, hidden ...an on), second append it, then trigger the post, wait for request success (maybe also consume it), and remove finally the iframe from the dom, something similar to:

<script>
    function postIt (p, url, cb) {
        url = url ||  "http://www.targetdomain.org";
        p = p || {};
        cb && (p.cb = +new Date);

        var params = (function (o) {
                var s=[];
                for (var j in o) {
                    o.hasOwnProperty(j) && s.push(j+'='+o[j]);
                }
                return s.join('%26');
            })(p),
            html = '<html><head><meta name="referrer" content="no-referrer"/></head><body><script>(function(){var http = new XMLHttpRequest(), url = "' + url + '", params = "' + params + '";http.onreadystatechange = function(){if (http.readyState == 4 && http.status == 200){window.parent.postMessage("posted", "*");}};http.open("POST", url, true); http.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=UTF-8"); http.send(params);})(); <\/script><\/body><\/html>',
            src = "data:text/html;charset=utf-8,"+ escape(html),
            iframe = document.createElement('iframe');
        iframe.style.display= 'none';
        iframe.height = iframe.width = '0px';
        window.addEventListener('message', function (e) {
            e.data == "posted" && document.body.removeChild(iframe);
        }, false);
        iframe.src = src;
        document.body.appendChild(iframe);
    }
</script>
<body onload="postIt({hello:'there'});">
    <h1>Hello world</h1>
</body>
fedeghe
  • 1,243
  • 13
  • 22
  • This code shows the referrer. For a referrer to not show, the logic was to make the default iframe src be about:blank. i.e `src="about:blank"` before the POST request. – Julius Jul 17 '18 at 14:43
  • @Julius ...strange, the origin in the request headers of the xhr request are null... at least on the brows I tested – fedeghe Jul 17 '18 at 15:01
  • adding `` to the encoded iframe content in the header could solve it. I updated the answer – fedeghe Jul 17 '18 at 15:03
  • While no-referrer meta could solve this in [some browsers](https://caniuse.com/#feat=referrer-policy), There are browsers incompatible with it that would still display referrer. Like IE and some old android browsers. – Julius Jul 17 '18 at 18:03
  • @Julius altough most likely there is no far behind in versions working solution, the caniuse link you reported highlights only opera mini...are You worried :) about that or by others bro older versions? – fedeghe Jul 18 '18 at 07:09
  • @Julius could You answer to the first comment I replied to Your the original question? – fedeghe Jul 18 '18 at 07:15
  • I just did and updated the question to give a sample code to that effect. I also added your no-referrer meta tag answer as a work around for some browsers. – Julius Jul 21 '18 at 13:01
2

If you're only intent is to not send the referer, then you can mention it using the referrer-policy. In the html of your webpage just add the meta information (source):

<meta name="referrer" content="no-referrer" />

Just create a the requeired html and serve it locally and test it on your machine.

$ cat untitled.html
<!DOCTYPE html>
<head>
     <meta name="referrer" content="no-referrer" />
</head>
<body>
    <p> SOME CONTENT </p>
    <script>
        (function(){
            var http = new XMLHttpRequest();
            var url = "https://demo6945017.mockable.io/random/post"
            http.open("POST", url, true);
            http.setRequestHeader("Content-type", "application/x-www-form-urlencoded; charset=UTF-8");
            http.send();
        })();
    </script>
</body>

$ python -m http.server

On testing it, you can see the request headers with and without the referrer-policy.

(Without the meta info -> With referrer)

POST /random/post HTTP/1.1
Host: demo6945017.mockable.io
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:61.0) Gecko/20100101 Firefox/61.0
Referer: http://localhost:8000/untitled.html
Content-type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://localhost:8000

===================================

(With the meta info -> Without referrer)

POST /random/post HTTP/1.1
Host: demo6945017.mockable.io
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.13; rv:61.0) Gecko/20100101 Firefox/61.0
Content-type: application/x-www-form-urlencoded; charset=UTF-8
Origin: http://localhost:8000

(I've removed a lot of the common headers like content-length and cache-control for redablilty)
But beware that the origin is still being sent on every request and that cannot be changed, it tied to the behaviour of the browser. Also browser support is limited to Chrome and Firefox.

TheChetan
  • 4,440
  • 3
  • 32
  • 41
  • This solution will apply to all page request instead of the specific iFrame request. @fedeghe answer https://stackoverflow.com/a/51382768/1777938 is also a no-referrer meta tag workaround, but for the specific iFrame and as stated earlier, this only works in some browsers. – Julius Jul 21 '18 at 13:10
1

Using onload in <body> or in <iframe> tag doesn't change the referrer because the code is still fired from the parent window.

You should fire the ajax within the iframe. To put code in the iframe from a parent window like this example:

frames[0].window.foo = function(){
   console.log ("Look at me, executed inside an iframe!", window);
}

you have to observe Same-origin policy, but in this case, since you want to change referrer, you will have the same origin and referrer, then referrer is not going to change.

Also, browsers set the referrer in ajax requests (see this and this) despite of the fact that exists a referrer header to manipulate the referrer behavior. Somethig like <meta name="referrer" content="no-referrer" /> unfortunately has some important restrictions (no Edge, IE and Safari support).

So I think a possible way to change or hide referrer is through some kind of server proxy. This is: redirecting all ajax requests to one server controlled by you, in the server you perform a POST/GET request and in this case hiding the referrer. It's really simply in fact, here an example using php (CURL) and javascript:

myproxy.php:

<?php
$url = $_GET['url'];
$ch = curl_init(); 
curl_setopt($ch, CURLOPT_URL, $url); 
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_REFERER, "referrer you want");//<-- spoof referrer here 
$output = curl_exec($ch); 
curl_close($ch);
echo $content;//content is from $_GET['url'] passed from javascript ajax
?>

in javascript

var url = "Url we want to hide the referrer";
    var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
     //response from myproxy.php here
    }
  };
  xhttp.open("GET", "myproxy.php?="+url , true);
  xhttp.send();
Emeeus
  • 5,072
  • 2
  • 25
  • 37
  • I have updated the question to answer your questions, with a code that POST normal request without sending referrer which works on any browser, and also added a workaround for XMLHttpRequest POST with no-referrer meta tag. And like you said, that doesn't work for all browsers. – Julius Jul 21 '18 at 12:59
  • @Julius I've updated the answer and I added an example of a little server proxy in case you could use server side code. – Emeeus Jul 23 '18 at 13:56
  • Seen. However, the issue with curl approach is that, it uses the server IP instead of User IP. – Julius Jul 23 '18 at 14:43
  • @Julius Yes, and unfortunately we can't spoof IP. – Emeeus Jul 23 '18 at 14:50
  • @Julius have you seen this http://jpgerek.github.io/referrer-killer/example.html ? I didn't try – Emeeus Jul 23 '18 at 15:14
  • Sure. Normal Request is solved with iframe `src=about:blank` but not for XMLHttpRequest. I already wrote a code for normal request in my update. – Julius Jul 23 '18 at 15:21