At one point in your web developer life you might come across the following scenario:
I recently discovered a browser extension named "Requestly" (available for Firefox and Chrome). It allows you to intercept and redirect network requests of your browser and replace the requested remote assets (like a JS File) with assets served from your own machine's localhost or any other remote server.
Remember, that's only for debugging and testing on the browser side, it will not actually change the original files on the server!
Let's exercise this scenario with some random website. I will use the website of Hackernews https://news.ycombinator.com/. As a proof of concept I just want to add an alert in their script saying "Hello world".
In Firefox what I do is opening the Devtools, go to the network panel, filter for JS resources only, right-click on the relevant JS file, choose "Open in new tab", and then download the file from there.
In the case of our example (Hacker News) the file we want to replace is named
hn.js followed by a query string including a hash
Let's do some changes to the downloaded file with our favorite code editor. Let's just add one line of code right at the beginning of the file. Obviously in reality you would do your foxy debugging magic here!
When you tell Requestly to set up a redirect, the destination has to be a URL. A file from within your file system will not work. I use the super handy Live Server plugin for VS Code to serve my local files straight from my IDE in a matter of a click. Here we go, my edited JS file is served by localhost under: http://localhost:5500/hn.js
When you have the Requestly browser extension installed, click on it in the browser toolbar, then choose "Open app". Select "New Rule", then the "Redirect Request" option from the menu.
Enter the URL of the file you want to replace (Request Url) and the URL of your replacement file (Destination Url). If the original Url contains a (versioning) hash you might want to select "contains" instead of "equals".
Now refresh the Hacker News site in the browser. Do we see the "Hello world" alert? Nope. But why? When we open the browser console we see a hint.
Let's look at this console error more closely:
Content Security Policy: The page’s settings blocked the loading of a resource at http://localhost:5500/hn.js (“script-src”).
The message refers to a HTTP header named Content Security Policy (CSP) which is attached to the HTTP response of the original site. The exact content of the header can be examined again with the browser Devtools: Go to the Network tab again, filter by HTML, click on the file, this will show the details to the right. Select the tab "Headers".
Here's what's in it:
Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/ https://cdnjs.cloudflare.com/; frame-src 'self' https://www.google.com/recaptcha/; style-src 'self' 'unsafe-inline'
The interesting part of this CSP string is the part that is following the keyword/directive
script-src. It translates to:
This site is allowed to execute scripts from resources that are hosted on the same domain (
'self'), or embedded as inline scripts (
'unsafe-inline'). Furthermore the site will accept scripts from a few selected external providers (Google, Gstatic, Cloudfare).
Obviously, our localhost is not invited by this policy! That's why the request could not be redirected to localhost. Luckily we can also change that by telling Requestly to modify the HTTP headers of the site.
Create a new Requestly rule. This rule should be of the type "Modify Headers". Then, inside the form enter "Content-Security-Policy" (without quotes) in the "Header" input field. In the "Value" input field enter the original CSP header of the site and finally add localhost after the script-src directive. At the input field for Request Url enter the URL of the site you want to change (in our example the site of Hacker News).
Select "Response", because we want to change the Reponse HTTP Headers.
The updated CSP header for our example would be:
default-src 'self'; script-src 'self' 'unsafe-inline' http://localhost:5500 https://www.google.com/recaptcha/ https://www.gstatic.com/recaptcha/ https://cdnjs.cloudflare.com/; frame-src 'self' https://www.google.com/recaptcha/; style-src 'self' 'unsafe-inline'
(Note that the existing CSP HTTP header of your site might be different. Use the one of your site!)
⚠️ A word of caution ⚠️
Be aware that tinkering with HTTP security headers can be dangerous if you don't know what you're doing. Modifying the CSP header by adding localhost to your trusted hosts is of relatively low risk (because localhost === you). But disabling the CSP header altogether for a site could expose you to a security risk. By entering the wrong parameters (like a wildcard * for the URL pattern) you could easily disable important security mechanisms for all sites you visit. So be very, very careful!
This is the last hurdle finally. If the website is served over HTTPS, but your replacement file is served over HTTP, you have a situation called "Mixed Content". Most browsers are blocking mixed content to keep you safe. In this case you will see an error like this in the console:
Blocked loading mixed active content http://localhost:5500/hn.js
To solve this you have 2 options:
- Serving your replacement file over HTTPS
- Telling your browser to (temporarily) unblock mixed content
I used the second option for this demo (note the lock icon with the red line in the address bar). And voila we see the alert!
Don't forget to deactivate your Requestly rules once you're done! Speaking from experience! You will forget that you set up this redirect and wonder why things are behaving so strangely. That's because there is no visual hint on the site to remind you that you have tampered with it (unless you're checking the network panel)!
Happy (live) debugging!