ASP.NET Core Application Insights JS With Content Security Policy

This post assumes you’ve added Application Insights to an ASP.NET Core application by following the post available at https://github.com/Microsoft/ApplicationInsights-aspnetcore/wiki/Getting-Started-with-Application-Insights-for-ASP.NET-Core.

That post states that you must add @Html.Raw(JavaScriptSnippet.FullScript) to the head of the _Layout.cshtml, by doing this an inline script is added to that file _Layout.cshtml . This will cause the browser to prevent execution of that script because of Content Security Policy (CSP).

Content Security Policy

More detailed information is available here: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP

CSP is a browser level security measure to mitigate some types of attacks including cross site scripting attacks. To use CSP Content-Security-Policy: <policy> needs to be added to the response header. These policy can include among other things the sources of script files, reporting location if there is an attack, and allowing inline scripts to execute.

There are 2 options to allow running inline scripts, the first is to use a nonce and the second is a sha256, sha384 or sha512 hash. This example will the nonce approach. For details about the CSP properies being set in the post go to: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src.

Implementation

Application Insights JS Snippet Helper

The ApplicationInsightsJsHelper class will set the nonce value to the inline script HTML tag and be referenced in the _Layout.cshtml file.

using Microsoft.ApplicationInsights.AspNetCore;
using Microsoft.AspNetCore.Http;

namespace Services
{
    public class ApplicationInsightsJsHelper
    {
        private readonly IHttpContextAccessor _httpContext;
        private readonly JavaScriptSnippet _aiJavaScriptSnippet;
        
        public ApplicationInsightsJsHelper(IHttpContextAccessor httpContext, JavaScriptSnippet aiJavaScriptSnippet)
        {
            _httpContext = httpContext;
            _aiJavaScriptSnippet = aiJavaScriptSnippet;
        }

        public string Script
        {
            get
            {
                var js = _aiJavaScriptSnippet.FullScript;
                const string scriptTagStart = @"<script type=""text/javascript"">";
                var scriptTagStartWithNonce = $"<script type=\"text/javascript\" nonce=\"{_httpContext.HttpContext.Items["csp-nonce"]}\">";

                var script = js.Replace(scriptTagStart, scriptTagStartWithNonce);
                return script;
            }
        }
    }
}

This class is doing on thing, substituting the JS inline tag with one that includes the nonce. (Lines 21-25)

CSP Header

To add the CSP to the header of the application add the following to the Startup.cs (this must be before app.UseMvc)

app.Use(async (ctx, next) =>
{
    var nonce = Guid.NewGuid().ToString("N");
    ctx.Response.Headers.Add("Content-Security-Policy",
        $"script-src https://az416426.vo.msecnd.net 'self' \'nonce-{nonce}\'");

    ctx.Items["csp-nonce"] = nonce;
    await next();
});

This function is doing two things:

  1. Adds the CSP header, and allows source files from selfhttps://az416426.vo.msecnd.net, and the nonce value which is just a GUID. This will change depending on your situation but the nonce and https://az416426.vo.msecnd.net must be included. The URL is because the AI code snippet includes code to download the AI script from that location. (see above for more information about what can be set in the script-scr)
  2. Setting the Nonce property in the ctx.Items so the helper class can access it for this request.

_Layout.cshtml

The last step is to add the ApplicationInsightsJsHelper to the _Layout.cshtml file.

This project is using dependency injection (as ASP.NET Core application should) so the helper class needs to be injected.

@inject OA.ID.Services.ApplicationInsightsJsHelper AiJsHelper

To get the inline script with the nonce replace @Html.Raw(JavaScriptSnippet.FullScript) with @Html.Raw(AiJsHelper.Script)

Now when you run the application the browser will allow the inline script to run and download the additional script file.

You Might Also Like
Leave a Reply