1

I added a fetch call in my entry.js file:

fetch('https://ipinfo.io/json')
  .then(response => response.json())
  .then(data => console.log(data));

My webpack build was fine until I added that, then i started getting errors regarding Content Security Policy.

Previously, I didn't have any content security policy defined.

I've tried several approaches to resolve errors (see end of post), the latest was adding this to head section of index.html file:

<meta http-equiv="Content-Security-Policy" content="script-src 'self'; connect-src https://ipinfo.io/json">

Now I am getting these errors in Chrome developer tools console, which seem to contradict themselves:

Error #1 - relating to jquery ajax get request to my own api

bundle.js:25 Refused to connect to 'http://localhost:3000/api/v1/resource?parameter=false&_=1598715905517' because it violates the following Content Security Policy directive: "connect-src https://ipinfo.io/json".

Error #2 - relating to fetch call to external website

bundle.js:1 Refused to connect to 'https://ipinfo.io/json' because it violates the following Content Security Policy directive: "default-src 'self'". Note that 'connect-src' was not explicitly set, so 'default-src' is used as a fallback.

Note: This second error says that connect-src is not explicitly set, however the first error occurs because it is explicitly set.

Error #3 - relating to fetch call to external website

bundle.js:1 Refused to connect to 'https://ipinfo.io/json' because it violates the document's Content Security Policy.

Error #4 - relating to fetch call to external website

bundle.js:1  
Uncaught (in promise) TypeError: Failed to fetch
    at HTMLDocument.<anonymous> (bundle.js:1)
    at u (bundle.js:25)
    at l (bundle.js:25)

Other approaches

For reference, here is one of the other approaches I tried using a nonce:

I added this to entry.js:

__webpack_nonce__ = 'something-tricky';

And this to the <head> section of the index file:

<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'nonce-something-tricky';

The result was that it wouldn't load bundle.js:

Refused to load the script 'http://localhost:3000/js/bundle.js' because it violates the following Content Security Policy directive: "script-src 'nonce-something-tricky'". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.

This approach was based on:

https://webpack.js.org/guides/csp

https://stackoverflow.com/a/42924000

Further context

As i started trying all sorts of CSP combinations in the <head> section, I noticed it started breaking lots of things like:

  • script tag links in index.html to google fonts etc
  • css styles generated and included in bundle.js

So i'm aware of the need to get CSP completely right, or it won't work at all.

Edit

My last attempt was this:

<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' https://ipinfo.io; style-src 'self' fonts.googleapis.com 'unsafe-inline'; font-src 'self' fonts.gstatic.com">

And I am getting this error:

bundle.js:25 Refused to load the script 'https://ipinfo.io/json?callback=jQuery35106635073412967416_1598793439782&_=1598793439783' because it violates the following Content Security Policy directive: "script-src 'self'". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.

When this code is in the entry.js file:

$.get("https://ipinfo.io/json", function(response) {
  console.log(response);
}, "jsonp"); 

That last error is weird, because it seems to be ignoring the domain that I specifically 'whitelisted' in the srcipt-src directive.

user1063287
  • 10,265
  • 25
  • 122
  • 218
  • You don't need to set CSP here. The root cause is that you don't have privilege to call https://ipinfo.io/json from your website. You need to call it by JSONP https://ipinfo.io/developers#jsonpcors-requests, or put the calls to your backend. – ninhjs.dev Aug 29 '20 at 16:52
  • I used the code snippet at the link you shared, i just modified the url from `https://ipinfo.io?token=$TOKEN` to `https://ipinfo.io/json` (as i don't want to sign up for an account yet in order to get a token), and i removed any content security policy from `index.html` and I get this error: `Refused to load the script 'https://ipinfo.io/json?callback=jQuery35108965288122289881_1598790072238&_=1598790072239' because it violates the following Content Security Policy directive: "script-src 'self'". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.` – user1063287 Aug 30 '20 at 12:25
  • Answer added, the issue was related to usage of helmet in node. – user1063287 Aug 30 '20 at 22:40
  • The default CSP would allow `script-src` from anywhere `*`. As I mentioned, the root cause is cross domain issue (JSOP should work), not CSP itself. – ninhjs.dev Aug 31 '20 at 02:02

1 Answers1

1

Ok, well i looked at the Network tab in Chrome developer tools for other requests and saw a rather rich set of CSP directives were being applied in the Response Headers tab, but i hadn't set them, so I wondered where they were coming from.

I came across this answer, which mentioned that they could be set using helmet in node

So in my node app, I replaced:

app.use(helmet());

which at the time of this edit is equivalent to this:

app.use(helmet.contentSecurityPolicy());
app.use(helmet.dnsPrefetchControl());
app.use(helmet.expectCt());
app.use(helmet.frameguard());
app.use(helmet.hidePoweredBy());
app.use(helmet.hsts());
app.use(helmet.ieNoOpen());
app.use(helmet.noSniff());
app.use(helmet.permittedCrossDomainPolicies());
app.use(helmet.referrerPolicy());
app.use(helmet.xssFilter());

where helmet.contentSecurityPolicy() defaults to:

default-src 'self';
base-uri 'self';
block-all-mixed-content;
font-src 'self' https: data:;
frame-ancestors 'self';
img-src 'self' data:;
object-src 'none';
script-src 'self';
script-src-attr 'none';
style-src 'self' https: 'unsafe-inline';
upgrade-insecure-requests

with:

app.use(
    helmet({
        contentSecurityPolicy: {
            directives: {
                defaultSrc: ["'self'"],
                scriptSrc: ["'self'", "https://ipinfo.io"],
                styleSrc: ["'self'", "fonts.googleapis.com", "'unsafe-inline'"],
                fontSrc: ["'self'", "fonts.gstatic.com"]
            }
        },
    })
);

and now this request from entry.js is working:

$.get("https://ipinfo.io/json", function(response) {
  console.log(response);
}, "jsonp"); 

Note, I had to add unsafe-inline to the StyleSrc or else the CSS that was imported and bundled into bundle.js using webpack wouldn't work.

user1063287
  • 10,265
  • 25
  • 122
  • 218