Im am trying to keep my CSP policy as strict as possible. I need to include 3d party component in my bundle. But it uses element.setAttribute('style'...)
method which breaks CSP. Is there a way to allow this particular script to inline styles in that manner?
Im am trying to keep my CSP policy as strict as possible. I need to include 3d party component in my bundle. But it uses element.setAttribute('style'...)
method which breaks CSP. Is there a way to allow this particular script to inline styles in that manner?
- 1 You could sanitize it yourself, use regex checking for script elements and so forth – Trash Can Commented Jun 17, 2017 at 20:08
- I can solve it in this way, but I am afraid to freeze a 3d party component. There is no way to update it in the future. I still hope there is a possibility to fix it with 'nonce' attribute or something like that. – maxvektor Commented Jun 17, 2017 at 20:15
- 1 Google reCAPTCHA is one common source that does this. See related answer at stackoverflow.com/questions/44512798/… – sideshowbarker ♦ Commented Jun 17, 2017 at 21:07
- 1 Many 3rd party libraries don't set styles right, and I have had to 'fix' many of them for our own use because the original authors couldnt be bothered to do it themselves (even when asked). Annoying yes, but unless you can find a better alternative 3rd party library, editing it to work with your CSP is needed. – IncredibleHat Commented Jun 11, 2020 at 13:13
4 Answers
Reset to default 7Yes, there is a way.
There is much discussion about this here: https://github.com/w3c/webappsec-csp/issues/212
Which is succinctly summarised towards the end:
CSP is checked at parsing and blocks parsing the style attribute. Any direct operations go through.
Using setAttribute
invokes the HTML parser and CSP is triggered.
So, instead of:
.setAttribute("style","background:red"); // needs HTML parsing
You would need:
.style.background = "red"; // direct manipulation
It may sound odd that one method works and the other does not, I think the understanding here is that there is a subtle difference between HTML attributes and DOM properties. https://joji.me/en-us/blog/html-attribute-vs-dom-property/
2018-10-06 update
The original answer here is still correct for now — because with CSP as currently implemented in browsers at least, there’s still no way to have dynamically injected styles at all without specifying unsafe-inline
, and specifying unsafe-inline
basically negates the whole purpose of CSP.
However, CSP3 adds a new unsafe-hashes
expression for enabling you to allow particular inline scripts/styles. See https://w3c.github.io/webappsec-csp/#unsafe-hashes-usage, and see Explainer: ‘unsafe-hashes’, ‘unsafe-inline-attributes’ and CSP directive versioning. It hasn’t shipped in any browsers yet, though. So for the time being, the answer below still fully applies.
The only way to allow style
attributes is to use unsafe-inline
. It doesn’t matter whether the style
attributes are coming from a different origin or from self
—they’re still going to be considered a CSP violation unless you have unsafe-inline
.
Specifically, one solution that won’t work for style
attributes is to use a nonce or hash—because in CSP, nonce and hash usage are only defined for style
and script
elements; the spec has a Hash usage for style elements section that explicitly omits defining hash use for style attributes.
So even if in your policy you specify the correct hash for the contents of a style
attribute, your browser will still handle it as a violation.
The bottom line is that since unsafe-inline
is the only way to allow style
attributes—but using unsafe-inline
pretty much completely defeats the purpose of having any CSP policy to begin with—the only safe solution from a CSP perspective is just to never use style
attributes—neither directly from your own markup/code nor by way of any third-party code.
For anyone looking for a jQuery patch to change setting the style attrib into setting the proper css values, here is one I use (sourced from this Github but with an important bug fix to make it work correctly):
var native = jQuery.attr;
jQuery.attr = function (element, attr, value) {
if (attr === 'style') {
resetStyles(element);
return applyStyles(element, value);
} else {
//native.apply(jQuery, arguments);
return native(element, attr, value);
}
};
function applyStyles(element, styleString) {
if (styleString) {
var styles = styleString.split(';');
styles.forEach(function (styleBit) {
var parts = styleBit.split(':');
var property, value;
if (parts.length === 2) {
property = parts[0].trim();
value = parts[1].trim();
element.style[property] = value;
}
});
return styleString;
}
}
function resetStyles(element) {
var styleList = [].slice.call(element);
styleList.forEach(function (propertyName) {
element.style.removeProperty(propertyName);
});
}
It is strange why the '3 years old' question appeared in the new ones, and why the topicstarter's issue still unsolved.
The issue of using element.setAttribute('style', ...)
without causing a CSP violation is easily solved with a little hack that globally replaces the problematic element.setAttribute('style')
with the safe element.style.ptop = '...'
. After that you can use setAttribute('style')
without breaking CSP, this will fix jQuery and other libs too.
Solutions using only the CSP itself will be ineffective because:
- the
'unsafe-hashes'
token is still not supported in all browsers. - in the case of third-party JavaScript libraries that actively use dynamic styles through 'setAttribute('style')', it is technically impossible to maintain a list of dozens of hashes. In addition, CSP headers are limited in size.
cakeboeing727 started on the right track, but unfortunately the idea of the new contributor was not heard by society. It's a pity.