SVG + CSP: An Unappetizing Alphabet Soup

When I decided to rewrite my personal website, I thought to myself, “Hey, April! You're a security professional who focuses on web security. It would be practically embarrassing if you didn't implement Content Security Policy to demonstrate your competence.”

I simply followed my usual procedure: start with default-src 'none', and keep adding sources until all the console errors go away. That lead me to the following CSP header:

Content-Security-Policy:
    default-src 'none';
    child-src https://www.youtube.com;
    font-src 'self' https://fonts.gstatic.com;
    frame-ancestors 'none';
    frame-src https://www.youtube.com;
    img-src 'self';
    media-src 'self';
    script-src 'self';
    style-src 'self' https://fonts.googleapis.com

I walked away blissfully, knowing that some ne'er-do-well whould be thwarted in their attempts to find any XSS vulnerabilities in my pointless, mostly static personal blog. That is, until I clicked Contact and gasped in horror…

At first, I thought that I had made a mistake with my SVG files, but opening them up directly rendered them in full color. And there was nothing at all in the console log about them, either. After a pile of fruitless Google searches, a tip by my good friend Chuck Harmston led me to consider my CSP policy.

See, SVG files are not just a pile of binary data like most raster images. They're vectors, created mathematically, and defined via XML. The GitHub icon's content looks like this:

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 256 250" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:1.41421;">
    <path d="..." style="fill:rgb(246,188,246);fill-rule:nonzero;"/>
</svg>

Firefox was considering style="fill:rgb(246,188,246)" to be an 'unsafe-inline' style, and therefore blocking it. But it considers it to be an 'unsafe-inline' style inside the SVG's context, hence the lack of console errors. I didn't want to add 'unsafe-inline' to 'style-src' for my entire site, so I ended up creating a completely different CSP directive just for SVGs:

server {
    add_header Content-Security-Policy "default-src 'none'; child-src https://www.youtube.com; font-src 'self' https://fonts.gstatic.com; frame-ancestors 'none'; frame-src https://www.youtube.com; img-src 'self'; media-src 'self'; script-src 'self'; style-src 'self' https://fonts.googleapis.com";

    location ~ \.svg$ {
        add_header Content-Security-Policy "default-src 'none'; frame-ancestors 'none'; style-src 'self' 'unsafe-inline'";
    }
}

The nice thing about this policy is that it has the added bonus of disabling all script execution inside SVG files, itself a huge security risk. In any case, I thought this madness was crazy enough (and undocumented enough) that I opened up a bug for it.

[Category: Standards] [Tags: CSP, SVG, Browser]