Search code examples
reactjswindowsservicestackchromium-embedded

Is there a way to avoid X-Frame-Options in a CEF Windows Chromium Desktop App?


I created a simple app using the suggested "app init", then I dropped a pre-compiled ReactApp in place. The app has a browser within it that uses an IFrame to host the navigated pages, but in some pages, it issues the following error:

Refused to display 'https://www.theverge.com/' in a frame because it set 'X-Frame-Options' to 'sameorigin'.", source: http://localhost:5000/#/

https://content-security-policy.com/

The page above has a series of ways to how this could be avoided, and Chromium has a flag that could help, where it disables security and as many suggested in other posts and questions, that may help with this issue.

Beyond those, there is the possibility of writing a reverse-proxy that could potentially take care of this.

Either way what I need to know is if there is a way to achieve that through parameters within the "app" tool, something like:

app --unsecure
app publish --unsecure
app publish-exe --unsecure

Thank you


Solution

  • I've tried a number of different options inclusing using a Custom .NET Core Desktop Apps that adds the disable-web-security switch which used to work:

    static int Main(string[] args)
    {
        var host = new WebHostBuilder()
            .UseKestrel()
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseStartup<Startup>()
            .UseUrls("http://localhost:5000/")
            .Build();
    
        host.StartAsync();
    
        var config = new CefConfig(Debug)
        {
            Args = args,
            StartUrl = startUrl,
            HideConsoleWindow = false,
            OnBeforeCommandLineProcessing = (processType, commandLine) => {
                commandLine.AppendSwitch("disable-web-security");                    
            }
        };
    
        return CefPlatformWindows.Start(config);
    }
    

    But no longer does so appears this security restriction is now embedded inside of Blink.

    Using a Proxy to Remove Headers

    The only solution I could get to work is to use a proxy that calls the internal .NET Core server which proxies the downstream URL but ignoring the X-Frame-Options header.

    This is easy to do using ServiceStack's Proxy Feature where you can register a proxy to https://www.theverge.com that strips the X-Frame-Options header with:

    Plugins.Add(new ProxyFeature(
        matchingRequests: req => req.PathInfo.StartsWith("/theverge"),
        resolveUrl: req => $"https://www.theverge.com" + req.RawUrl.Replace("/theverge", "/")) {
        IgnoreResponseHeaders = {
            "X-Frame-Options"
        }
    });
    

    This will let you embed The Verge in your App with:

    <iframe src="/theverge" style="width:100%; height:800px;" frameborder="0"></iframe>
    

    Which will render TheVerge in an iframe as expected:

    enter image description here

    Working Demo

    You can find a working example of this in ServiceStack.CefGlue.Win64.AspNetCore:

    Startup.cs

    public class Startup
    {
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            app.UseServiceStack(new AppHost());
    
            app.Run(context =>
            {
                context.Response.Redirect("/metadata");
                return Task.FromResult(0);
            });
        }
    }
    
    public class AppHost : AppHostBase
    {
        public AppHost() : base("MyApp", typeof(MyServices).Assembly) { }
    
        public override void Configure(Container container)
        {
            Plugins.Add(new SharpPagesFeature());
    
            Plugins.Add(new ProxyFeature(
                matchingRequests: req => req.PathInfo.StartsWith("/theverge"),
                resolveUrl: req => "https://www.theverge.com" + 
                                    req.RawUrl.Replace("/theverge", "/")) {
                IgnoreResponseHeaders = {
                    "X-Frame-Options"
                }
            });
        }
    }
    
    [Route("/hello")]
    public class Hello : IReturn<HelloResponse>
    {
        public string Name { get; set; }
    }
    
    public class HelloResponse
    {
        public string Result { get; set; }
    }
    
    public class MyServices : Service
    {
        public object Any(Hello request) => 
            new HelloResponse { Result = $"Hello, {request.Name}!" };
    }
    

    ServiceStack.CefGlue.Win64.AspNetCore.csproj

    <PackageReference Include="Microsoft.AspNetCore.Server.Kestrel" Version="2.*" />
    <PackageReference Include="ServiceStack.CefGlue.Win64" Version="5.*" />
    <PackageReference Include="ServiceStack" Version="5.*" />
    <PackageReference Include="ServiceStack.CefGlue" Version="5.*" />
    <PackageReference Include="ServiceStack.CefGlue.Win64" Version="5.*" />
    <PackageReference Include="WinApi" Version="4.0.0" />
    

    You'll also need to copy CEF binaries from the ServiceStack.CefGlue.Win64 NuGet package with:

    <ItemGroup>
        <Content Include="locales\*.*">
            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
        </Content>
        <Content Include="swiftshader\*.*">
            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
        </Content>
        <Content Include="*.pak">
            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
        </Content>
        <Content Include="*.lib">
            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
        </Content>
        <Content Include="*.dat">
            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
        </Content>
        <Content Include="*.dll">
            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
        </Content>
        <Content Include="*.bin">
            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
        </Content>
        <Content Include="*.exe">
            <CopyToOutputDirectory>Always</CopyToOutputDirectory>
        </Content>
        </ItemGroup>
    
        <Target Name="CopyLinkedContentFiles" BeforeTargets="Build">
        <Copy SourceFiles="%(Content.Identity)"
                DestinationFiles="$(OutputPath)\%(Content.Link)"
                SkipUnchangedFiles="true"
                OverwriteReadOnlyFiles="true" />
    </Target>
    

    index.html

    <!DOCTYPE html>
    <html lang="en">
    <body>
        <h1>X-Frame-Options Proxy Test</h1>
        <iframe src="/theverge" style="width:100%; height:800px;" frameborder="0"></iframe>
    </body>
    </html>