I have a frame that looks like this: <iframe srcdoc="*insert HTML here*"></iframe>.
The frame may have some javascript in it and that's okay.
How could I prevent the contents of that frame from connecting to the network?
This includes:
- Javascript's HTTP requests and WebSocket connections etc
- Remote resources referenced in CSS
- External files in the HTML code
Is there some kind of sandbox rule to disable remote connections or do I have to regex all of that out? If so, what should I watch out for when applying the regex?
There is currently no reliable way of accomplishing this.
The sandbox attribute cannot apply the type of restriction you are trying to apply here. A Content-Security-Policy can (with some difficulty), but there is currently no way of reliably applying such a policy to an <iframe> that has its contents set by the srcdoc attribute, as there is no way of simulating HTTP headers for such a document. Indeed, an iframe with srcdoc is simply treated as part of the page which embeds it, and inherits any Content-Security-Policy from that page!
The W3C draft specification "Content Security Policy: Embedded Enforcement" has proposed a csp attribute. In the future, this might be usable to apply restrictions to such a document.
In the meantime, however, you will probably need to serve this content through a sandbox domain, or rethink your design.
Related
I am looking for an approach to allow only whitelisted scripts to run within a sandboxed iframe. I was thinking of an iframe-sandbox directive that allows only whitelisted scripts to run within an iframe. The analogy is the script-src directive in the Content Security Policy.
The problem:
<iframe sandbox="allow-same-origin allow-scripts" src="https://app.thirdparty.com" width="100%" height="800" frameBorder="0"></iframe>
The app in the iframe provides valuable functionality for my website. However, it pulls in external resources that I would like to control (i.e., block), e.g., AnalyticsJavaScript.com and TrackingPixel.com. I would like to allow scripts from app.thirdparty.com but block AnalyticsJavaScript.com and TrackingPixel.com.
Any help appreciated.
The answer to this is unfortunately complicated. With the advent of iframe sandboxing the question seems simple enough, but the spec that you're looking for is very much a work in progress. Thus, if you want decent browser support, the issue devolves into how to modify an iframe's content, which usually involves some sort of proxy.
Content Security Policy
The spec you really need is the CSP. At its simplest, you would allow specific scripts with the iframe atribute csp="...".
<iframe ...
src=""
csp="script-src https://app.thirdparty.com/"
...></iframe>
Any scripts from domains not specified (i.e. tracking scripts as in the question) would not be allowed in the response. Note that limiting scripts to those from a specified source does rely on cooperation with the third party app's server. If the server does not inform the user agent that it will adhere to the CSP restrictions then the response will be blocked.
The CSP is still a working draft and may change in the future. As stated in the comments, Chrome 61 and Opera 48 have implemented the CSP spec, but at this stage there is no sign from Firefox, Edge or Safari that they will also implement it. Unless you can guarantee that your users will only be using a browser that supports the spec, the tracking scripts will still be present for a very large percentage of users.
The remaining suggestions all involve modifying the iframe's content to remove the offending scripts.
Reverse proxy
Creating a reverse proxy to block a couple of tracking scripts in an iframe is probably equivalent to using a nuclear warhead to light a camp fire as far as overkill goes. But, if you are able to configure your server to this extent, it is the most reliable and seamless method for iframe content injection/modification/blocking that I've found.
The Wikipedia page states:
A reverse proxy is a type of proxy server that retrieves resources on behalf of a client from one or more servers. These resources are then returned to the client, appearing as if they originated from the proxy server itself.
Because the reverse proxy is an intermediary between the third party app and your site, it can transparently modify the responses to remove the undesired scripts. I'll use Apache in this example, but your implementation really depends on what server you're already using.
You need a subdomain for the proxy that points to your server IP, e.g. proxywebapp.yourdomain.com. On your server you would then create a virtual host in httpd.conf that uses the Apache mod_proxy module. Within your virtual host configuration you would then substitute the script calls to AnalyticsJavaScript.com and TrackingPixel.com with blanks. If the third party app must use HTTPS then reverse proxying gets trickier as you need an SSL virtual host and a SSL certificate for the proxy's FQDN.
<VirtualHost *:*>
ServerName proxywebapp.yourdomain.com
ProxyPreserveHost On
ProxyPass "/" "http://app.thirdparty.com/"
ProxyPassReverse "/" "http://app.thirdparty.com"
# in case any URLs have the original domain hard coded
Substitute "s|app.thirdparty.com/|proxywebapp.yourdomain.com/|i"
# replace the undesired scripts with blanks
Substitute "s|AnalyticsJavaScript/| /|i"
Substitute "s|TrackingPixel/| /|i"
</VirtualHost>
Your iframe would then point to proxywebapp.yourdomain.com.
<iframe ... src="proxywebapp.yourdomain.com" ...></iframe>
Again: total overkill but should work transparently.
Proxy scripts
A third option to consider is implementing a proxy script on your server between the iframe and third party app. You would add functionality into the proxy script that searches for and removes the undesired scripts before they reach the iframe. Additionally the proxy means the iframe's content will validate the same-origin policy, thus you could instead remove the undesired content with JavaScript in the frontend, although this may not guarantee that the scripts won't run before they are removed. There are many proxy scripts available online for all manner of backends (PHP, Node.js etc. ad nauseum). You would likely install the script and add it as the iframe's src, something like <iframe ... src="proxy.php?https://app.thirdparty.com/" ...>.
Unless properly configured for all cases, the proxy may not correctly transfer data between the third party app and its parent server. Testing will be required.
Writing your own server side proxy to remove a couple of scripts from an iframe is probably a bit excessive.
If you can't access the backend, it is possible to scrape the web app's content using JavaScript and a CORS or JSONP web app, and modify it to remove the scripts. Essentially making your own proxy in JavaScript. Such web apps (Any Origin, All Origins, etc) allow you to bypass cross-domain policy restrictions, but because they are third party you can no longer assume any of the web app's data is private. The issue with correctly communicating any data transfer between the app and its parent server will still be present.
Summary
A widely supported pure frontend solution is not feasible at the moment. But there are many ways to skin a cat and perhaps even more ways to modify an iframe's content, regardless of cross-domain restrictions.
Content Security Policy does look promising and is exactly what you're asking for, but currently its lack of widespread support means it can only be used in very niche situations. A reverse proxy that modifies content may take a lot of configuring and in this situation is like driving a full size semi-trailer over a Hot Wheels track, but will likely operate seamlessly. Content modification from a forward proxy is somewhat simpler to implement, but may break communications with the third party app's parent server.
You can't do this the way you want (for now). As mentioned in comments CSP:EE is a thing yet to come.
However you can try proxying the request and removing the unnecessary scripts from the body on the server side or on the client side, e.g.:
1) Get the needed page via XMLHTTPRequest
2) Remove unwanted
3) Inject into iframe on the page
"Workability" of this method depends purely on external app functionality. I.e. it will not work if the aforementioned app needs registration/authorisation of the end user to work, however this can still be suitable for some simple cases.
P.S.: you can implement a workaround to make such thing work via browser extension, however I'm sure this is not what you want.
Iframes are known for sandboxing the JavaScript inside the iframe. But, is the converse true? Do iframes protect their contents from JavaScript outside (on the hosting webpage)? My basic tests suggest that they do, but I can't find explicit documentation on this.
If you're curious, here's my use case:
A browser extension must display sensitive text on a third-party, untrusted webpage. Ideally, it will inject the text such that it cannot be accessed by JavaScript running on the untrusted webpage. The user may see it; the JavaScript may not.
The mechanisms for separating the JavaScript environments of the parent document and the iframed document work the same way in both directions.
They have their own environments
Access to the other environment is possible via parent and frameNode if they contain documents that are on the same origin
postMessage can be used to pass strings between them even if they are not on the same origin (but must be listened for by the document receiving the message).
A browser extension must display sensitive text on a third-party, untrusted webpage. Ideally, it will inject the text such that it cannot be accessed by JavaScript running on the untrusted webpage. The user may see it; the JavaScript may not.
This sounds like something better addressed using a mechanism that displays the text in the browser chrome rather than embedded in the page itself, e.g. with the Chrome popup api.
As best I can tell, the content is protected.
Iframes' contents are (by default) treated as if from a different origin. This brings the same-origin policy into play, preventing each page from accessing the others' DOM.
However, if you do <iframe sandbox="allow-same-origin">...</iframe>), then the iframe's contents can be accessed. Here's an important question:
Can malicious JavaScript programmatically set allow-same-origin to get into an iframe?
I don't know of explicit documentation on this, but my tests in Google Chrome say that programmatically changing the sandbox attribute has no effect on the iframe. Yes, you can dynamically change the sandbox attribute, but it has no effect.
After reading this compendium of methods here Ways to circumvent the same-origin policy it's apparent that any workaround requires modification of the target iframe code to get communications across domains.
Unfortunately on this project I'm working on I may only modify the parent page's code, the iframe is provided from another source and is untouchable by us. Are there any methods that don't require modifications to the iframe code?
The only solution then is to fetch the iframe content from your server, either through a proxy or through specific code, and serve it yourself so that the browser only sees one origin.
But be aware that this usually breaks the rules or contract of normal use of the site providing the iframe. If they didn't include CORS headers to allow inclusion and access, there's probably a reason.
No, there cant be such a method, that would kill the security.
I'm generating iframes dynamically for a lot of different random websites, and I tend to get this error in my javascript console (not necessarily with this url):
Unsafe JavaScript attempt to access frame with URL http://localhost:3000/results/ from frame with URL http://www.apple.com/iphone/. Domains, protocols and ports must match.
Why am I getting this error? Is there a way to get rid of it?
So the only two times I interact with iframes in javascript is when I dynamically load in an iframe:
$("#results_div").html('<iframe src='+url+' frameborder="0" class="iframe"><p>Browser does not support iframes.</p></iframe>');
and when I pull the src attribute of an iframe:
var previewed = $("iframe").attr("src");
Which one is causing the error?
Script in your iframe is trying to access parent's script/dom and they are in different domains. Cross domain scripting genrally generates that error.
In your case, apple.com and localhost are different domains and something in those iframe is trying to access its parent window's script or dom element.
Couple of ways to solve this:
Make sure both belong to same domain: http://www.google.com/url?sa=t&source=web&cd=2&ved=0CD0QFjAB&url=https%3A%2F%2Fdeveloper.mozilla.org%2Fen%2FSame_origin_policy_for_JavaScript&ei=VHsaTtqtGcPngQeLvN31Dw&usg=AFQjCNF2yi5TJQGfSywsrfxVvKdsQYzIKg
Limited: Use HTML5's cross domain messaging via postmessage: http://ajaxian.com/archives/cross-window-messaging-with-html-5-postmessage
Limited: Use JOSNP calls: http://www.zackgrossbart.com/hackito/jsonp-sop/
This is due to the fact that your script is trying to acces some code on another domain. This behaviour can be understood by reading Same origin Policy. http://en.wikipedia.org/wiki/Same_origin_policy
There are workarounds and some implementation by which you can resolve this issue.
Some of them are using proxy approach, Fragment identifier approach.
In HTML5 we can use postmessages to get this resolved.
But you have to figure out what may work for you.
You cannot interact with a different domain with javascript or iframes apart from loading the domain with the into the iframe.
When creating iFrame dynamically (javascript) on IE and trying to access its document, access denied error is issued (because its source is not on the same domain as the containing html).
I think I read somewhere that P3P header can lower this restriction (usually it is used for 3rd party cookies). Can anyone explain how to do it for dynamically created iframe (or point me to this data)?
No. P3P has no relation to the JavaScript Same-Original Policy, which cannot be circumvented short of a security hole in the browser or the remote site.
You may have to proxy the iframe content through your own site, if that's possible.