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.