Content Security Policy Hashes for Files Don't Seem to Work

279 Views Asked by At

Step 1: Download a raw copy of Bootstrap 4's SHA-384 integrity listed css file, to a local web project: https://getbootstrap.com/docs/4.0/getting-started/download/

As of writing this, they have their hash listed as sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm

Step 2: Reference it in the <head> of a served .html file, as such:

<link rel="stylesheet" href="bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" >

Step 3: Serve a Content-Security-Policy header that includes that hash in its style-src-elem policy, for the html file:

Content Security Policy Header Response

Expected Results: File's sha-384 integrity hash matches the CSP header's hash, file is loaded and works

Actual Results (for all browsers I tested):

Refused to load the stylesheet 'https://localhost:8000/bootstrap.min.css' because it violates the following Content Security Policy directive: "style-src-elem 'sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm' 'sha384-iFY+dCgFnB1nmk59trTsB2i1eTLIUAg+NhcRaA3bWZAqE9HK0ZSnZdOZ9NkoAs8S'".

Independently I can validate that the hash is indeed correct for the file, testing through multiple mechanisms online for generating CSP hashes.

Anyone know what I am missing here? This should work, have I missed a step or misconfigured something?

C# Logic I used to calculate the hash of the file to check, and it outputs the same hash as what Bootstrap supplied:

private static string ComputeFileHash(IFileInfo file)
{
    using var stream = file.CreateReadStream();
    using var reader = new StreamReader(stream);
    using var sha384= SHA384.Create();

    var text = reader.ReadToEnd();
    var utf = Encoding.UTF8.GetBytes(text);
    var hash = sha384.ComputeHash(utf);
    return "'sha384-" + Convert.ToBase64String(hash) + "'";
}

I have an example repo setup here, on the branch CSP-Not-Working-Example.

https://github.com/SteffenBlake/Notes/tree/CSP-Not-Working-Example

It is an Asp.Net application that statically serves the html, css, and js files. It performs an initial calculation on bootup to hash the css and js files, and then adds them to the Content-Security-Policy header for index.html

You can run the application and navigate to https://localhost:8000 and observe both the served headers and browser errors produced as bootstrap.min.css, main.css, and notes.js all get individually rejected.

You can verify if you like that the served file hashes are indeed correct. I even re-downloaded each file from https://localhost:8000/bootstrap.min.css and so on, and re-hashed them again, just to double check that perhaps Asp.Net wasn't modifying the files when it served them, but nope, the hash still comes out to be the same.

For those reporting that the browser gives hints about why the files are rejected, the error I posted up above is a complete copy paste of the error for bootstrap.min.css, a similar error occurs for the other files as well.

That is all the info the browsers are providing to me, at best. Firefox is even more tight lipped about it, providing even less information.

2

There are 2 best solutions below

7
Tore Nestenius On

This code I have used to calculate the CSP hash and it works for me:

    static void Main(string[] args)
    {
        string[] fileEntries = Directory.GetFiles(".");
        foreach (string fileName in fileEntries)
        {
            if (fileName.ToLower().EndsWith(".css") || fileName.ToLower().EndsWith("js"))
                GetChecksum(fileName);
        }
    }

    private static void GetChecksum(string file)
    {
        using (FileStream stream = File.OpenRead(file))
        {
            var sha = new SHA256Managed();
            byte[] checksum = sha.ComputeHash(stream);

            string r = System.Convert.ToBase64String(checksum);

            Console.WriteLine(file + $" \t integrity=\"sha256-{r}\"");

        }
    }
}

in the HTML code, you need to use this, using a script-tag as an exmaple.

so, I guess you lack the crossorigin.

2
Halvor Sakshaug On

In CSP level 2 you can only allow inline scripts/styles with hashes. In CSP level 3 you can allow other scripts by adding 'strict-dynamic' to script-src or default-src. The spec doesn't mention that you can do this for style-src or style-src-elem.

If the file is hosted on the same origin you could instead use 'self' in style-src-elem and the integrity attribute when you are loading the file to enable SubResource Integrity.