Exploring CSP (Content Security Policy) using ASP.NET MVC

In this post, I'll practically show you one promising new defense using CSP (Content Security Policy) that can significantly reduce possible XSS (Cross-Site Scripting) attacks in modern web-browsers (currently Chrome 16+ and Firefox 4+)!

I'll not only block inline/outer scripts but also ask user-agent to auto-generate report for each violated activity it founds that violates my pre-defined policy.

I created an ASP.NET MVC demo app; which is aimed to:
  1. Attach HTTP headers for CSP with each response
  2. Save and preview all reports generated by the browser
                      Download Demo Project


Here is how Google Chrome blocks unallowed scripts:



And the Firefox is blocking unallowed scripts as well:


Here is how Google Chrome generates a report to pre-defined 'report-uri' directive and passes data as 'application/json':



To append CSP headers for each response; we'll add code in "Application_BeginRequest" method in "MvcApplication" class under "Global.asax" file:

void Application_BeginRequest(object sender, EventArgs ex)
{
    /* ======== For Chrome ======== */
    Response.AddHeader("X-WebKit-CSP", "default-src 'self'; img-src *; script-src 'self'; style-src 'self' 'unsafe-inline'; report-uri /Home/Report");

    /* ======== For Firefox ======== */
    Response.AddHeader("X-Content-Security-Policy", "default-src 'self'; img-src *; script-src 'self'; style-src 'self' 'unsafe-inline'; report-uri /Home/Report");
}


As you can see in the above code, I used following two headers for CSP implementation:
  1. "X-Content-Security-Policy" for Firefox
  2. "X-WebKit-CSP" for Chrome
I used 'report-uri' directive to instruct the browser to notify me about any activity that violates my pre-defined policies.

... report-uri /Home/Report

The "Report" action method looks like this:

public JsonResult Report() { return Json(true); }

Remember, POST/GET doesn't matter for this action method! Also the browser will totally ignore what you return with this action. Because: "User Agents MUST NOT honor HTTP 3xx response codes to prevent HTTP header leakage across domains."


I'm blocking all inline as well as outer scripts for possible security gain; however I'm allowing inline-styles because it is difficult for me to not use inline-styles!!!

...; script-src 'self'; style-src 'self' 'unsafe-inline'; ...

"Banning inline script is the biggest security win CSP provides, and banning inline style likewise hardens your application."


To allow all outer scripts from SSL:

script-src https:

To allow inline scripts as well:

script-src https: 'unsafe-inline'

Instead of blocking any resource; you can use report-only header to ONLY get notified of violated activities:

/* ======== For Chrome ======== */
Response.AddHeader("X-WebKit-CSP-Report-Only", "...; report-uri /Home/Report");

/* ======== For Firefox ======== */
Response.AddHeader("X-Content-Security-Policy-Report-Only", "...; report-uri /Home/Report");

"The policy specified in report-only mode won't block restricted resources, but it will send violation reports to the location you specify."



img-src *

I'm also allowing images from all locations (http/https) by using asterisk (*) symbol. To restrict SSL only domains for images:

img-src https: *

Or to allow self-domain ONLY for images:

img-src 'self'

There as so many other directives that you can use to get full control over content that was not possible yesterday. For example, you can use:

  1. connect-src
    Limits the origins to which you can connect (via XHR, WebSockets, and EventSource).

  2. font-src
    Pecifies the origins that can serve web fonts. Google's Web Fonts could be enabled via font-src https://themes.googleusercontent.com

  3. frame-src
    Lists the origins that can be embedded as frames. For example: frame-src https://youtube.com would enable embedding YouTube videos, but no other origins.

  4. media-src
    Restricts the origins allowed to deliver video and audio.

  5. object-src
    Allows control over Flash and other plugins.

Firefox's Behavior regarding CSP

Firefox fails to recognize/parse 'unsafe-inline' source as show in the following figure:

Firefox not only displays CSP Error but also CSP Warning:

CSP ERROR: Couldn't parse invalid source 'unsafe-inline'
CSP WARN: Failed to parse unrecoginzied source 'unsafe-inline'

Firefox is also NOT appending 'document-uri' directive in the report.

Firefox absolute-links the 'blocked-uri' however Google Chrome just appends the origin only.

For "inline script base restriction"; Firefox uses 'self' as 'blocked-uri' value however Google Chrome ignores it.

For further reading:
  1. http://www.w3.org/TR/CSP/
  2. https://wiki.mozilla.org/Security/CSP/Specification
  3. http://www.html5rocks.com/en/tutorials/security/content-security-policy/
Updated at: Jan 15, 2012

Unprefixed support for Content Security Policy in Chrome 25 Beta
Content Security Policy (CSP) helps you reduce the risk of cross-site scripting and other content injection attacks. Starting in today’s Beta release, you can use the unprefixed Content-Security-Policy HTTP header to define a whitelist of trusted content sources. The browser will only execute or render resources from those sources. For example:

Using BackgroundWorker in Windows Forms Application

I created a simple Windows Forms project using C# while taking advantage of BackgroundWorker object to run task at backward thread.

You just have to change/alter only one method: "CallMeBackward" which is called at backward thread in the demo project.



using System;
using System.ComponentModel;
using System.Threading;

namespace BackgroundWorkerInWindowsFormsApp
{
    public partial class Form1 : System.Windows.Forms.Form
    {
        BackgroundWorker _backgroundWorker = new BackgroundWorker();
        
        /*============================================================================*/
        public Form1()
        {
            InitializeComponent();
            
            /*============================================================================*/
            _backgroundWorker.DoWork += new DoWorkEventHandler(BackgroundWorkerDoWorkEvent);
            _backgroundWorker.WorkerSupportsCancellation = true;

            ThreadPool.QueueUserWorkItem(_backgroundWorker.RunWorkerAsync);
            /*============================================================================*/
        }

        /*============================================================================*/
        void BackgroundWorkerDoWorkEvent(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker backgroundWorker = sender as BackgroundWorker;
            string argument = (string)e.Argument;
            e.Result = OnTimeConsumingOperation(backgroundWorker, argument);
        }
        /*============================================================================*/
        string OnTimeConsumingOperation(BackgroundWorker bw, string arg)
        {
            ThreadPool.QueueUserWorkItem(_ =>
            {
                CallMeBackward();
            });
            return string.Empty;
        }
        /*============================================================================*/
        void CallMeBackward()
        {
            for (var i = 0; i < 2000; i++)
            {
                counterLabel.Text = i.ToString();
                timerLabel.Text = DateTime.Now.ToLongTimeString();
            }
        }
        /*============================================================================*/
        protected override void Dispose(bool disposing)
        {
            _backgroundWorker.CancelAsync();
            _backgroundWorker.Dispose();
            /*============================================================================*/
            var timer = new System.Windows.Forms.Timer();
            timer.Tick += (obj, evt) =>
            {
                if (!_backgroundWorker.IsBusy)
                {
                    if (disposing && (components != null))
                    {
                        components.Dispose();
                    }
                    base.Dispose(disposing);
                }
            };
            timer.Start();            
        }

        /*============================================================================*/        
    }
}

UI elements are not thread-safe and should be accessed only on the thread that created them. Otherwise you'll get following error message:

Cross-thread operation not valid: Control 'richTextBox1' accessed from a thread other than the thread it was created on.

BackgroundWorker cannot intercept main thread for UI elements. You've to use main thread instead.