Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Grakify 13 posts 80 karma points
    Sep 29, 2023 @ 08:05
    Grakify
    0

    Using middleware to set Security headers

    Hi! I would like to set different Security headers (specifically different Content Security Policy headers) for the frontend of my website and the Umbraco backoffice (version 11). I however do not seem to be able to get this done with middleware, since I don't have access to the IApplicationBuilder builder to .UseSecurityHeaders() in there. I used another example from another topic here on the Umbraco forum, but this does not set the CSP headers at all :

    public static IApplicationBuilder UseSecurityPolicy(this IApplicationBuilder app) => app

          .UseWhen(x => !IsBackOffice(x), builder =>
          {
              policyCollection
              .AddContentSecurityPolicy(builder =>
              {
                  builder.AddDefaultSrc()
                      .Self();
    
                  builder.AddScriptSrc()
                      .Self()
                      .WithNonce()
                      .From("https://www.google-analytics.com")
                      .From("https://consentcdn.cookiebot.com")
                      .From("https://consent.cookiebot.com")
                      .From("https://consent.cookiebot.com/Scripts/widgetIcon.min.js")
                      .From("https://consentcdn.cookiebot.com/consentconfig/3d82664e-d60b-4a2a-bdaa-cd426292c836/state.js")
                      .From("https://cdn.jsdelivr.net")
                      .From("https://kit.fontawesome.com")
                      .From("https://ajax.googleapis.com")
                      .From("https://unpkg.com")
                      .From("https://www.youtube.com/")
                      .From("https://ajax.aspnetcdn.com")
                      .From("https://cdnjs.cloudflare.com/");
    
                  builder.AddStyleSrc()
                      .Self()
                      .UnsafeInline() // Has to be removed later! Removing it makes Cookiebot adding inline style elements break
                      .From("https://fonts.googleapis.com")
                      .From("https://cdn.jsdelivr.net")
                      .From("https://cdnjs.cloudflare.com")
                      .From("https://fonts.gstatic.com")
                      .From("https://unpkg.com")
                      .From("https://consent.cookiebot.com/Scripts/widgetIcon.min.js")
                      .From("https://consent.cookiebot.com/uc.js");
    
                  builder.AddImgSrc()
                      .Self()
                      .Data()
                      .From("https://www.google-analytics.com")
                      .From("https://cdn.jsdelivr.net")
                      .From("https://img.youtube.com/")
                      .From("https://i.ytimg.com/");
    
                  builder.AddFontSrc()
                      .Self()
                      .Data()
                      .From("https://fonts.gstatic.com")
                      .From("https://cdnjs.cloudflare.com");
    
                  builder.AddConnectSrc()
                      .Self()
                      .From("https://www.google-analytics.com")
                      .From("https://consentcdn.cookiebot.com")
                      .From("https://*.algolianet.com")
                      .From("https://*.algolia.net")
                      .From("https://*.algolia.io")
                      .From("https://cdnjs.cloudflare.com");
    
                  builder.AddFrameSrc()
                      .Self()
                      .From("https://www.youtube.com")
                      .From("https://youtube.com")
                      .From("https://consentcdn.cookiebot.com");
    
                  builder.AddObjectSrc()
                      .None();
    
                  builder.AddBaseUri()
                      .Self();
    
                  builder.AddFormAction()
                      .Self();
    
                  builder.AddFrameAncestors()
                      .None();
              });
    
              app.UseSecurityHeaders(policyCollection);
          })
          .UseWhen(IsBackOffice, a =>
          {
              policyCollection
              .AddContentSecurityPolicy(builder =>
              {
                  builder.AddDefaultSrc()
                      .Self();
    
                  builder.AddScriptSrc()
                      .Self().UnsafeInline().UnsafeEval();
    
                  builder.AddStyleSrc()
                      .Self().UnsafeInline();
    
                  builder.AddImgSrc()
                      .Self()
                      .Data()
                      .From("dashboard.umbraco.com")
                      .From("our.umbraco.com");
    
                  builder.AddConnectSrc()
                      .Self()
                      .From("our.umbraco.com");
    
                  builder.AddObjectSrc()
                      .None();
              });
    
              app.UseSecurityHeaders(policyCollection);
          });
    
        private static bool IsBackOffice(HttpContext context) => context.Request.Path.StartsWithSegments(new PathString("/umbraco"));
    

    Above code I call in the Startup.cs like this -> app.UseSecurityPolicy();

    Any idea on how to set the CSP headers correctly? Thank you in advance.

  • Huw Reddick 1929 posts 6697 karma points MVP 2x c-trib
    Sep 29, 2023 @ 08:57
    Huw Reddick
    0

    you don't actually appear to be setting the header anywhere (unless I'm missing something)

    In ASP.NET Core, you can create middleware to set the header to http response, here is a minimal middleware to do this. You need to add this code to the Configure() method in Startup.cs before the UseEndpoints method.

    app.Use(async (context, next) =>
    {
        context.Response.Headers.Add("Content-Security-Policy", "default-src 'self';");
        await next();
    });
    
  • Huw Reddick 1929 posts 6697 karma points MVP 2x c-trib
    Sep 29, 2023 @ 08:58
  • Grakify 13 posts 80 karma points
    Sep 29, 2023 @ 09:05
    Grakify
    0

    Hi Huw Reddick, thank you for your quick reply. I'm using a package to set the headers (NetEscapades.AspNetCore.SecurityHeaders, https://github.com/andrewlock/NetEscapades.AspNetCore.SecurityHeaders), mainly because it was easy to create a Nonce and implement this at the frontend with a tag helper. This package sets the security headers by using the method app.UseSecurityHeaders(), possible to pass a PolicyCollection as a parameter (like I did) for custom configuration.

    So with the way the package works I need to set the headers with that method on the IApplicationBuilder, and not like you directly on the context. Would it be unpossible to use this package then, if I understand it correctly from you?

  • Huw Reddick 1929 posts 6697 karma points MVP 2x c-trib
    Sep 29, 2023 @ 09:24
    Huw Reddick
    0

    Just had a quick look and I don't see any reason why you couldn't, so perhaps you have not implemented it quite right.

    Where exactly in your startup.cs did you add app.UseSecurityPolicy();

  • Grakify 13 posts 80 karma points
    Sep 29, 2023 @ 09:29
    Grakify
    0

    I put it here:

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {

            app.UseResponseCompression();
            app.UseWebP();
            app.UseImageSharp();
            app.UseSession();
    
            //Custom middleware to make a distinction between /umbraco and non-/umbraco for Content Security Protocol header
            **app.UseSecurityPolicy();**
    
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
    
                var rewriteOptions = new RewriteOptions().AddIISUrlRewrite(env.ContentRootFileProvider, "IISUrlRewriteDev.xml");
                app.UseRewriter(rewriteOptions);
            }
            else
            {
                app.UseHttpsRedirection();
                app.UseHsts();
    
                var rewriteOptions = new RewriteOptions().AddIISUrlRewrite(env.ContentRootFileProvider, "IISUrlRewrite.xml");
                app.UseRewriter(rewriteOptions);
            }
    
            // This line is needed for the rewrites to take effect.
            app.UseStaticFiles();
    
            //AppDomain.CurrentDomain.SetData("HostEnvironment", env);
            app.UseDownloadRequestMiddleware();
    
            app.UseOutputCache();
            app.UseResponseCaching();
    
            app.UseUmbraco()
                .WithMiddleware(u =>
                {
                    u.UseBackOffice();
                    u.UseWebsite();
                })
                .WithEndpoints(u =>
                {
                    u.UseCustomRoutingRules();
                    u.UseInstallerEndpoints();
                    u.UseBackOfficeEndpoints();
                    u.UseWebsiteEndpoints();
                });
        }
    

    Sorry for the weird formatting at the start. The app.UseSecurityPolicy(); is towards the start of the code.

  • Huw Reddick 1929 posts 6697 karma points MVP 2x c-trib
    Sep 29, 2023 @ 09:40
    Huw Reddick
    0

    in your code, where are you defining policycollection?

    In the packages examples, it does this

    var policyCollection = new HeaderPolicyCollection()
    .AddContentSecurityPolicy(builder =>
    
  • Grakify 13 posts 80 karma points
    Sep 29, 2023 @ 09:44
    Grakify
    0

    Ahh, I see I omitted that part. I do it here:

    public static class SecurityPolicyAppBuilderExtension {

    public static HeaderPolicyCollection policyCollection = new HeaderPolicyCollection();

        public static IApplicationBuilder UseSecurityPolicy(this IApplicationBuilder app) => app
    
          .UseWhen(x => !IsBackOffice(x), builder =>
          {
              policyCollection
              .AddContentTypeOptionsNoSniff()
              .AddXssProtectionBlock()
              .AddFrameOptionsSameOrigin()
              .AddStrictTransportSecurityMaxAge()
              .AddReferrerPolicyStrictOriginWhenCrossOrigin()
              .RemoveServerHeader()
              .AddPermissionsPolicy(builder =>
              {
                  builder.AddAccelerometer();
                  builder.AddCamera();
                  builder.AddFullscreen();
                  builder.AddGeolocation();
                  builder.AddGyroscope();
                  builder.AddMagnetometer();
                  builder.AddMicrophone();
              })
              .AddContentSecurityPolicy(builder =>
              {
                  builder.AddDefaultSrc()
                      .Self();
    
                  builder.AddScriptSrc()
                      .Self()
                      .WithNonce()
                      .From("https://www.google-analytics.com")
                      .From("https://consentcdn.cookiebot.com")
                      .From("https://consent.cookiebot.com")
                      .From("https://consent.cookiebot.com/Scripts/widgetIcon.min.js")
                      .From("https://consentcdn.cookiebot.com/consentconfig/3d82664e-d60b-4a2a-bdaa-cd426292c836/state.js")
                      .From("https://cdn.jsdelivr.net")
                      .From("https://kit.fontawesome.com")
                      .From("https://ajax.googleapis.com")
                      .From("https://unpkg.com")
                      .From("https://www.youtube.com/")
                      .From("https://ajax.aspnetcdn.com")
                      .From("https://cdnjs.cloudflare.com/");
    
                  builder.AddStyleSrc()
                      .Self()
                      .UnsafeInline() // Has to be removed later! Removing it makes Cookiebot adding inline style elements break
                      .From("https://fonts.googleapis.com")
                      .From("https://cdn.jsdelivr.net")
                      .From("https://cdnjs.cloudflare.com")
                      .From("https://fonts.gstatic.com")
                      .From("https://unpkg.com")
                      .From("https://consent.cookiebot.com/Scripts/widgetIcon.min.js")
                      .From("https://consent.cookiebot.com/uc.js");
    
                  builder.AddImgSrc()
                      .Self()
                      .Data()
                      .From("https://www.google-analytics.com")
                      .From("https://cdn.jsdelivr.net")
                      .From("https://img.youtube.com/")
                      .From("https://i.ytimg.com/");
    
                  builder.AddFontSrc()
                      .Self()
                      .Data()
                      .From("https://fonts.gstatic.com")
                      .From("https://cdnjs.cloudflare.com");
    
                  builder.AddConnectSrc()
                      .Self()
                      .From("https://www.google-analytics.com")
                      .From("https://consentcdn.cookiebot.com")
                      .From("https://*.algolianet.com")
                      .From("https://*.algolia.net")
                      .From("https://*.algolia.io")
                      .From("https://cdnjs.cloudflare.com");
    
                  builder.AddFrameSrc()
                      .Self()
                      .From("https://www.youtube.com")
                      .From("https://youtube.com")
                      .From("https://consentcdn.cookiebot.com");
    
                  builder.AddObjectSrc()
                      .None();
    
                  builder.AddBaseUri()
                      .Self();
    
                  builder.AddFormAction()
                      .Self();
    
                  builder.AddFrameAncestors()
                      .None();
              });
    
              app.UseSecurityHeaders(policyCollection);
          })
          .UseWhen(IsBackOffice, a =>
          {
              policyCollection
              .AddContentTypeOptionsNoSniff()
              .AddXssProtectionBlock()
              .AddFrameOptionsSameOrigin()
              .AddStrictTransportSecurityMaxAge()
              .AddReferrerPolicyStrictOriginWhenCrossOrigin()
              .RemoveServerHeader()
              .AddPermissionsPolicy(builder =>
              {
                  builder.AddAccelerometer();
                  builder.AddCamera();
                  builder.AddFullscreen();
                  builder.AddGeolocation();
                  builder.AddGyroscope();
                  builder.AddMagnetometer();
                  builder.AddMicrophone();
              })
              .AddContentSecurityPolicy(builder =>
              {
                  builder.AddDefaultSrc()
                      .Self();
    
                  builder.AddScriptSrc()
                      .Self().UnsafeInline().UnsafeEval();
    
                  builder.AddStyleSrc()
                      .Self().UnsafeInline();
    
                  builder.AddImgSrc()
                      .Self()
                      .Data()
                      .From("dashboard.umbraco.com")
                      .From("our.umbraco.com");
    
                  builder.AddConnectSrc()
                      .Self()
                      .From("our.umbraco.com");
    
                  builder.AddObjectSrc()
                      .None();
              });
    
              app.UseSecurityHeaders(policyCollection);
          });
    
        private static bool IsBackOffice(HttpContext context) => context.Request.Path.StartsWithSegments(new PathString("/umbraco"));
    }
    

    Again sorry for the shitty formatting. It's the public static HeaderPolicyCollection policyCollection = new HeaderPolicyCollection(); almost at the top, it's at the same level as the UseSecurityPolicy method.

  • Huw Reddick 1929 posts 6697 karma points MVP 2x c-trib
    Sep 29, 2023 @ 10:00
    Huw Reddick
    0

    I can't see any reason it wouldn't work. I will have a little play later myself.

    Do you get any errors or does it just not add the header?

  • Grakify 13 posts 80 karma points
    Sep 29, 2023 @ 10:25
    Grakify
    0

    Thank you! I reallt appreciate that. Have been struggling a while to get it work but kinda stuck now. These are the headers that are being set (same for / as /umbraco):

    enter image description here

    Weirdly enough the other headers are being set, except for the CSP one. When I put this code directly in the Configure method in Startup.cs, without the backoffice check, I get all the headers set correctly, also CSP.

  • Huw Reddick 1929 posts 6697 karma points MVP 2x c-trib
    Sep 29, 2023 @ 10:28
    Huw Reddick
    0

    so it may possibly be your backoffice check that is failing then

  • Grakify 13 posts 80 karma points
    Sep 29, 2023 @ 10:47
    Grakify
    0

    Hmm yes but then it would be weird that the other headers are being set correctly:

    .AddContentTypeOptionsNoSniff()

          .AddXssProtectionBlock()
          .AddFrameOptionsSameOrigin()
          .AddStrictTransportSecurityMaxAge()
          .AddReferrerPolicyStrictOriginWhenCrossOrigin()
    
  • Grakify 13 posts 80 karma points
    Sep 29, 2023 @ 10:58
    Grakify
    1

    Ok so I just found the solution. Not declaring the policyCollection:

    public static HeaderPolicyCollection policyCollection = new HeaderPolicyCollection();

    at the top outside of the method, but inside the method, like:

    public static IApplicationBuilder UseSecurityPolicy(this IApplicationBuilder app) => app.UseWhen(x => !IsBackOffice(x), appBuilder => {

              var policyCollection = new HeaderPolicyCollection()
              .AddContentTypeOptionsNoSniff()
              .AddXssProtectionBlock()
              .AddFrameOptionsSameOrigin()
    

    ...Et cetera.

    Must have been some kind of scope issue?? Kind of weird but I'm glad it's solved now. Thank you anyway for taking the time to think towards a solution with me, really appreciate it :) And again sorry for the shitty formatting of the code :')

  • Huw Reddick 1929 posts 6697 karma points MVP 2x c-trib
    Sep 29, 2023 @ 11:05
    Huw Reddick
    1

    No problem, glad you got it sorted 🙂

Please Sign in or register to post replies

Write your reply to:

Draft