Does Vaadin implement a CSP Policy on IFrames on the fly?

We are running vaadin flow version 24.7.1

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

Can anybody give some advice or point in the right direction on how to fix or atleast understand the problem?

What makes you think it’s blocked by CSP? I don’t see any indication of that in the shared screenshot.

Does it behave the same also in other browsers?


i can only upload one picture per post sorry.

Yes it behaves like that in multiple browsers. (chrome, chromium, edge)

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”

This sounds like CORS might also be a “problem” here. I would suggest to configure CORS and CSP properly

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.

Yes, this is exactly the problem

And just for reference, I cannot reproduce the symptoms in a simple example application that I run in development mode on localhost:

@Route
public class MainView extends VerticalLayout {
    public MainView() {
        IFrame iframe = new IFrame();
        iframe.setSrc(new StreamResource("example.pdf", () -> {
            try {
                return new FileInputStream("/path/to/example.pdf");
            } catch (FileNotFoundException e) {
                throw new RuntimeException(e);
            }
        }));
        add(iframe);
    }
}

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.

As far as I know it is related to https - browser do all kind of security magic once that’s in place

Everything is https, the main site, the iframe and the link to the pdf.

My Code points to a website which has link to the pdf like this

<a href="https://xxxx" target="_blank" title="Some Title">
  <img src="https://xxx" alt="" aria-hidden="true"> Some Title</a>

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!

1 Like

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.

4 Likes