I am trying to implement content security policies (CSP) in a node server, and am having trouble setting up socket.io. It looks like I am setting up the connectSrc
incorrectly in the code below. Could someone suggest the correct way to set up helmet so that web socket connections are allowed by the browser? Thanks in advance!
I am using helmet module for generation of CSP; the following is the code that sets up CSP:
securitySetup = function(app) {
var connectSources, helmet, scriptSources, styleSources;
helmet = require("helmet");
app.use(helmet());
app.use(helmet.hidePoweredBy());
app.use(helmet.noSniff());
app.use(helmet.crossdomain());
scriptSources = ["'self'", "'unsafe-inline'", "'unsafe-eval'", "ajax.googleapis.com"];
styleSources = ["'self'", "'unsafe-inline'", "ajax.googleapis.com"];
connectSources = ["'self'"];
return app.use(helmet.contentSecurityPolicy({
defaultSrc: ["'self'"],
scriptSrc: scriptSources,
styleSrc: styleSources,
connectSrc: connectSources,
reportUri: '/report-violation',
reportOnly: false,
setAllHeaders: false,
safari5: false
}));
};
This works fine for all HTTP/AJAX traffic, but fails for ws:// protocol. I get this error in chrome debugger when socket.io connection is made:
Refused to connect to 'ws://localhost:3000/socket.io/1/websocket/ubexeZHZiAHwAV53WQ7u' because it violates the following Content Security Policy directive: "connect-src 'self'".
A close reading of the Content Security Policy spec explains why 'self'
doesn't work:
'self'
matches requests with the same host, port, and scheme. Since your original page loaded with the scheme http://
or https://
, 'self'
won't match connections using ws://
.
This makes 'self'
rather useless for websocket connections. Unless I'm missing something, I think this is a bug in the spec.