使用 express 和 helmet 服务的 React 应用程序无法发出 API 请求

React app served with express and helmet cannot make API requests

我正在使用 express 构建 React 应用程序。

const root = path.join(__dirname, 'build/');
app.use(express.static(root));
app.use((req, res, next) => {
  if (req.method === 'GET' && req.accepts('html') && !req.is('json') && !req.path.includes('.')) {
    res.sendFile('index.html', { root });
  } else next();
});

一切正常。但是,一旦我将 helmet(不是 react-helmet)添加到 express 应用程序,我就会遇到问题(样式和脚本未加载)。 在搜索了几个资源之后,我想出了一个解决方案来让它工作。 下面的代码显示了我为加载样式和脚本所做的修复。

app.use(helmet());
app.use(helmet.contentSecurityPolicy({
  defaultSrc: [
    '\'self\'',
    'https://api.domain.tld/*',
    'https://domain.tld',
  ],
  styleSrc: [
    '\'self\'',
    '\'unsafe-inline\'',
    'https://*.googleapis.com',
    'https://api.domain.tld/*',
    'https://domain.tld',
  ],
  scriptSrc: [
    '\'self\'',
    '\'unsafe-inline\'',
    'https://api.domain.tld/*',
    'https://domain.tld',
  ],
  contentSrc: [
    '\'self\'',
    '\'unsafe-inline\'',
    'https://api.domain.tld/*',
    'https://domain.tld',
  ],
}));

此外,我还在 .env file 中添加了 INLINE_RUNTIME_CHUNK=false

我目前遇到的问题是我对 api.domain.tld 的 API 调用不起作用。它被阻止并在 firefox 上显示以下错误。

Content Security Policy: The page’s settings blocked the loading of a resource at https://api.domain.tld/endpoint (“default-src”).

Chrome 显示如下错误。

Refused to connect to 'https://api.domain.tld/endpoint' 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.

如何解决这个问题,以便我可以拨打 API 电话?

根据 Documentation,您应该在 directives

中指定来源

app.use(helmet.contentSecurityPolicy({
  useDefaults: false, // you can change it to `true` if you want.
  directives:{
    defaultSrc: [
      '\'self\'',
      'https://api.domain.tld/',
      'https://domain.tld',
    ],
    styleSrc: [
      '\'self\'',
      '\'unsafe-inline\'',
      'https://*.googleapis.com',
      'https://api.domain.tld/',
      'https://domain.tld',
    ],
    'image-src': [
      '\'self\'',
      '\'unsafe-inline\'',
      'data:',
      'https://api.domain.tld/',
      'https://domain.tld',
    ],
    scriptSrc: [
      '\'self\'',
      '\'unsafe-inline\'',
      'https://api.domain.tld/*',
      'https://domain.tld',
    ],
    contentSrc: [
      '\'self\'',
      '\'unsafe-inline\'',
      'https://api.domain.tld/',
      'https://domain.tld',
    ],
  }
}));

更新

要解决图片无法加载的问题,请添加以下内容。

   imageSrc: [
      '\'self\'',
      '\'unsafe-inline\'',
      'data:',
      'https://api.domain.tld/',
      'https://domain.tld',
   ],

有 2 个问题:

  1. 修正语法错误:contentSrc: -> connectSrc:

  2. CSP 规范不允许在路径部分使用 *(通配符),因此请修复 'https://api.domain.tld/* -> 'https://api.domain.tld/'。同样在路径部分,您可以使用:

  • 文件夹名称:'https://api.domain.tld/real_path_here/'(带尾部斜杠 /)- 将允许指定文件夹和子文件夹中的任何子文件夹和任何文件。
  • 文件名:'https://api.domain.tld/endpoint''https://api.domain.tld/some_path/script.js'(不带尾部斜杠 /)- 仅允许指定 file_name。