We have a vaadin flow site which contains an inframe. In this iframe there is a link to a pdf document.
If the user clicks on the pdf document link, a new browser tab opens up with an error site from the browser “ERR_BLOCKED_BY_CLIENT”. If we dig deeper in the developer tools you can find that the request was blocked by csp. Unfortunatly no furhter information, nothing in the console.
So far so good, but there is no CSP Policy set either by the main site nor the iframe site nor the pdf document site. I guess there is a default browser csp policy that could be a problem.
The iframe has no sandbox attribute set.
If i manipulate the HTML DOM and duplicate the iframe, the new iframe which is identical to the original created by vaadin flow, works normal and the document is openend in a second tab.
If i reproduce the same iframe on a simple html site than everything works as expected.
The only difference between the original iframe and the duplicated one is that the original one was created by FlowClient.js
Interesting mystery. I don’t remember seeing anythin similar.
According to blocked:csp ⟶ Understanding why CSP blocks resources, there should also be a line in the browser console that describes the reason for blocking. The last idea on my mind would be to check if there are any additional hints from Firefox since that’s a completely different browser engine compared to Chrome, Chromium and Edge that all share the same foundation.
Yes normaly in the console there is a reason why it is blocked, but not in this case. I guess because there are no CSP Policy in place by neither website.
In Firefox we can observe a similar error code “NS_ERROR_CONTENT_BLOCKED”
Yes CORS could also be the problem, that crossed my mind aswell. but what really confuses me, is that if i duplicate the iframe, the new one works fine without problem, with the exact same html.
Also if i use the same iframe setup without vaadin on a simple website, it works fine.
Thats why i suspect vaadin, that is the only difference that i can see.
I don’t see that Vaadin in itself would do anything special but the browser might make a difference between an iframe created using JS and an iframe that was present in the original HTML document.
I cannot reproduce it either somewhere else in the webapp, with the same iframe src. I cannot see any difference in how the iframe is setup in the java code.
I think i got the problem, it has to do with how we setup the iframe. If we provide the url in the constructor or set it later, we even got code that initializes the iframe with an empty string.
Apparently the browsers dont like that, if the src of the iframe is changed by js.
I will run some tests and give a full explanation as soon as have time to write it up.
Thx for the help!
We dynamically set the sandbox attribute on the iframe by user settings. This leads to the case that we change the iframe sanbox after the browser already rendered the iframe. This triggers some security feature of the browser that afterwards does not allow cross origin request anymore.
simplified code to reproduce
public class VACoreIFrame extends IFrame {
private static final String VAADIN_IFRAME_SANDBOX_EXCLUSION_URLS = "vaadin.iframe.sandbox.exclusion.urls";
public VACoreIFrame() {
init();
}
public VACoreIFrame(@Nonnull String url) {
super(Objects.requireNonNull(url));
init();
}
private void init() {
setSizeFull();
setSandbox(
SandboxType.ALLOW_SCRIPTS,
SandboxType.ALLOW_SAME_ORIGIN,
SandboxType.ALLOW_POPUPS,
SandboxType.ALLOW_FORMS
);
checkSandboxExclusion();
}
@Override
public final void setSrc(AbstractStreamResource src) {
super.setSrc(src);
checkSandboxExclusion();
}
@Override
public final void setSrc(String src) {
super.setSrc(src);
checkSandboxExclusion();
}
private void checkSandboxExclusion() {
final List<String> excludedURLs = DataProperties.getCurrentSystemProperties().getStringList(VAADIN_IFRAME_SANDBOX_EXCLUSION_URLS);
final boolean isURLExcluded = excludedURLs.stream().anyMatch(excludedURL -> getSrc().startsWith(excludedURL.trim()));
if (isURLExcluded) {
setSandbox(); //remove sandbox attribute
}
}
}
The code was a good intention but the browser is apprently stronger, we will not change the sandbox attribute after the component is added and loaded.
Key takeaway for me, is to not manipulate an iframe after its creation.
Thx again for the help.