Click here to Skip to main content
13,146,014 members (42,664 online)
Click here to Skip to main content
Add your own
alternative version

Tagged as

Stats

1.6K views
2 bookmarked
Posted 12 Sep 2017

Using CSP Header in ASP.NET Core 2.0

, 12 Sep 2017
Rate this:
Please Sign up or sign in to vote.
How to use Content-Security-Policy header in ASP.NET Core to prevent XSS attacks. Continue reading

Problem

How to use Content-Security-Policy header in ASP.NET Core to prevent XSS attacks.

Solution

Create an empty project and update Startup to include service and middleware for MVC:

public void ConfigureServices(
            IServiceCollection services)
        {
            services.AddMvc();
        }

        public void Configure(
            IApplicationBuilder app, 
            IHostingEnvironment env)
        {
            app.UseDeveloperExceptionPage();

            app.Use(async (context, next) =>
            {
                context.Response.Headers.Add(
                    "Content-Security-Policy",
                    "script-src 'self'; " +
                    "style-src 'self'; " +
                    "img-src 'self'");

                await next();
            });

            app.UseStaticFiles();
            app.UseMvcWithDefaultRoute();
        }
    }

Add a _Layout page and include scripts, styles and images from local (wwwroot) and remote domains:

<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>

    <link rel="stylesheet" href="~/css/site.css" />
    <link rel="stylesheet" 

          href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.5/css/bootstrap.min.css" />
</head>
<body>
    <img src="~/img/site-banner.jpg" />
    <img src="https://media-www-asp.azureedge.net/media/5245130/home-hero-2.png" />

    <div>
        @RenderBody()
    </div>

    <script src="~/js/site.js"></script>
    <script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.2.1.min.js"></script>
</body>
</html>

Run and observe the errors when loading the page, browser has rejected loading from remote sources:

Discussion

Content Security Policy (CSP) is an additional level of security that could help prevent Cross Site Scripting (XSS) attacks. In these attacks malicious scripts are executed on user’s browser since browser doesn’t know whether the source of the script is trustworthy or not.

CSP allow developers to specify the sources (domains) that trustworthy and can serve executable scripts. This whitelisting of domains is achieved by using Content-Security-Type HTTP header like:

Content-Security-Policy: [policy]

Here [policy] is made up of directives describing the type of restrictions and domains to whitelist. It is a string defining a series of directives and sources separated by semicolon:

[directive] <source> <…source>; [directive] <source> <…source>; …

Sources

Sources are the trusted domains and commonly used ones are:

  • *: allow any URL.
  • ‘self’: allow the origin of served page. Note that single quotes are required.
  • ‘none’: no source should be allowed. Note that single quotes are required.
  • Host: allow a specified internet host (by name or IP address). A wildcard (asterisk character) can be used to include all subdomains e.g. http://*.foo.com
  • ‘unsafe-line’: allow inline scripts
  • ‘nonce-[base64-value]’: allow inline scripts with a specific nonce (number used once). The nonce should be encrypted and unique for every HTTP request/response.

Note: other sources could be found at Mozilla documents for developers, see Other Resources section at the end of this post.

Directives

Directives specify the resource types to put restrictions on. Commonly used ones are:

  • script-src: defines valid sources of JavaScript
  • style-src: defines valid sources of stylesheets
  • img-src: defines valid sources of images
  • connect-src: defines valid sources to which AJAX calls can be made
  • font-src: defines valid sources of fonts
  • object-src: defines valid sources for <object>, <embed> and <applet> elements
  • media-src: defines valid sources of audio and video
  • form-action: defines valid sources that can be used as a HTML <form> action.
  • default-src: specifies the default policy for loading content

Note: other directives could be found at Mozilla documents for developers, see Other Resources section at the end of this post.

Middleware

We could encapsulate the building and adding of CSP header in a reusable middleware. Below is an example to get you started, you could extend as necessary. First create a class to hold sources:

public sealed class CspOptions
    {
        public List<string> Defaults { get; set; } = new List<string>();
        public List<string> Scripts { get; set; } = new List<string>();
        public List<string> Styles { get; set; } = new List<string>();
        public List<string> Images { get; set; } = new List<string>();
        public List<string> Fonts { get; set; } = new List<string>();
        public List<string> Media { get; set; } = new List<string>();
    }

Create a builder we’ll use when setting up the middleware:

public sealed class CspOptionsBuilder
    {
        private readonly CspOptions options = new CspOptions();
        
        internal CspOptionsBuilder() { }

        public CspDirectiveBuilder Defaults { get; set; } = new CspDirectiveBuilder();
        public CspDirectiveBuilder Scripts { get; set; } = new CspDirectiveBuilder();
        public CspDirectiveBuilder Styles { get; set; } = new CspDirectiveBuilder();
        public CspDirectiveBuilder Images { get; set; } = new CspDirectiveBuilder();
        public CspDirectiveBuilder Fonts { get; set; } = new CspDirectiveBuilder();
        public CspDirectiveBuilder Media { get; set; } = new CspDirectiveBuilder();

        internal CspOptions Build()
        {
            this.options.Defaults = this.Defaults.Sources;
            this.options.Scripts = this.Scripts.Sources;
            this.options.Styles = this.Styles.Sources;
            this.options.Images = this.Images.Sources;
            this.options.Fonts = this.Fonts.Sources;
            this.options.Media = this.Media.Sources;
            return this.options;
        }
    }

    public sealed class CspDirectiveBuilder
    {
        internal CspDirectiveBuilder() { }

        internal List<string> Sources { get; set; } = new List<string>();

        public CspDirectiveBuilder AllowSelf() => Allow("'self'");
        public CspDirectiveBuilder AllowNone() => Allow("none");
        public CspDirectiveBuilder AllowAny() => Allow("*");

        public CspDirectiveBuilder Allow(string source)
        {
            this.Sources.Add(source);
            return this;
        }
    }

Now we can create a middleware:

public sealed class CspMiddleware
    {
        private const string HEADER = "Content-Security-Policy";
        private readonly RequestDelegate next;
        private readonly CspOptions options;

        public CspMiddleware(
            RequestDelegate next, CspOptions options)
        {
            this.next = next;
            this.options = options;
        }

        public async Task Invoke(HttpContext context)
        {
            context.Response.Headers.Add(HEADER, GetHeaderValue());
            await this.next(context);
        }

        private string GetHeaderValue()
        {
            var value = "";
            value += GetDirective("default-src", this.options.Defaults);
            value += GetDirective("script-src", this.options.Scripts);
            value += GetDirective("style-src", this.options.Styles);
            value += GetDirective("img-src", this.options.Images);
            value += GetDirective("font-src", this.options.Fonts);
            value += GetDirective("media-src", this.options.Media);
            return value;
        }

        private string GetDirective(string directive, List<string> sources)
            => sources.Count > 0 ? $"{directive} {string.Join(" ", sources)}; " : "";
    }

And an extension method to set it up:

public static class CspMiddlewareExtensions
    {
        public static IApplicationBuilder UseCsp(
            this IApplicationBuilder app, Action<CspOptionsBuilder> builder)
        {
            var newBuilder = new CspOptionsBuilder();
            builder(newBuilder);

            var options = newBuilder.Build();
            return app.UseMiddleware<CspMiddleware>(options);
        }
    }

We can now configure the middleware in Startup class:

app.UseCsp(builder =>
            {
                builder.Defaults
                       .AllowSelf();

                builder.Scripts
                       .AllowSelf()
                       .Allow("https://ajax.aspnetcdn.com");

                builder.Styles
                       .AllowSelf()
                       .Allow("https://ajax.aspnetcdn.com");

                builder.Fonts
                       .AllowSelf()
                       .Allow("https://ajax.aspnetcdn.com");

                builder.Images
                       .AllowSelf()
                       .Allow("https://media-www-asp.azureedge.net/");
            });

Run and observe the network traffic, browser is allowing local and remote resources:

Other Resources

I’ve only scratched the surface of security headers and CSP. These are some of the other useful resources you could look at:

  • Pluralsight course by Troy Hunt discussing security headers
  • Mozilla developer documentation on HTTP headers.
  • Joonas blog and sample for various security headers.

Source Code

GitHub: https://github.com/TahirNaushad/Fiver.Security.BrowserHeaders

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Tahir Naushad
Software Developer (Senior)
United Kingdom United Kingdom
Qualified and skilled professional with experience working as a Software Developer, Technical Lead and Architect. I have worked on windows, web and distributed applications using the latest set of technologies within the Microsoft .NET ecosystem. I have lead teams using agile methodologies and trained developers in writing well-designed and maintainable software applications.

Currently focusing on ASP.NET Core, C#, Azure, Angular and Domain Driven Design.

You may also be interested in...

Pro
Pro

Comments and Discussions

 
GeneralNeed a article on repository and data access code unit testing Pin
Mou_kol17-Sep-17 22:51
memberMou_kol17-Sep-17 22:51 
GeneralRe: Need a article on repository and data access code unit testing Pin
Tahir Naushad17-Sep-17 23:11
memberTahir Naushad17-Sep-17 23:11 
GeneralRe: Need a article on repository and data access code unit testing Pin
Mou_kol19-Sep-17 23:54
memberMou_kol19-Sep-17 23:54 
GeneralRe: Need a article on repository and data access code unit testing Pin
Tahir Naushad19-Sep-17 23:59
memberTahir Naushad19-Sep-17 23:59 
Questionfew question about Content-Security-Policy Pin
Mou_kol12-Sep-17 23:01
memberMou_kol12-Sep-17 23:01 
AnswerRe: few question about Content-Security-Policy Pin
Tahir Naushad13-Sep-17 3:19
memberTahir Naushad13-Sep-17 3:19 
GeneralRe: few question about Content-Security-Policy Pin
Mou_kol14-Sep-17 3:16
memberMou_kol14-Sep-17 3:16 
GeneralRe: few question about Content-Security-Policy Pin
Tahir Naushad14-Sep-17 3:32
memberTahir Naushad14-Sep-17 3:32 
GeneralRe: few question about Content-Security-Policy Pin
Mou_kol17-Sep-17 22:49
memberMou_kol17-Sep-17 22:49 
GeneralRe: few question about Content-Security-Policy Pin
Tahir Naushad17-Sep-17 23:07
memberTahir Naushad17-Sep-17 23:07 
PraiseRe: few question about Content-Security-Policy Pin
Mou_kol19-Sep-17 23:52
memberMou_kol19-Sep-17 23:52 
GeneralRe: few question about Content-Security-Policy Pin
Tahir Naushad19-Sep-17 23:57
memberTahir Naushad19-Sep-17 23:57 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.170915.1 | Last Updated 12 Sep 2017
Article Copyright 2017 by Tahir Naushad
Everything else Copyright © CodeProject, 1999-2017
Layout: fixed | fluid