I have a product that's playing a video in Flash (if available), and falls back to HTML5 if Flash isn't available.
I'm not able to find a way to determine if JavaScript is executing within an Iframe with the "sandbox" attribute, which is necessary for my solution because sandboxed iframes disable all plugins. The sandboxed iframe could be as simple as this:
<iframe src="http://www.cross-domain.com/" sandbox="allow-scripts">
To determine if Flash is enabled, I'm using swfobject's method of checking navigator.plugins["Shockwave Flash"].description, which is set even when in a sandboxed iframe. I can load the swf object, but it doesn't play.
To reproduce this issue, visit http://jsfiddle.net/max_winderbaum/9cqkjo45/, open your chrome inspector and click "Run". The script on the cross-domain site will pause in the context of the sandboxed iframe.
According to the W3 spec at http://dev.w3.org/html5/spec-preview/browsers.html#sandboxing-flag-set, there is supposed to be an "active sandboxing flag set" on the document that JavaScript can access (at least that's how I'm reading the spec). There doesn't seem to be any flag set on the iframe's document.
Does anyone have any ideas / solutions on how to detect if JavaScript is executing from within a sandboxed iframe?
Given an iframe with an empty sandbox attribute, the framed document will be fully sandboxed, subjecting it to the following restrictions: JavaScript will not execute in the framed document. This not only includes JavaScript explicitly loaded via script tags, but also inline event handlers and javascript: URLs.
Note that it's not advisable to add both values allow-scripts and allow-same-origin: these two values will allow the iframe to access and modify your DOM. In this case, a malicious iframe could perform all sorts of operations, and could even remove its own sandbox attribute!
Correct Option: A. Scripts are re-enabled by allow-scripts. The sandbox attribute enables an extra set of restrictions for the content in the iframe. Allow-forms re-enables from submission.
Click the Live button, and then select Disable sandbox feature. In the confirmation dialog, click the Yes, Disable button. This will deactivate the feature and delete any sandbox you have started.
I will consider different kinds of iframes (choose the first case which applies):
Iframes with the sandboxed scripts browsing context flag
That is, iframes with a sandbox
attribute which doesn't contain the allow-scripts
keyword.
This flag blocks script execution. In particular, you can't use a script to check if the iframe is sandboxed.
Same-origin iframes without the sandboxed origin browsing context flag
That is, same-origin iframes with no sandbox
attribute, or same-origin iframes with a sandbox
attribute which contains the allow-same-origin
and allow-scripts
keywords.
In this case, you can use the frameElement
global property to access the frame element (returns null
when not used inside an iframe).
Once you have a reference to the iframe, you can use hasAttribute
or getAttribute
to check its sandboxed
attribute. There is also the sandboxed
property, which should return a DOMSettableTokenList
(old browsers might return a string according to an old spec).
Cross-origin iframes without the sandboxed origin browsing context flag
That is, cross-origin iframes with no sandbox
attribute, or cross-origin iframes with a sandbox
attribute which contains the allow-same-origin
and allow-scripts
keywords.
In this case, the use of frameElement
is blocked:
SecurityError
exception. This is implemented by Chrome.null
. This is implemented by Firefox.allow-same-origin
in a cross-origin iframe isn't much useful, consider assuming the iframe isn't sandboxed.Iframes with the sandboxed origin browsing context flag
That is, iframes with a sandbox
attribute which doesn't contain the allow-same-origin
keyword but contains the allow-scripts
keyword.
As in the previous case, the use of frameElement
is blocked.
However, you can detect this case because document.domain
will be the empty string.
Note: Firefox treats data URIs as same-origin, so it's OK. However, Chrome treats them as cross-origin. Then frameElement
doesn't work and document.domain
is the empty string regardless of whether the iframe is sandboxed or not. You can check whether location.protocol
is 'data:'
string to detect data URIs.
In general, you might try something like
function isSandboxedIframe() {
if (window.parent === window) return 'no-iframe';
try { var f = window.frameElement; } catch(err) { f = null; }
if(f === null) {
if(document.domain !== '') return 'unkown'; // Probably 'non-sandboxed'
if(location.protocol !== 'data:') return 'sandboxed';
return 'unkown'; // Can be 'sandboxed' on Firefox
}
return f.hasAttribute('sandbox') ? 'sandboxed' : 'non-sandboxed';
}
A project sandblaster can help you detect if you running being sandboxed.
Sandbox check if itself is framed first and then scans through the attributes of the frame element to detect several information about itself. These includes framed
, crossOrigin
, sandboxed
, sandboxAllowances
, unsandboxable
, resandboxable
, sandboxable
.
To detect if itself is sandboxed in our case, it checks if the frame element has an attribute sandbox
.
// On below `frameEl` is the detected frame element
try {
result.sandboxed = frameEl.hasAttribute("sandbox");
}
catch (sandboxErr) {
result.sandboxed = null;
if (typeof errback === "function") {
errback(sandboxErr);
}
}
I tried to replicate your issue and to test if this solution works, I had to paste the script into the window itself due to the security issue.
<html>
<head>
</head>
<body>
<script>
//Paste the contents of the script(https://raw.githubusercontent.com/JamesMGreene/sandblaster/master/dist/sandblaster.js) here
var result = sandblaster.detect();
if(result.sandboxed === true) {
//sandboxed
}
debugger;
</script>
</body>
</html>
Here is a demo: http://jsfiddle.net/Starx/tzmn4088/ that shows this working.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With