Content Security Policy - What You Need to Know?

Jainishshah
The Startup
Published in
5 min readDec 8, 2020

Aloha developers, this is Jainish, SDE-1 @ Innovaccer Analytics. To give you a brief I am a JavaScript Dev working on creating resilient reusable components in React.js. I and my team are working on creating a platform for hosting, creating, and managing Dashboards for our Health Care partners in the United States. We have created a plethora of features like Click Based Dashboard creations, Permission Module for ACL, easy Alerts Creation, to name a few.

Last week I was in a Sprint Planning meeting, discussing creating a Publish Management Service for maintaining the controlled flow of Dashboards in our platform. As a part of this feature, the users will easily be able to push created PowerBi powered dashboards(.pbix file) to our platform and need not worry about setting basic permissions and configurations.

We came up with an end-to-end workflow where first the file will be uploaded to AWS S3 bucket, once the upload is complete we call REST-API with all metadata to ask the backend to trigger a job that will take care of all the configurations. Sounds easy peasy right??

We had 2 plans either to integrate ASW-SDK in node.js, used as a jump server in our case, or using the amazing concept of Pre-Signed URL provided by AWS. We chose to use the latter. The process is simple

  1. Provide metadata about directory structure and file and generate the pre-signed URL.
  2. Use this pre-signed URL and Promise base HTTP-client library like Axios/Fetch API to upload this file to S3.

All parts worked pretty fine, but the last step of uploading failed giving an error: Refused to connect to ‘${pre-signed URL}’ because it violates the following Content Security Policy directive: “connect-src”. At this point, I had no clue what CSP even was. I went to reading out a few blogs and found a fix. From my experience, this is what we must know about how modern browser works.

Well, many of you would have heard about XSS in Web Security class. Lets’s recap this a bit.

“Cross-Site Scripting (XSS) attacks are a type of injection, in which malicious scripts are injected into otherwise benign and trusted websites. XSS attacks occur when an attacker uses a web application to send malicious code, generally in the form of a browser side script, to a different end-user. Flaws that allow these attacks to succeed are quite widespread and occur anywhere a web application uses input from a user within the output it generates without validating or encoding it.”

The statement stands for itself, but to give you a little more background let's talk about how Web Security works.

The basis of the Web Security model is “same-origin policy” i.e Code from one origin “https://goodOrigin.com” only should have access to its data and some other origin eg “https://badorigin.com” should never be allowed. This provides goodOrigin isolation from the rest of the web, and this sounds wonderful. But in practice, attackers have found a way around it.

Browser by default will accept all the code that comes up with the page from goodOrigin considering it to be true as there is no violation of the same-origin policy. This is how it should work, but wait till it gets tricky, what if the attackers inject some malicious code that is shipped along with the intended code. In a way, the attacker bypassed the “same-origin policy”. Since browsers will believe this to be legitimate it will run those scripts and we are DOOMED!!!!! . Millions of dollars in litigation depending on the type of data you deal with.

For preventing this we need the browsers to be smart, but how will browsers know what is legitimate and what is not? You guessed it right, CSP.

Instead of trusting everything received from the server, CSP creates a Content-Security-Policy HTTP header. It is where you define the list of all the allowed origins from where the scripts can be loaded and executed. Even if the attacker manages to inject the script, he will not be able to modify the http header and the browser will check that it violates the CSP and will throw an error instead of executing it.

There is a list of directives that you can find here. eg Let say you want to execute some script from “apis.google.com”. You can add ascript-src directive that controls a set of script-related privileges for a specific page. Since we want scripts from goodOrigin and "apis.google.com" to run. We'll specify Content-Security-Policy: script-src :'self',https://apis.google.comin our response headers. The browser dutifully downloads and executes JavaScript from apis.google.com over HTTPS, as well as from the current page's origin.

But my problem is still not solved yet, I want to connect to my S3 bucket, I am not loading any script, so why should it fail !!. Turns out CSP is more than just scripts. It also enforces fonts, images, styles-scripts, Url over which you can connect to via XHR/WebSockets, etc. In my case, I was trying to make an XHR request, so I had to use the connect-src directive where I defined the base URL of the S3 bucket “https://{BucketName}.s3.amazonaws.com and Voila, we are done.

Note: if the CSP is not defined, the browser will trust everything it gets. So if I hadn’t defined CSP in the first place I would have never encountered this issue. It is though a good practice to implement CSP if you deal with sensitive data. Also, this is not just a single way to implement it. We can specify CSP in the <head> section of HTML but the most secure way is the HTTP header as it can’t be tampered with. CSP just reduces the risk of XSS and is not a complete solution as we have smart people who know how to get a way around.

Hope you found this article interesting and you probably learned something. Also, it's the first article I have written and I know there is a scope of error. Constructive Criticism is always welcomed.

You can contact me on my email: “jainishshah12@gmail.com” for further communication.

Ref:

--

--