Deploying Content Security Policies (CSPs) can help increase the security of your website. Therefore, it is an easy recommendation that most security professionals make when working with development teams. However, the process of helping a team go from the simple recommendation to a successfully deployed policy can be complicated. As a security professional, you need to be aware of the potential challenges along the way in order to help teams navigate their way to a successful deployment.
Let’s start with the initial challenges that developers may face. One, is that many authoring tools that support accelerated web design, do not produce CSP compliant code. While some of the major JS frameworks support CSP, the extensions to those frameworks may not. Also, there are trade-offs to running in CSP compliant mode. For instance, AngularJS has a CSP-compliant mode that you can enable. (https://docs.angularjs.org/api/ng/directive/ngCsp) However, there are speed trade offs in enabling that flag. Security people may think a 30% speed trade-off is acceptable but, depending on how dependent the page is on AngularJS, the developers may take notice.
Lastly, a CSP compliant coding style may be counter-intuitive to some long time web developers. For instance, let’s take a simple button tag as an example. In a pre-CSP design approach, you would write it as:
<input type=”button” id=”myButton” value=”Click Me!” onClick=”doCoolStuff();”/>
To enable CSP, we have to tell the developer to remove the onClick statement and put it in a separate JS file. This may be counterintuitive because the developer may wonder why making two Internet requests and dynamically adding the event handler might be safer than just hard coding the event inline within the original request. From a developer’s perspective, the extra web request adds latency to the page load. Also, it will make the code harder to read because you have to do more cross-referencing in order to understand what the button does.
In terms of moving the code out of the HTML, it is more complicated than just copying and pasting. For instance, you can’t add an event handler to the button until the button has been added to the DOM. Therefore, you may need to add an additional onLoad event handler just to add the onClick event handler to the button. The same race condition would apply anytime you dynamically add content to an innerHTML property.
For cascading style sheets, you also need to remove any style properties from the code. Fortunately, while many developers still use inline style statements, the deprecation of several style properties in tags by the HTML5 movement has already forced the migration of many style settings to separate files. That said, there may be templates and libraries that developers use with style statements still in the HTML.
Understanding the changes required to support CSP is important because it gives you perspective on the scope of what you are asking teams to change. It is more than just enumerating everywhere that you load content and putting it in a header. You are likely asking teams to recode large amounts of working, validated code. This is similar to the “banned function” movement that happened in C/C++ code several years ago where developers had to re-architect existing code to use newer C/C++ functions.
Before you start a CSP deployment with teams, you should be prepared to answer questions such as:
- Do I deploy CSP Level 1, Level 2, or Level 3?
- Is Level 2 better than Level 1 in terms of security or does it just have more features?
- What is the browser adoption for each level? I want to use the nonce and hash features of CSP Level 2 for my inline scripts but I also have to be compatible with older browsers.
- I heard that the “strict-dynamic” property in Level 3 is going to make deployment easier. Should we just wait for that?
- I read one site where it said I should start with the policy “default ‘self'” but another site said I should start with “default-src ‘none’; script-src ‘self’; connect-src ‘self’; img-src ‘self’; style-src ‘self’;”. Which should I use?
- Do I have to set a unique, restricted CSP for each page or can I just set one for the entire site that is a little more permissive?
- How do I handle the CSP report-only reports? What is the process for ensuring someone reviews the reports? Is there a pre-made module available via gem/pip/npm that handles some of this and automatically puts the reports in a database?
- Do I need to deploy the CSP reporting in production or is staging enough?
- What are the common mistakes people make when deploying CSPs?
- How do I debug when the CSP is breaking content? The CSP report doesn’t give me the line number of the violation and sometimes it doesn’t even give me the full path for the request. Do the builtin browser developer consoles help with debugging violations? Which is best?
- I load JS content from a third-party provider and their content is causing issues with the recommended CSP settings. What should I do?
- How bad is it to allow unsafe-inline? Can I use the nonce approach instead? Are there any prewritten/preapproved npm/pip/gem modules for generating nonces? How does that compare to hash source? How do I know whether to use nonces or hashes? I heard that nonces don’t work well with static content. Is that true? Is there a way to have the server generate the hash at runtime so I don’t have to update CSPs every time I change a file?
- How do we change our developer guides to help ensure that new code is written in a CSP compliant manner? Are there plugins for our IDEs to detect these things? Will our static analysis tools flag these issues? Are there pre-written guidelines which enumerate what you can and can’t do? Is there a way to style the code so that it is easier to handle the added complexity of cross-referencing between files?
- Are there any published stats on rendering time when implementing CSP? What is the performance impact?
- Is it OK to specify things like media-src, object-src or font-src and set them to ‘self’ even though we aren’t currently using them on the page? I want to limit how often I have to adjust the policy files in production. As long as they are set to ‘self’, then it shouldn’t be that big of a risk, right?
As you can see, the questions arising from deployment can get complicated quickly. For instance, it is trivial to define what would be the ideal policy and how to set the header. However, content security policies are the lowest common denominator of all your page dependencies. If the team integrates with multiple providers, then the policy is going to be whatever lowest common denominator is necessary to support all the providers that are linked by that page. How are you going to handle the situation where their third-party library requires “unsafe-inline unsafe-eval data: …”? Are your policies immutable or will there be an exception process? What is the exception process?
While I can’t provide all the answers in a single blog, here are some thoughts on how to approach it:
- Work on changing coding guidelines so that new code is CSP compliant. You will never catch up to where you need to be if new code continues to follow the existing development practices.
- You can get a feel for the scope of the effort by setting up a test server with a few web pages from your site and experimenting with adding CSPs locally.
- Rather than going after the entire site, start with critical pages. The simple process of getting your first page to work will likely require a decent amount of effort such as training the team on what the rules mean, deciding what approach should be used by the server to add the headers, learning how to debug issues, identifying patterns, etc. For instance, a login page is critical in terms of security and it is likely to have fewer third-party dependencies than other pages. Once that first page is established, you can build on your CSP deployment incrementally from there.
- Track the policies necessary to support different third-party providers and libraries. This will allow teams to share rules and limit the amount of debugging necessary for third-party code.
- Search for blogs and talks where people talk about the actual deployment process rather than just the technical definitions. One example is this blog by Terrill Dent of Square : https://corner.squareup.com/2016/05/content-security-policy-single-page-app.html For people who prefer full presentations, Michele Spagnuolo and Lukas Weichselbaum have conducted a lot research in this area. Their HITB Amsterdam presentation detailed common mistakes made in CSP deployments: https://conference.hitb.org/hitbsecconf2016ams/materials/D1T2%20-%20Michele%20Spagnuolo%20and%20Lukas%20Weichselbaum%20-%20CSP%20Oddities.pdf (slides) https://www.youtube.com/watch?v=eewyLp9QLEs (video) They also recently presented, “Making CSP great again” at OWASP AppSecEU 2016: https://www.youtube.com/watch?v=uf12a-0AluI&list=PLpr-xdpM8wG-Kf1_BOnT2LFZU8_SXfpKL&index=37
It is trivial to email a team telling them they should deploy content security policies and describing all the wonderful benefits that they can bring. However, the next step is to dig in and help the teams through the implementation process. In Spagnulo and Weichselbaum’s AppSecEU presentation, they mentioned that they analyzed 1.6 million policies from the web and they estimated that they were able to bypass the whitelist of at least 90% of them. Therefore, simply deploying CSPs does not guarantee security. You will need to be prepared for all the design questions and policy decisions that will result from the effort in order to deploy them well. That preparation will pay dividends in creating and executing a successful CSP deployment plan with a development team.