Browser-sync: Https proxying blocked by browsers Content Security Policy

Created on 31 Oct 2016  路  3Comments  路  Source: BrowserSync/browser-sync

Issue details

I have server on which I start BrowserSync proxy from CLI (code bellow), the proxied url has some content-security-policy headers set

Browser error:

browser-sync-client.js?v=2.17.5:2Refused to connect to 'wss://joe.dev:10000/browser-sync/socket.io/?EIO=3&transport=websocket&sid=IJFLKqUjWn-BQrnnAAAA' because it violates the following Content Security Policy directive: "default-src data: https: 'unsafe-inline' 'unsafe-eval'". Note that 'connect-src' was not explicitly set, so 'default-src' is used as a fallback.

CSP headers set on proxied url:

content-security-policy:default-src data: https: 'unsafe-inline' 'unsafe-eval'; report-uri /csp-report

I have control over the proxied url html so I could add <meta http-equiv="Content-Security-Policy"> tag but so far I had no luck with making it work. Ideally it would only allow urls that BrowserSync needs and nothing more.

Please specify which version of Browsersync, node and npm you're running

  • Browsersync 2.17.5
  • Node 5.11.1
  • Npm 3.8.6

Affected platforms

  • [x] linux
  • [ ] windows
  • [ ] OS X
  • [ ] freebsd
  • [ ] solaris
  • [ ] other _(please specify which)_

Browsersync use-case

  • [ ] API
  • [ ] Gulp
  • [ ] Grunt
  • [x] CLI

If CLI, please paste the entire command below

#!/bin/bash -eu

./node_modules/.bin/browser-sync start \
    --proxy "https://page.dev" \
    --port 10000 \
    --ui-port 10001 \
    --cors \
    --no-open

for all other use-cases, (gulp, grunt etc), please show us exactly how you're using Browsersync

./start.sh

Most helpful comment

Ok, I have figured it out. Probably not possible in CLI, but in nodejs interface you are able to alter the response of the proxied request - so what I did is I added little CSP parser and if CSP header is present, I add BrowserSync's wss url into the header.

For anybody interested here is code snippet that fixes the problem

'use strict'

const bs = require('browser-sync').create()
const CspParse = require('csp-parse')

bs.init({
  proxy: {
    target: 'https://page.dev',
    proxyRes: [
      function (proxyRes, req, res) {
        const cspHeader = proxyRes.headers['content-security-policy']
        if (!cspHeader) return

        const newCsp = new CspParse(cspHeader)

        // wss url which browser sync uses to communicate between devices,
        // CPS headers could block this url so add it to allowed urls
        const url = 'wss://' + req.headers.host
        newCsp.add('default-src', url)
        const newCspString = newCsp.toString()

        proxyRes.headers['content-security-policy'] = newCspString
      }
    ]
  },
  port: 10000,
  ui: {
    port: 10001
  }
})

This whole thing could probably be integrated into the Browser-Sync to make proxying even easier (behind some flag)

All 3 comments

Ok, I have figured it out. Probably not possible in CLI, but in nodejs interface you are able to alter the response of the proxied request - so what I did is I added little CSP parser and if CSP header is present, I add BrowserSync's wss url into the header.

For anybody interested here is code snippet that fixes the problem

'use strict'

const bs = require('browser-sync').create()
const CspParse = require('csp-parse')

bs.init({
  proxy: {
    target: 'https://page.dev',
    proxyRes: [
      function (proxyRes, req, res) {
        const cspHeader = proxyRes.headers['content-security-policy']
        if (!cspHeader) return

        const newCsp = new CspParse(cspHeader)

        // wss url which browser sync uses to communicate between devices,
        // CPS headers could block this url so add it to allowed urls
        const url = 'wss://' + req.headers.host
        newCsp.add('default-src', url)
        const newCspString = newCsp.toString()

        proxyRes.headers['content-security-policy'] = newCspString
      }
    ]
  },
  port: 10000,
  ui: {
    port: 10001
  }
})

This whole thing could probably be integrated into the Browser-Sync to make proxying even easier (behind some flag)

@Hurtak so clutch you should work for Toyota. Thank you.

I came upon the issue while using lite-server and built upon @Hurtak 's answer to solve this issue with middleware:

const CspParse = require('csp-parse');

module.exports = {
  https: true,
  server: {
    baseDir: "./dist",
    middleware: {
      2: function (req, res, next) {
        const origHeaders = req.headers;
        const cspHeader = origHeaders['content-security-policy'];
        const url = 'https://' + req.headers.host;
        const websocket = 'wss://' + req.headers.host;

        if (cspHeader) {
          const newCsp = new CspParse(cspHeader);

          // wss url which browser sync uses to communicate between devices,
          // CPS headers could block this url so add it to allowed urls

          newCsp.add('default-src', `'unsafe-inline' ${url} ${websocket}`);
          const newCspString = newCsp.toString();
          res.setHeader('content-security-policy', newCspString);

        } else {
          const newCsp = new CspParse('script-src', `'unsafe-inline' ${url} ${websocket}`);
          newCsp.add('default-src', `'unsafe-inline' ${url} ${websocket}`);
          const newCspString = newCsp.toString();
          res.setHeader('content-security-policy', newCspString);
        }
        next();
      }
    }
  }
}
Was this page helpful?
0 / 5 - 0 ratings

Related issues

Zver64 picture Zver64  路  3Comments

ericmorand picture ericmorand  路  3Comments

arve0 picture arve0  路  4Comments

kraf picture kraf  路  3Comments

jitendravyas picture jitendravyas  路  4Comments