Over the past couple of days I have been deep diving into CSP. To consolidate my knowledge and share some insights with you, I decided to write it down into a blog post. Nothing fancy, just some takeaway’s that could be handy to someone else.
What is unsafe-inline?
Unsafe-inline are scripts or style that have been added directly into the HTML DOM instead of a separate stylesheet or JavaScript file. This can be a <script><script/>
block, a <styling> <styling/>
block or a style attribute on an element such as<div style="display: none"><div/>
.
Why should I block unsafe-inline of styling?
The main risk of using unsafe-inline is the risk of a Cross Site scripting attack (XSS). Imagine that you are able to post a comment on this post that will be displayed in a different manner than the programmer intended to.
It will be displayed in such malicious way, not only to you and the admin, but also to all the other visitors of this post. Incase of unsafe inline scripts this could mean running a script in the browser of each visitor to mine crypto.
Incase of unsafe inline css, it could be used to modify the website to look like something else and tricking you to click on something you did not intend to.
Disabeling unsafe-inline in style-src
To disable the unsafe-inline, open the configuration of your web application. In ASP.NET this could be the web.config that contains the “Content-Security-Policy”. Make sure that the Content-Security-Policy is enabled and that the style-src does not contain a ‘unsafe-inline’ entry.
After disabling unsafe-inline, all inline styles will be blocked. This could mean that some colors are not being loaded. Some font-sizes are different then before the change. Basically all the things that were styled by inline style elements are now styled using a higher level/fallback style.
Additionally, in the browsers console several errors might be visible. Related to unsafe inline CSP.
If you do not have any errors. Great! keep that best-practices spirit alive!
If you have allot of errors, do not be afraid! We shall slaughter those fitly creatures one by one.
How to fix “Refused to apply inline style because it violates the following Content Security Policy directive” errors?
Inline scripts might have 3 sources. Your own code that you actively manage, Your own legacy code that you would rather not touch as it will bring you into the dark corners of your past. Who knows what skeletons are left in those dungeons? Lastly, code that you do not manage, this could be an external library, a framework such as Vue.js or KnockOut.
Each of these have different considerations.
Your own code that you actively manage
Plain and simple. STOP using inline styles such as:
<style>
body {
background-color: linen;
}
h1 {
color: maroon;
margin-left: 40px;
}
</style>
Or
<div style="display: none"><div/>
Just DO NOT DO IT!
Use stylesheets instead. Give the elements a proper selector such as a class or id. And add it to the stylesheet(.css file).
One more warning: moving inline style to a .css file and adding !important is not the solution. Inline styling has precedents over all other selectors, so in some cases you will need to be more specific when selecting the element from a stylesheet. But using !important is like nuking a fly, it might seem effective in the short term, but boy-oh-boy it will create some ugly mutant bugs in the future.
Your own legacy code that you would rather not touch
Aah yes…. remember those good old times, when you knew that using !important is not the right solution, but you used it anyway “to safe time”?
And now you are wondering: how in the world will I move all those inline styles combined with !important statements to properly structured .css classes? I would rather not touch this pile of shit and forget that it ever existed. But my CISO is very eager to solve those OWAS ZAP issues, so I need to do something with it….
Unsafe Hashes to the rescue! By hashing the content of an inline style we can declare the piece of style as safe.
So for example you have <div style="display: none"><div/>
by hashing
and adding it to the style-src CSP directive. The browser will trust that piece styling. display: none
But why is it still called unsafe? Well… If you have allot of hashes, a hacker could still potentially insert a piece of styling at a place that was not intended. However, with unsafe-inline, the hacker can insert whatever styling they wish. With unsafe-hashes, only the content that you have hashed will be usable. So the less unsafe-hashes the safer. Look at it as a whitelisted list of styling while unsafe-inline is no restrictions at all.
But here comes the curveball: hashes look at the content between the <style><style/>
tags and between the quotes in <div style="display: none"><div/>
. This means that display: none
will result in a hash of 'sha256-ZdHxw9eWtnxUb3mk6tBS+gIiVUPE3pGM470keHPDFlE='
, display:none
will result in 'sha256-aqNNdDLnnrDOnTNdkJpYlAxKVJtLt9CtFLklmInuUAE='
and display:none;
will result in 'sha256-0EZqoz+oBhx7gF4nvY2bSqoGyy4zLjNF+SDQXGp/ZrY='
. Three correct styles, that result in exactly same thing, but due to a single character difference they are perceived as completely different hashes.
To reduce number of hashes, make sure to consolidate and make the styling consistent. What I did, was search the entire solution for ” style=” and <style>. And basically changed all the variations of the same style to be exactly the same.
One more warning: Hashing large style entries that are susceptible to change will be painful as even a very small change, a space or comma, will result in a different hash that should be updated in the web.config. Large inline styles should be moved to a separate stylesheet. Which means the code will become less maintainable, developers will prevent making small improvements due to the overhead and risk of introducing a runtime bug due to hash mismatch.
Code that you do not manage
Ah yes, frameworks and external libraries. They save so much time, until you reach the boundary of it’s capabilities. Considering that we are not able to change the working of an external library we are at the mercy of supplier/community for fixing inline styles.
Some libraries, might have some options to compile/generate stylesheets instead of injecting style directly into the dom. Check those options first. For small inline snippets of css hashing can be used to make sure that piece of styling is secure. For larger pieces of inline style the same issue arises. Small changes to the style will result in a new hash. Considering this issue out side of out control, hashing large styles of external libraries is highly undesireable.
There are some JavaScript libraries that could potentially extract all <style> elements into separate files by post-processing. I am not sure if this will work with style attributes on html elements.
if an external library has a lot of issues, consider using a different one.
CSP and generated style
Sometimes, *sarcasm on*, a very smart person decided to generate html by creating html in plain string with parameters. For example: $"<div style=""width: {percentageInt}%""></div>"
. This results in a completely different hash depending on the percentage. Therefore hashes are useless in case of such “dynamic” content. However css manipulation via JavaScript and CCSOM is possible for example: obj.style.width = '50%';
does not trigger a CSP error. Therefore rewrite such html generation to JavaScript that changes the style in the CSSOM.
Considerations before disabling unsafe-inline for style-scp
- Manual regression testing is most likely required. Current testing framework do not properly handle CSP errors. Perhaps I am missing something at this moment, but I will update this post in the future.
- Updating external libraries for which you had created hashes, will require regression testing the existing hashes and perhaps regenerating the new ones.
- After disabling unsafe-inline, all inline that has no valid hash, will not be rendered. A normal user will not see any errors. Only if you look in the browser console.
- Missing style sheets might result in something small like a font size change of a couple of px. Or a completely broken page incase of large stylesheets from the JavaScript framework.
- Combining unsafe-hashes and unsafe-inline is not possible. If you enable unsafe-hashes, all inline styles without a hash will fail.
I hope this will be useful to some of you guys 🙂