-
Notifications
You must be signed in to change notification settings - Fork 287
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Content Security Policy with script-src 'self' prevents Application Insights javascript running #1443
Comments
I created a quick and dirty work around for this issue in my project. NOTE: I've updated the post to fix a bug. The original post stored the nonce in a static (I was clearly not thinking when I wrote it). It is now stored in the request context Items property. I also wrote a more detailed blog post today, which can be found at: http://robanstett.com/blog/asp-core-ai-csp/ First I added a Content-Security Policy by adding the following to my startup (this will probably be different for you. This must be added prior to app.UseMvc.
Next I created a helper class to get the AI script and add to the header of it the Nonce Code I put in the Content-Security-Policy. Here is a the class:
|
I have a similar issue but would prefer that a solution was considered that would allow the AI snippet to populate the instrumentation I have a separate script like below: var aiScript = document.querySelector('#ai-script-snippet');
if(aiScript) {
var key = key : aiScript.getAttribute('data-ai-key');
var userId = aiScript.getAttribute('data-ai-userid');
if(key) {
var appInsights=window.appInsights||function(config){
function i(config){t[config]=function(){var i=arguments;t.queue.push(function(){t[config].apply(t,i)})}}var t={config:config},u=document,e=window,o="script",s="AuthenticatedUserContext",h="start",c="stop",l="Track",a=l+"Event",v=l+"Page",y=u.createElement(o),r,f;y.src=config.url||"https://az416426.vo.msecnd.net/scripts/a/ai.0.js";u.getElementsByTagName(o)[0].parentNode.appendChild(y);try{t.cookie=u.cookie}catch(p){}for(t.queue=[],t.version="1.0",r=["Event","Exception","Metric","PageView","Trace","Dependency"];r.length;)i("track"+r.pop());return i("set"+s),i("clear"+s),i(h+a),i(c+a),i(h+v),i(c+v),i("flush"),config.disableExceptionTracking||(r="onerror",i("_"+r),f=e[r],e[r]=function(config,i,u,e,o){var s=f&&f(config,i,u,e,o);return s!==!0&&t["_"+r](config,i,u,e,o),s}),t
}({
instrumentationKey: key
});
window.appInsights=appInsights;
if(userId) {
appInsights.setAuthenticatedUserContext(userId);
}
appInsights.trackPageView();
}
} This is a separate script that is then satisfied by the 'self' directive, I then include an id selector used to query an element that will contain the values needed to populate the AI snippet, this can then target any element to query the values: <html lang="en" id="ai-script-snippet" data-ai-key="@TelemetryConfiguration.Active.InstrumentationKey" data-ai-userid="@(User.Identity.IsAuthenticated ? User.Identity.Name : null)">
<script src="/scripts/my-ai-snippet.js"></script>
</html> or <html>
<script id="ai-script-snippet" data-ai-key="@TelemetryConfiguration.Active.InstrumentationKey" data-ai-userid="@(User.Identity.IsAuthenticated ? User.Identity.Name : null)" src="/scripts/my-ai-snippet.js"></script>
</html> or even (perhaps this could work as an update to this library?) <html>
<script id="ai-script-snippet" data-ai-key="@TelemetryConfiguration.Active.InstrumentationKey" data-ai-userid="@(User.Identity.IsAuthenticated ? User.Identity.Name : null)">
var aiScript = document.querySelector('#ai-script-snippet');
if(aiScript) {
var key = key : aiScript.getAttribute('data-ai-key');
var userId = aiScript.getAttribute('data-ai-userid');
if(key) {
var appInsights=window.appInsights||function(config){
function i(config){t[config]=function(){var i=arguments;t.queue.push(function(){t[config].apply(t,i)})}}var t={config:config},u=document,e=window,o="script",s="AuthenticatedUserContext",h="start",c="stop",l="Track",a=l+"Event",v=l+"Page",y=u.createElement(o),r,f;y.src=config.url||"https://az416426.vo.msecnd.net/scripts/a/ai.0.js";u.getElementsByTagName(o)[0].parentNode.appendChild(y);try{t.cookie=u.cookie}catch(p){}for(t.queue=[],t.version="1.0",r=["Event","Exception","Metric","PageView","Trace","Dependency"];r.length;)i("track"+r.pop());return i("set"+s),i("clear"+s),i(h+a),i(c+a),i(h+v),i(c+v),i("flush"),config.disableExceptionTracking||(r="onerror",i("_"+r),f=e[r],e[r]=function(config,i,u,e,o){var s=f&&f(config,i,u,e,o);return s!==!0&&t["_"+r](config,i,u,e,o),s}),t
}({
instrumentationKey: key
});
window.appInsights=appInsights;
if(userId) {
appInsights.setAuthenticatedUserContext(userId);
}
appInsights.trackPageView();
}
}
</script>
</html> ..and then include a fixed csp hash of |
Is there any official response on this? The automatic light-up feature of AppInsights in core 2.0 injects a code snippet that causes script errors when using a strict CSP policy. Not having a method to disable the script injection without a big hammer ( |
This may well be frowned upon and get me barred from the "acceptable code" club but I'm using a slightly modified, but equally hacky, approach. I added the script manually with a nonce/hash attribute on the tag (as described in various C# + CSP articles) but then found that the AI startup code still insisted on putting a second copy of the script into the page - thus breaking the CSP. There doesn't seem to be a (straightforward) way of preventing it doing that so I used a bit of reflection in the Startup.ConfigureServices method to replace the script:
This allows the rest of the AI stuff to work as normal and just prevents a second copy of the snippet in my page. It's dirty and not strictly future proof but it works until they come up with a more acceptable solution. |
Yesterday crypto-mining exploit against Browsealoud further outlines the need for a robust solution that allows for secure inclusion of scripts such as AppInsights. The irony here is that the very inclusion of AppInsights allowed me to investigate the extent of the exploit on sites I maintain, but the need for a lax Content Security Policy for AppInsights also allowed for the Browsealoud exploit to happen. This is a wake up calls for a lot of people that read about CSP and did nothing, and for software and script vendors to take heed of the warnings of this incident to better secure their own resources and also support browser mechanics to aid in the protection of websites using these scripts. |
I had the same issue in trying to implement CSP on my site. The workaround from @shunty-gh worked great, and should continue to work until Microsoft changes a namespace, type, or field name. |
Thanks @shunty-gh I've applied your workaround too |
I'm having a related issue implementing a strict-dynamic, nonce based CSP with no host whitelist. This workaround won't work for a The injected JS snippet builds another script tag which means we can't easily pass a nonce into the script when it's included in the page. I can manually do it using some convoluted string parsing but this is fragile if the generated snippet ever changes the script it writes. Can I raise a feature request to support CSP nonces in the JS snippet? |
@garethterrace I think that raises a larger question of, if this were to be implemented, how and where would the I think this is something that has to be supported natively throughout ASP.NET Core (if it's not planned or in progress already), and then the Application Insights library needs to take advantage of it. |
I'm new to CSP and had the same problem. Adding the sha to scrpit-src solved it quickly and easily. You'd have to change the sha if the script was changed but that isn't often afaik. I'm not up to speed on dynamic script in csp 3 yet, if it allows only nonces the sha won't work. Like I said I'm new to this side of things so could very easily be wrong. Checking my headers with various tools seems to confirm what I think you can do. Please feel free to correct me if I'm talking out of my hat, I'm keen to learn where I'm mistaken. |
Any update on this? Does ASP.NET Core handle script nonces out of the box, or how is CSP being handled going forward with regards to the App Insights injected script tag? |
For reference, the snipped comes from here: ApplicationInsights-dotnet/NETCORE/src/Microsoft.ApplicationInsights.AspNetCore/JavaScriptSnippet.cs Lines 62 to 64 in d032dbf
I'm reading up on CSP and I'm not aware of any mechanism in AspNetCore to provide a nonce for us.
So the immediate question I have is if you implement your own nonce, where would you store it that we could read it and apply it to the script tag? |
@TimothyMothra I have a home-grown It works very well, except when stuff wants to write One possible solution would be, if we had the capability to do something like this: <script auto-nonce="true">
@Microsoft.ApplicationInsights.AspNetCore.JavaScriptSnippet.WriteSnippet(false);
</script> With the signature of that method being something like: This method, if called, would also somehow need to detect that it exists or was invoked on a Razor page, and not write the automatic snippet in that case. (Not sure it's possible to detect if this method exists in the Razor compilation context ahead of time or not.) Another solution would simply be to read a specific config value from the ASP.NET Core config provider (we already have This may work well in conjunction with the manual snippet writing above. |
@qJake thanks for your detailed reply! |
This solution would be great. I use a also helper lib to manage my nonces. |
So the plan is to add a new public property:
I'm going to try to get a PR for this today or tomorrow. |
@TimothyMothra This is awesome. And just to come full circle so I understand how this is going to work... if we look at the client-side snippet documentation, it says to add this line: @Html.Raw(JavaScriptSnippet.FullScript) Which, for this PR, we can optionally change to: <script auto-nonce="true"> @* Or whatever your nonce strategy is... *@
@Html.Raw(JavaScriptSnippet.ScriptBody)
</script> Correct? 😁 |
Correct! |
I love it! I can't think of any scenarios where this wouldn't be flexible. And I suppose that if someone decided to define the script tag as, say, |
merged. Expect this change to be available in 2.14-beta3. Should be released this or next week. |
Hi I'm having trouble getting this working with these packages. <PackageReference Include="NWebsec.AspNetCore.Middleware" Version="3.0.0" />
<PackageReference Include="NWebsec.AspNetCore.Mvc.TagHelpers" Version="3.0.0" />
<PackageReference Include="Microsoft.ApplicationInsights.AspNetCore" Version="2.17.0" /> In my startup code I have these csp headers configured: app.UseCsp(options =>
{
options.ScriptSources(s =>
{
s.StrictDynamic()
.CustomSources("https:", "http:")
.Self()
.UnsafeInline();
//s.UnsafeInline().CustomSources("https://az416426.vo.msecnd.net");
});
}); After that I updated my cshtml to this: <script nws-csp-add-nonce="true">
@Html.Raw(JavaScriptSnippet.ScriptBody)
</script> After I load the page the following snippet is added. <script src="https://az416426.vo.msecnd.net/scripts/b/ai.2.min.js"></script> However it has no nonce so csp refuses to load it.
Am I missing something? |
Add "https://az416426.vo.msecnd.net" to script-src or use a SHA directive. Nonces seemed too difficult to me to get working but the other 2 options work fine. |
In Shared/_Layout I have added the following line within the
<head>
section of my application:@Html.Raw(JavaScriptSnippet.FullScript)
Which correctly outputs the Application Insights javascript, inline. However, my application has the following header in the response for security reasons...
Content-Security-Policy:script-src 'self';
... which prevents inline javascript. Therefore, I receive the following error in the browser console (chrome):
The text was updated successfully, but these errors were encountered: