Inject javascript at the very top of the page \ Anti iframe-buster - javascript

I'm developing an extension that, sometimes, will show some websites inside iframes. I've already bypassed the X-FRAME-OPTIONS issue but now I'm stuck with the simple iframe buster code, eg.:
if (top != self) {
document.getElementsByTagName("html")[0].style.display = "none";
top.location.replace(location);
}
I'm trying to inject javascript at the very top of the page to override the window.top object, but at document_start is already too late to inject it, ie alert() is never called before the buster script runs:
chrome.webRequest.onCompleted.addListener(function(details) {
if (isEnabled) {
chrome.tabs.executeScript(details.tabId, {frameId: details.frameId, runAt: "document_start", code: "alert('asas');"});
}
}, {
types: ["sub_frame"],
urls: ["<all_urls>"]
});
Is there any way around this?
Thank you

The problem is probably caused by chrome.webRequest.onCompleted.addListener listener being asynchronous
document_start injects code before any DOM is created, so that is not the cause of your problem. I have verified this while playing around and trying to answer this question.
The problem here is that chrome.webRequest.onCompleted.addListener is asynchronous, which means that when the callback (and therefor your chrome.tabs.executeScript) is executed, the browser has already started constructing the DOM.
You can start by injecting the script to all relevant iframes directly using the "content_scripts" in manifest.json instead of using programmatic injection. I haven't verified this, but you could also try injecting the script from a chrome.webRequest.onHeadersReceived listener with the "blocking" option, which allows you to handle the request synchronously. You are probably already listening to onHeadersReceived in order to remove the X-Frame-Options header anyway.
Edit:
Programmatic injection in a blocking onHeadersReceived listener is not possible. Chrome returns an error about lack of permissions - probably because the URL is not known at this point yet (the headers could cause a redirect).

Related

Is there an alternative to preprocessorScript for Chrome DevTools extensions?

I want to create a custom profiler for Javascript as a Chrome DevTools Extension. To do so, I'd have to instrument all Javascript code of a website (parse to AST, inject hooks, generate new source). This should've been easily possible using chrome.devtools.inspectedWindow.reload() and its parameter preprocessorScript described here: https://developer.chrome.com/extensions/devtools_inspectedWindow.
Unfortunately, this feature has been removed (https://bugs.chromium.org/p/chromium/issues/detail?id=438626) because nobody was using it.
Do you know of any other way I could achieve the same thing with a Chrome Extension? Is there any other way I can replace an incoming Javascript source with a changed version? This question is very specific to Chrome Extensions (and maybe extensions to other browsers), I'm asking this as a last resort before going a different route (e.g. dedicated app).
Use the Chrome Debugging Protocol.
First, use DOMDebugger.setInstrumentationBreakpoint with eventName: "scriptFirstStatement" as a parameter to add a break-point to the first statement of each script.
Second, in the Debugger Domain, there is an event called scriptParsed. Listen to it and if called, use Debugger.setScriptSource to change the source.
Finally, call Debugger.resume each time after you edited a source file with setScriptSource.
Example in semi-pseudo-code:
// Prevent code being executed
cdp.sendCommand("DOMDebugger.setInstrumentationBreakpoint", {
eventName: "scriptFirstStatement"
});
// Enable Debugger domain to receive its events
cdp.sendCommand("Debugger.enable");
cdp.addListener("message", (event, method, params) => {
// Script is ready to be edited
if (method === "Debugger.scriptParsed") {
cdp.sendCommand("Debugger.setScriptSource", {
scriptId: params.scriptId,
scriptSource: `console.log("edited script ${params.url}");`
}, (err, msg) => {
// After editing, resume code execution.
cdg.sendCommand("Debugger.resume");
});
}
});
The implementation above is not ideal. It should probably listen to the breakpoint event, get to the script using the associated event data, edit the script and then resume. Listening to scriptParsed and then resuming the debugger are two things that shouldn't be together, it could create problems. It makes for a simpler example, though.
On HTTP you can use the chrome.webRequest API to redirect requests for JS code to data URLs containing the processed JavaScript code.
However, this won't work for inline script tags. It also won't work on HTTPS, since the data URLs are considered unsafe. And data URLs are can't be longer than 2MB in Chrome, so you won't be able to redirect to large JS files.
If the exact order of execution of each script isn't important you could cancel the script requests and then later send a message with the script content to the page. This would make it work on HTTPS.
To address both issues you could redirect the HTML page itself to a data URL, in order to gain more control. That has a few negative consequences though:
Can't reload page because URL is fixed to data URL
Need to add or update <base> tag to make sure stylesheet/image URLs go to the correct URL
Breaks ajax requests that require cookies/authentication (not sure if this can be fixed)
No support for localStorage on data URLs
Not sure if this works: in order to fix #1 and #4 you could consider setting up an HTML page within your Chrome extension and then using that as the base page instead of a data URL.
Another idea that may or may not work: Use chrome.debugger to modify the source code.

Chrome Extension API not firing onChanged event

I am using chrome.downloads.onChanged.addListener to find out when a download completes.
The callback does not fire sometimes, and there is no apparent reason for it.
The code looks like this
chrome.downloads.onChanged.addListener(function (downloadDelta) {...});
and it is in background.js and file is mentioned in background section of manifest. I added console.log(downloadDelta); at the very begining of callback, but it is not fired.
The API is mentioned here
EDIT: Test extension
Most probably this has to do with peculiarities of Event pages - something not working properly when your page gets unloaded.
This is easy to test - remove "persistent": "false" from the manifest.
That said, your test extension does not violate any Event page recommendations and chrome.downloads API does not list any incompatibilities. Therefore, 2 scenarios are possible:
It's quite possible that you are misinterpreting results due to the page being unloaded. For instance, if the page gets unloaded between the callback executing and you opening the devtools for it - the console and all local state will be wiped.
To test for that, make sure to write your diagnostics to persistent storage - chrome.storage API is one option for this.
If you are 100% sure the above is not the case, there may be a bug related to event pages and chrome.downloads. In that case, it should be reported.

javascript google chrome extension iframe cannot examine contentWindow

I am writing a Google Chrome extension. Now I need to examine the contents of an iframe but the content script seems unable to access this content even though the debugger can. The iframe contents are a list of messages I have previously sent to that site. If I put the following statement in the content script, it always returns null:
document.getElementById("td_messages_show").getElementsByTagName("iframe")[0].contentWindow.document;
But if I open the debugger and execute the same command from the command line, it returns "Document" with the appropriate contents. At first I thought it was because the frame wasn't finished loading so I found a snippet like this and tried to use it.
function wait4Iframe2Load() {
// Get a handle to the iframe element
//console.log('Checking for null myFrame');
var myFrame = document.getElementById("td_messages_show").getElementsByTagName("iframe")[0].contentWindow;
if (myFrame!=null)
{
console.log(myFrame);
// Check if loading is complete
if ( myFrame.document.readyState == 'complete' ) {
// The loading is complete, call the function we want executed once the iframe is loaded
console.log('Loading Complete');
//frameContent=document.getElementById("td_messages_show").getElementsByTagName("iframe")[0].contentWindow.document.getElementsByTagName('tbody')[0];
return;
}
else alert(" Frame is Not Loaded");
}
else myFrame = document.getElementById("td_messages_show").getElementsByTagName("iframe")[0].contentWindow;
// If we are here, it is not loaded. Set things up so we check the status again in 100 milliseconds
console.log('Waiting for frame to load...');
window.setTimeout('wait4Iframe2Load()', 100);
}
This simply returns null forever. But while this script is piling up console messages, I can open the debugger and execute the very same command line and it returns a document. Faced with this problem and researching internet answers, it seems it may be some deliberate kind of security issue. Whether it is or isn't, I need to examine the iframe contents and determine what I have written there previously so I can decide what to write there next.
Does anybody have an idea how to solve this problem?
The idea is to inject content script into this iframe and use it to get required information.
As I understand this frame has a specific url known upfront, so you can just inject the script through the manifest (use the all_frames option).
If for some reason you need to dynamically inject it, then there is:
chrome.tabs.executeScript(tabId, {allFrames: true});
It will be injected into both parent and iframe pages. Then inside injected content script you can check whether or not it is running inside the right iframe. For example your dynamically injected content script might look like this (if you inject it though the manifest url checking won't be needed):
if(window.location.href == "http://iframe.example.com/" && window != window.top) {
//we are in the right page that is embedded as iframe, do stuff
} else {
//do nothing
}
Excuse me, have you got solution, Jerome? I am having same problem as yours. But I can not make comment on your post. So please do not mind because this is a question...
Edited:
Finally I got it working. I don't understand while flag all_frames: true in manifest.json is not affected. I need to code as #serg's:
chrome.tabs.executeScript(tabId, {allFrames:true, file:"content_script.js"})
Thank you all, and could you please accept #serg's answer as the right one, Jerome? :-)
I created this library:
https://github.com/attachmentsme/Queuebert
to simplify the communication between browser tabs, the extension's background process, and iframes.
The use-case that I was running into was:
an action is taken in one iframe.
the results should be displayed in an alternate iframe.
Doing this can be a pain, hence the abstraction I built.

Call Javascript Function in Child iFrame with Cross Domain site but Same location JS file

I am trying to do the following:
Main document calls a function in iFrame whose URL is from a different location but the Javascript function I'm trying to call loaded from the same domain as the main document.
Is there any way to do this?
To clarify:
Main document: http://www.main.com
iFrame document: http://www.example.com
JS function i'm calling in iFrame is at http://www.main.com/js/script.js
I'm getting
Permission denied to access property 'js_function'
When doing
document.getElementById("iframe").contentWindow.js_function(n)
Even though the script is hosted on main.com it is executed in the context of example.com and therefore is considered to be part of example.com ... and therefore has no access to variables or functions in the main.com window. You can hack around this with various cross domain communication hacks (or you can ignore IE < 8 and use window.postMessage by itself).
SEE ALSO: http://stevehanov.ca/blog/index.php?id=109
I see what you're doing. There was a "hack" that made use of two iframes (if I remember correctly).
Both that hack and the one you mention here are awfully obscure, and I wouldn't be surprised if they have been locked down knowingly.
The best fix I can think of is to load the code for js_function() in the main window (outside of the iframe).
Can you be more specific on what the JS code does? I may be able to help better.
Use easyXDM's RPC feature, it combines XDM with RPC.
An example of this can be seen here: http://consumer.easyxdm.net/current/example/methods.html

Cross domain iframe content load detection

I have a rather interesting problem. I have a parent page that will create a modal jquery dialog with an iframe contained within the dialog. The iframe will be populated with content from a 3rd party domain. My issue is that I need to create some dialog level javascript that can detect if the content of the iframe loaded successfully and if it hasn't within a 5 second time frame, then to close the dialog and return the user to the parent page.
I have researched numerous solutions and only two are of any true value.
Get the remote site to include a javascript line of document.domain = 'our-domain.com'.
Use a URL Fragment hack, but again I would need the request that the remote site
able to modify the URL by appending '#some_value' to the end of the URL and my dialog window would have to poll the URL until it either sees it or times out.
Are these honestly the only options I have to work with? Is there not a simpler way to just detect this?
I have been researching if there's a way to poll for http response errors, but this still remains confined to the same restrictions.
Any help would be immensely appreciated.
Thanks
The easiest way (if you can get code added to the external sites) is to have them add an invisible iframe pointing to a special html file on your domain. This could then use parent.parent.foo() to notify the original window about the load event.
Listening for the "load" event will only tell you if the window loaded, not what was loaded or if the document is ready for interaction.
Nicholas Zakas has an article about detecting if an iframe loaded: http://www.nczonline.net/blog/2009/09/15/iframes-onload-and-documentdomain/. Basically you have this code snippet:
var iframe = document.createElement("iframe");
iframe.src = "simpleinner.htm";
if (iframe.attachEvent){
iframe.attachEvent("onload", function(){
alert("Local iframe is now loaded.");
});
} else {
iframe.onload = function(){
alert("Local iframe is now loaded.");
};
}
document.body.appendChild(iframe);
I haven't tested it, but I'm pretty sure jQuery should be able to handle it by doing something like $("#iframe").load(function () { alert("Local iframe is now loaded."); });
You could try using postMessage for communication between frames.
This will require the remote site to include some specific JavaScript to post a message to the parent document when it has finished loading.
It's possible to do this with an onload handler on the iframe itself. Unfortunately (surprise!) IE makes it difficult. The only way I could get this to work was to compose HTML for the iframe, then append it to the document with innerHTML. Then I have to poll to see when the iframe appears in the DOM, which varies depending on if the page is loading. Here's a link to the source: http://svn.openlaszlo.org/openlaszlo/trunk/lps/includes/source/iframemanager.js
See create(), __finishCreate() and gotload(). Feel free to take a copy of this and use it yourself!
Regards,
Max Carlson
OpenLaszlo.org
This is how I detected the loading of a Cross-Domain Iframe,
Set a unique id for the iframe ( U may use any sort of identifier, it doesn't matter )
<iframe id="crossDomainIframe" src=""> </iframe>
Set window event listener:
document.getElementById("crossDomainIframe").addEventListener('load',
function actionToPerform(){
//Do your onLoad actions here
}
)
In any case you will need some sort of cooperation from the other domain's server, as you are trying to abuse the Same Origin Policy (SOP)
The first solution document.domain=... won't work if domains are different. It works only for subdomains and ports, as described in the link above.
The only option that allows cross domain communication without polling is JSONP or script injection with a JS function callback. This method is available in all Google APIs and works well.
We've explained on our blog a way to sandbox those calls in an iframe to secure them. While postMessage is better now, the window.name hack has the advantage of working on old browsers.
Ironically, SOP does not prevent you to POST anything to another domain. But you won't be able to read the response.

Categories