Problem
We are an oEmbed provider to embed.ly which is integrated at medium.com to display widgets in articles. Embed.ly provides an height control API, which lets the iFrame change its size from the inside. This has been working, and still works in all browsers but Safari. Which now throws an error:
SecurityError: Blocked a frame with origin "https://medium.com" from accessing a cross-origin frame. Protocols, domains, and ports must match.
See an example here: https://medium.com/climateaction/its-time-to-challenge-instagrams-climate-footprint-e15c67bc2b7c
Update 2019-07-10
medium.com apparently changed something. Now:
The described bug doesn't occur when opening the link directly * yaeeehi *
It still occurs when coming from an overview page and clicking on an article * buuhhhh *
There is a new bug, now embeds on custom domains don't work at all * arg *
Refused to display 'https://medium.com/media/c31b8b0f7cb609aaf60d13f46e3777bf' in a frame because it set 'X-Frame-Options' to 'sameorigin'.
There is now a double scrollbar for embeds in Windows/Mac Mojave/... + Chrome/Safari/...
Detailed Description
The integration is as followed:
The medium.com article integrates an medium.com/media/... iFrame:
<iframe data-width="620" data-height="500" width="350" height="282" data-src="/media/1389b69a290289ae20aedd68efce0d4b?postId=e15c67bc2b7c" data-media-id="1389b69a290289ae20aedd68efce0d4b" data-thumbnail="https://i.embed.ly/1/image?url=https%3A%2F%2Fposixion.com%2Fimages%2Flogo.jpg&key=a19fcc184b9711e1b4764040d3dc5c07" class="progressiveMedia-iframe js-progressiveMedia-iframe" allowfullscreen="" frameborder="0" src="/media/1389b69a290289ae20aedd68efce0d4b?postId=e15c67bc2b7c">
</iframe>
This again integrates an embedly.com/widgets/... iFrame which loads our site.
We call window.parent.postMessage which reaches the medium.com iFrame sucessfully and the notifyResize function is called:
function notifyResize(height) {
height = height ? height : document.documentElement.offsetHeight;
var resized = false;
if (window.donkey && donkey.resize)
{donkey.resize(height); resized = true;}
if (parent && parent._resizeIframe)
{var obj = {iframe: window.frameElement, height: height}; parent._resizeIframe(obj); resized = true;}
if (window.location && window.location.hash === "#amp=1" && window.parent && window.parent.postMessage)
{window.parent.postMessage({sentinel: "amp", type: "embed-size", height: height}, "*");}
if (window.webkit && window.webkit.messageHandlers && window.webkit.messageHandlers.resize)
{window.webkit.messageHandlers.resize.postMessage(height); resized = true;}
return resized;
}
This breakes at parent._resizeIframe - which is weird, since the iFrame contents comes from https://medium.com and tries to access the parent which is also https://medium.com. Any thoughts on this?
Simulation
I tried replicating the error in my environment, but it works:
Simulated medium.com article to: https://test.posixion.com/tests/medium.html
Simulated iFrame to: https://test.posixion.com/tests/medium-embed.html
Left embedly integration as is
Clue 1: content-security-policy
Maybe it is related to the content-security-policy of medium.com:
default-src 'self';
connect-src https://localhost https://*.instapaper.com https://*.stripe.com https://glyph.medium.com https://*.paypal.com https://getpocket.com https://medium.com https://*.medium.com https://*.medium.com https://medium.com https://*.medium.com https://*.algolia.net https://cdn-static-1.medium.com https://dnqgz544uhbo8.cloudfront.net https://cdn-videos-1.medium.com https://cdn-audio-1.medium.com https://*.lightstep.com https://*.branch.io https://app.zencoder.com 'self';
font-src data: https://*.amazonaws.com https://*.medium.com https://glyph.medium.com https://medium.com https://*.gstatic.com https://dnqgz544uhbo8.cloudfront.net https://use.typekit.net https://cdn-static-1.medium.com 'self';
frame-src chromenull: https: webviewprogressproxy: medium: 'self';
img-src blob: data: https: 'self';
media-src https://*.cdn.vine.co https://d1fcbxp97j4nb2.cloudfront.net https://d262ilb51hltx0.cloudfront.net https://*.medium.com https://gomiro.medium.com https://miro.medium.com https://pbs.twimg.com 'self' blob:;
object-src 'self';
script-src 'unsafe-eval' 'unsafe-inline' about: https: 'self';
style-src 'unsafe-inline' data: https: 'self';
report-uri https://csp.medium.com
But there is no matching report-uri call.
Clue 2: missing source
Interesting is also that the iFrame source: https://medium.com/media/1389b69a290289ae20aedd68efce0d4b?postId=e15c67bc2b7c does not appear in the webdeveloper network tab!? But it is still selectable as a page in the console.
Okay, the problem is the following:
Blocked a frame with origin "https://medium.com" from accessing a
frame with origin "https://medium.com". The frame being accessed set
"document.domain" to "medium.com", but the frame requesting access did
not. Both must set "document.domain" to the same value to allow
access.
Setting document.domain = "medium.com" in the first iFrame fixes the issue. So medium/embed.ly have to do that.
Related
I have custom CSP headers in next.config.js that are loaded into the head of the page in ./layouts.
const defaultCSP = {
"script-src": [
"'self'",
"'unsafe-eval'",
"'unsafe-inline'",
`'nonce-${nonce}'`,
"tagmanager.google.com/",
"googletagmanager.com",
],
"script-src-elem": [
"'self'",
`'nonce-${nonce}'`,
"tagmanager.google.com/",
"googletagmanager.com",
],
};
Trying to use react-gtm-module-nonce like so in _app.tsx
useEffect(() => {
TagManager.initialize({
gtmId: GTM_CONTAINER_ID,
auth: GTM_AUTH,
preview: GTM_PREVIEW,
nonce: NONCE,
});
}, []);
but when the app is loaded I see the following error
Refused to execute inline script because it violates the following Content Security Policy directive: "script-src-elem 'self' 'nonce-SOME_NONCE_VALUE' tagmanager.google.com/ googletagmanager.com Either the 'unsafe-inline' keyword, a hash ('sha256-r7NoIbKRzEIuATQ9EL7eN52m5xWoVwuBBTdGzzqnMbY='), or a nonce ('nonce-...') is required to enable inline execution.
It seems like I have the necessary items to run GTM but can't get past CSP. Any clues as to what's happening here? I've tried adding 'unsafe-inline' to script-src-elem but then it shows that it will be ignored if there's a nonce.
I have a micro front-end which is connected to my identity server. In order for a user to access the micro front-end, the user needs to be authenticated via this ID server. The micro front-end needs to be embedded in an iframe in my main application but since this micro front-end requires the ID server, I am getting an error on the console. Everything works fine before embedding this micro front-end but I get this problem as soon it gets into the iframe.
Refused to frame 'https://localhost:44300/' because an ancestor violates the following Content Security Policy directive: "frame-ancestors 'none'".
I tried including this in my identity server's web.config file which resulted to the above error on the console.
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Content-Security-Policy" value="frame-ancestors ''" />
</customHeaders>
</httpProtocol>
</system.webServer>
It seems ID server does not allow its content to be embedded in an iframe. This perhaps might not be a good practice but per my requirements, i will like to know how to enable this.
UPDATE
Here is where the csp is defined
public override void OnResultExecuting(ResultExecutingContext context)
{
var result = context.Result;
if (result is ViewResult)
{
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Type-Options"))
{
context.HttpContext.Response.Headers.Add("X-Content-Type-Options", "nosniff");
}
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
if (!context.HttpContext.Response.Headers.ContainsKey("X-Frame-Options"))
{
context.HttpContext.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN");
}
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
var csp = "default-src 'self'; object-src 'none'; frame-ancestors 'none'; sandbox allow-forms allow-same-origin allow-scripts; base-uri 'self';";
// also consider adding upgrade-insecure-requests once you have HTTPS in place for production
//csp += "upgrade-insecure-requests;";
// also an example if you need client images to be displayed from twitter
// csp += "img-src 'self' https://pbs.twimg.com;";
// once for standards compliant browsers
if (!context.HttpContext.Response.Headers.ContainsKey("Content-Security-Policy"))
{
context.HttpContext.Response.Headers.Add("Content-Security-Policy", csp);
}
// and once again for IE
if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Security-Policy"))
{
context.HttpContext.Response.Headers.Add("X-Content-Security-Policy", csp);
}
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
var referrer_policy = "no-referrer";
if (!context.HttpContext.Response.Headers.ContainsKey("Referrer-Policy"))
{
context.HttpContext.Response.Headers.Add("Referrer-Policy", referrer_policy);
}
}
}
It is really a bad idea to try to iframe IdentityServer, because then its hard for the user to know where he is actually logging in to.
If you still want to change it you need to look at the SecurityHeadersAttribute.cs file in the QuickStart folder, that one defines the CSP.
In this file you can tweak the security headers that are sent to the browser. You need to look at the CSP and the X-Frame-Options header.
I have a simple image preview that, when clicked, takes you to a full size image:
<html>
<head>
<meta name="referrer" content="origin">
</head>
<body>
<a href="/myimage.jpg">
<img src="/myimage.jpg" title="my image" border="0" />
</a>
</body>
</html>
The problem: in addition to the request for the image, the browser will also request the favicon.ico. The request headers for the image is: Referer: https://example.com/ (as expected). However, for the request for the favicon.ico file, this header is the full url of the referring page: Referer: https://example.com/where-I-was.
How do I set the Referer header for the favicon.ico request to simply the origin? I don't want it the full url to show in my nginx logs. Thx!
Option 1
Use the referrerpolicy HTML attribute on the link tag - see docs on MDN.
<a href="/myimage.jpg" referrerpolicy="origin">
Option 2
Use the webRequest API to rewrite the Referer header - see the MDN page on it here. Simply set the header to whatever you want it to be - seems like you might be interested in the window.location object.
Sample code - modified from the MDN page:
var targetURL = "http://example.com/favicon.ico";
var protocol = window.location.protocol;
var hostname = window.location.hostname;
var port = window.location.port;
var origin = protocol + '//' + hostname + (port ? ':' + port : '');
function rewriteUserAgentHeader(e) {
e.requestHeaders.forEach(function(header){
if (header.name.toLowerCase() == "referer") {
header.value = origin;
}
});
return {requestHeaders: e.requestHeaders};
}
browser.webRequest.onBeforeSendHeaders.addListener(
rewriteUserAgentHeader,
{urls: [targetURL]},
["blocking", "requestHeaders"]
);
I'm creating extension to organize youtube channels using tags. It has angular frontend with url like this
moz-extension://f78b3bd9-a210-41c5-9d8d-9b7ab3717f6e/index.html#/channel/UCtinbF-Q-fVthA0qrFQTgXQ
And I want to embed channel's page using iframe, but security policies doesn't allow me to do that.
Load denied by X-Frame-Options: https://www.youtube.com/ does not permit cross-origin framing.
So I tried to modify X-Frame-Options, but it doesn't change anything(headers aren't added).
What I did:
1 Added permissions to manifest.json:
"webRequest",
"://.youtube.com/",
"://www.youtube.com/*"
2 Wrote some code in background.js
function addFramePermissions(e) {
console.log("Loading url: " + e.url);
var allowedHeaders = [];
for (var header of e.responseHeaders) {
if (header.name.toLowerCase() !== "x-frame-options") {
allowedHeaders.push(header);
} else {
console.log('x-frame-options found!!!');
}
}
e.responseHeaders = allowedHeaders;
return { responseHeaders: e.responseHeaders };
}
browser.webRequest.onHeadersReceived.addListener(
addFramePermissions,
{
urls: [
"*://*.youtube.com/*",
"*://youtube.com/*"
]
},
["blocking", "responseHeaders"]
);
Code reaches function and I can see "x-frame-options found!!!" in console, but firefox's Network Monitor shows that x-frame-options exists with value SAMEORIGIN
I ran my extension in Chrome and Chrome said that I forgot to add "webRequestBlocking" in permissions. Thanks, Chrome!
Inside my application, I have an iframe that can open any webpages (99% of the time of the same origin). But the user can click in a link inside the iframe and go on an external website. I managed to detect if the website is of the same origin or not, but a request to a website such as "https://www.google.ca/" throw the following error:
Refused to display 'https://www.google.ca/' in a frame because it set 'X-Frame-Options' to 'SAMEORIGIN'.
I have a beforeunload, onerror and onload event binded to my iframe, but I can't manage to handle this security issue with the event object.
Have had same problem, loading 3rd party pages into my iframe some of them were blocked by X-Frame-Options that is not included into HTML.
NOTICE: working in CHROME,iPhone,Mozilla Android 4.4.2 BUT not working on FireFox.
If you want those pages to be opened in new window this is how i manage it.
Maybe usefull stuff for someone else searching solution for this particular scenario.
PHP (get_headers) works fine for most of them but i came accross few that did not give X-Frame-Options directly into HTML header (auto include), so get_headers failed and browser receive this [Load denied by X-Frame-Options: your_guest_url does not permit cross-origin framing.]
i had to add link on my first page to let users open problematic page in new window by clicking on it and create inbetween page with javascript,loading icon and setTimeout().
firstpage.php [with 'iframe' and 'a' below 'iframe']
<?php
foreach(get_headers("http://".$_REQUEST["address"]) as $v) {
if($v == "X-Frame-Options: SAMEORIGIN" || $v == "X-Frame-Options: DENY") {
header("location: http://".$_REQUEST["address"]);
die();
}
}
?>
<iframe id="iframe-id" src="secondpage.php"></iframe>
<a href=your_guest_url>go to your_guest_url</a>
<script language='javascript'>
setTimeout(function() {
document.getElementById('iframe-id').src = 'your_guest_url';
},500);
</script>
secondpage.php [loaded into iframe]
<div class="loading-info">
<div id="loading-info" class="loading-info-content">
<i class="fa fa-3x fa-spinner fa-pulse"></i>
</div>
</div>
<script>
setTimeout(function() {
document.getElementById("loading-info").innerHTML = "<p'>USE LINK BELOW IFRAME</p>";
},5000);
</script>
page without 'hidden' X-Frame-Options loads via javascript into iframe after 500ms, problematic page won't load so secondpage stays in iframe and executes javascript setTimeout();.