简体   繁体   English

node.js - 使用头盔更正socket.io(Web套接字)的内容安全策略

[英]node.js - correct content security policy for socket.io (web sockets) using helmet

I am trying to implement content security policies (CSP) in a node server, and am having trouble setting up socket.io. 我正在尝试在节点服务器中实现内容安全策略(CSP),并且在设置socket.io时遇到问题。 It looks like I am setting up the connectSrc incorrectly in the code below. 看起来我在下面的代码中错误地设置了connectSrc Could someone suggest the correct way to set up helmet so that web socket connections are allowed by the browser? 有人建议设置头盔的正确方法,以便浏览器允许Web套接字连接吗? Thanks in advance! 提前致谢!

I am using helmet module for generation of CSP; 我使用头盔模块生成CSP; the following is the code that sets up CSP: 以下是设置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. 这适用于所有HTTP / AJAX流量,但对于ws://协议失败。 I get this error in chrome debugger when socket.io connection is made: 当进行socket.io连接时,我在chrome调试器中出现此错误:

Refused to connect to 'ws://localhost:3000/socket.io/1/websocket/ubexeZHZiAHwAV53WQ7u' because it violates the following Content Security Policy directive: "connect-src 'self'".

Chrome控制台错误

A close reading of the Content Security Policy spec explains why 'self' doesn't work: 仔细阅读内容安全政策规范可以解释为什么'self'不起作用:

'self' matches requests with the same host, port, and scheme . 'self'匹配具有相同主机,端口和方案的请求。 Since your original page loaded with the scheme http:// or https:// , 'self' won't match connections using ws:// . 由于您的原始页面加载了方案http://https:// ,因此'self'与使用ws://连接不匹配。

This makes 'self' rather useless for websocket connections. 这使得'self'对于websocket连接毫无用处。 Unless I'm missing something, I think this is a bug in the spec. 除非我遗漏了什么,否则我认为这是规范中的一个错误。

添加指定协议的地址解决了我的问题。

connectSources = ["'self'", "ws://localhost:3000"]

Bit late to the party, but thought I'd throw something into the mix on this subject. 派对迟到了,但我想我会在这个主题上加入一些东西。 @Ed4 is quite right, 'self' will indeed only match the same host, port and scheme. @ Ed4非常正确, 'self'确实只会匹配相同的主机,端口和方案。 A way around it is to include it in one or more forms: 解决它的方法是将其包含在一种或多种形式中:

connect-src: wss: - to allow a connection to the whole wss scheme - basically any web socket (probably not ideal) connect-src: wss: - 允许连接到整个wss方案 - 基本上任何 web套接字(可能不理想)

connect-src: wss://yoursite.domain.com - to restrict it to a specific endpoint. connect-src: wss://yoursite.domain.com - 将其限制为特定端点。 This is most ideal, but might be restrictive if your subdomain changes between deployments (as ours do) 这是最理想的,但如果您的子域在部署之间发生更改(如我们所做的那样),则可能会受到限制

connect-src: wss://*.domain.com - can use wildcards in there to tighten security up a bit. connect-src: wss://*.domain.com - 可以在那里使用通配符来加强安全性。 This is what we do 这就是我们的工作

TL;DR - use wildcards to make things more specific without just opening yourself up to any web sockets out there/ TL; DR - 使用通配符使事情变得更具体,而不仅仅是打开任何网络套接字/

Refer to this passage from Google devs: 请参阅Google开发者的这篇文章:

The source list in each directive is flexible. 每个指令中的源列表都是灵活的。 You can specify sources by scheme (data:, https:), or ranging in specificity from hostname-only (example.com, which matches any origin on that host: any scheme, any port) to a fully qualified URI ( https://example.com:443 , which matches only HTTPS, only example.com, and only port 443). 您可以按方案(data:,https :)指定源,或者从仅限主机名(example.com,与该主机上的任何源匹配:任何方案,任何端口)的特定范围指定为完全限定的URI( https:/ /example.com:443 ,仅匹配HTTPS,仅匹配example.com,仅匹配端口443)。 Wildcards are accepted, but only as a scheme, a port, or in the leftmost position of the hostname: :// .example.com:* would match all subdomains of example.com (but not example.com itself), using any scheme, on any port. 接受通配符,但仅作为方案,端口或主机名的最左侧位置:: // .example.com:*将匹配example.com的所有子域(但不是example.com本身),使用任何计划,在任何港口。

https://developers.google.com/web/fundamentals/security/csp/ https://developers.google.com/web/fundamentals/security/csp/

Here's a way to automatically add the current host and port in express (es6 syntax): 这是一种在express(es6语法)中自动添加当前主机和端口的方法:

import csp from 'helmet-csp';

app.use((req, res, next) => {
  let wsSrc = (req.protocol === 'http' ? 'ws://' : 'wss://') + req.get('host');

  csp({
    connectSrc: ['\'self\'', wsSrc],
  })(req, res, next);
});

you can use ws: or wss: as keyword for websocket, here is my code use 你可以使用ws:或wss:作为websocket的关键字,这是我的代码使用

app.use(helmet.csp({
  'default-src': ["'self'"],
  'connect-src': [
    "'self'" , "blob:",
    'wss:',
    'websocket.domain',
  ],
  'font-src': ["'self'",'s3.amazonaws.com',"maxcdn.bootstrapcdn.com"],
  'img-src': ["'self'", 'data:'],
  'style-src': ["'self'","maxcdn.bootstrapcdn.com",'s3.amazonaws.com',"'unsafe-inline'"],
  'script-src': ["'self'","'unsafe-inline'","'unsafe-eval'",'blob:'],
  reportOnly: false,
  setAllHeaders: false,
  safari5: false
}))

with wss://websocket.domain is domain for websocket so if you can use it for localhost 使用wss://websocket.domain是websocket的域名,所以如果你可以将它用于localhost

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM