Javascript, track iframes redirecting top window

2019-03-23 18:55发布

问题:

Since there is no way to prevent an iframe from redirecting the top frame besides sandboxing which prevents other features required for viewability tracking I would like to track redirects. Since one site can have more than one iframe, it could be any of these.

Is there any way to track/find out which one (specific iframe) caused the top frame redirect?

Here is a sandbox (use browser console and enable preserve log):

Note the iframe content is usually cross domain. For ease of use its within the sandox.

回答1:

We can access to the iframe content with somethig like iframe.contentWindow.document but this is possible if we observe Same-origin policy.

Another approach could be setting a Content-Security-Policy header like:

<meta http-equiv="Content-Security-Policy" content="frame-src http://example.com">

This header in the parent page prevents to load sites different to http://example.com in frames, There is also a way to report the refuse behavior sending a post but unfortunately can't be setting with <meta> tag (it's only server side). With this approach we have to perform a white list, so I think maybe it's not useful in this case. But, if the white list is given the first time, is possible to set all sites available, so when the iframe redirect, browser will refuse to load it.

If it's not the case of same-origin and the possibility of performing a white list, then I think the better we can do is calling iframe onunload event, unfortunately this event are going to be fired also when iframe page reloads not only on redirection. I think it's the closest approach. To achieve that, this code works.

var srcs = ["iframe2.html","iframe.html","iframe2.html"];


        for (let i = 0; i < srcs.length; i++) {
            var iframe = document.createElement('iframe'); 
                iframe.src = srcs[i]; 
                iframe.name = "i"+i; 
                document.body.appendChild(iframe);
                window["i"+i].onunload = function(){console.log("change "+i)}
        }

Of course onunload is fired the first time, when all iframes load, so redirections are 2th 3th and so on. But we could exclude that first case.

Here a full example https://codesandbox.io/s/o16yk7mqy , I've created iframe3.html that doesn't refresh neither reload to show clearly the point. Also I've created a simple List of redirect or reload iframes.

UPDATE As I understand now, what you want is to set iframes with sandbox property and whitelist all what you want but without allow-top-navigation, something like:

<iframe src="iframe.html" sandbox="allow-script allow-forms allow-popups allow-pointer-lock allow-same-origin"></iframe>
  • This Example doesn't allow allow-top-navigation https://codesandbox.io/s/lpmv6wr6y9
  • This Example here https://codesandbox.io/s/4x8v1mojq7 allow allow-top-navigation but codesandbox prevents the frame to redirect so if we try https://4x8v1mojq7.codesandbox.io/ that is the url created by codesandbox, we could see the top frame reload.

As I said in comments, at least Chrome 64.0.3282.167, when we delegate all but allow-top-navigation when the iframe attempt to redirect top frame, it throw an exception. The behavior is different in Firefox (at least 58.0.2). Firefox deny top navigation but continues with the code.

So, as conclusion the best approach in my opinion is or a combination of sanbox and onunload or just onunload. Of course, if it could be possible, Content-Security-Policy is the safest and more flexible way. It depends of the implementation. It's almost impossible I think not to involve server side code to perform a perfect solution. There are white list to check, like this API https://developers.google.com/safe-browsing/v4/ and there are black list to check, look at this post https://security.stackexchange.com/questions/32058/looking-for-url-blacklists-of-malicious-websites .



回答2:

If you have control over all your frames you can implement interaction between frames with postMessage. Flow:

  1. Frame want to execute redirect - it sends a message to parent frame with redirect request.
  2. Parent frame executing redirect and know which frame caused a redirect.

Parent:

window.addEventListener("message", (event) => {
    // show message source (your frame)
    console.log(event.source);

    const message = event.data;
    console.log(`Frame ID: ${message.frameId}`);

    if(message.messageType === "redirect") {
        window.location.href = message.redirectUrl;
    }
});

Child frame:

function redirect(url) {
    var message = {
        messageType: "redirect",
        frameId: "frame1"
        redirectUrl: url
    }
    window.parent.postMessage(message, "*");
}


回答3:

You can show a dialog box before redirecting to another domain/application and then the user can decide - to stay or leave the current application. You can also track the current target (i.e. iframe in your case).

window.onbeforeunload = function (e) {
  console.log(e.currentTarget.location.href);
  return 'Stop redirection. Show dialog box.';
};


回答4:

From the individual iframes, can you set a cookie when the redirect happens? Say it happened from iframe1, you may set a cookie like

document.trackFrame = "FrameName=iframe1";  

And once the redirect completes, can you try reading the cookie and there by determine which iframe caused the re-direct?