Bootstrap’s progress bar versus Content Security Policy

Bootstrap’s progress bar component uses an inline width style. Here’s the example from their docs.

This is unfortunate for the obvious reasons (it mixes content and presentation, the value—60 in this case—is repeated) but there’s also a less obvious repercussion. Inline styles prevent us from gaining the greatest benefit from the Content Security Policy header. If we have any inline styles then we’re forced to use the 'unsafe-inline' source with the style-src directive. Which, as Egon says, would be bad:

Inline style is treated in the same way: both the style attribute and style tags should be consolidated into external stylesheets to protect against a variety of surprisingly clever data exfiltration methods that CSS enables.

Where we could have permitted only external stylesheets:

We are forced to allow unsafe inline styles:

Mike West on 'unsafe-inline':

If you really, absolutely must have inline script and style, you can enable it by adding 'unsafe-inline' as an allowed source in a script-src or style-src directive. But please don’t. Banning inline script is the biggest security win CSP provides, and banning inline style likewise hardens your application. It’s a little bit of effort up front to ensure that things work correctly after moving all the code out-of-line, but that’s a tradeoff that’s well worth making.

It so happens that Bootstrap’s progress bar was the only case of inline styles in the project I’m working on, so it was worth finding a way to rip it out. Here’s the unashamedly low-tech solution I used: progressbar.css.

Here’s my advice if you’re using Bootstrap’s progress bar:

  • Include the progressbar.css styles somewhere in your CSS,
  • remove the inline width style from any progress bar divs,
  • get rid of the 'unsafe-inline' source (or if you’re not yet using the Content Security Policy header, start using it without 'unsafe-inline').

The caveat is that this will only work if all your progress bars go from zero to one hundred. But if changing any progress bars that don’t fit this range is the difference between avoiding 'unsafe-inline' and being stuck with it, it’s worth making the change.