带有express和头盔nonce的Angular ssr如何处理?

问题描述 投票:0回答:1

我有一个由express托管的角度ssr应用程序。我正在使用谷歌分析,现在我想使用头盔.js 来强化我的应用程序。

现在我面临着谷歌分析内联脚本违反了 SCP。

<script>
    window.dataLayer = window.dataLayer || [];
    function gtag(){dataLayer.push(arguments);}
    gtag('js', new Date());
    gtag('config', 'G-xxxxxxx');
</script>

Google 说我应该使用随机数来识别我的脚本,不,我不想使用不安全的内联设置,因为这确实是很糟糕的做法。有人有示例代码吗?

我尝试了很多来自谷歌和聊天gpt的代码。

angular express google-analytics content-security-policy helmet.js
1个回答
0
投票

在高层次上,您将执行以下操作:

  1. 生成一个随机数值,对于每个请求都是唯一的。将其保存到
    res.locals.cspNonce
    或同等格式。
  2. 告诉 Helmet 这个随机数。
  3. nonce
    HTML 属性添加到相关的
    <script>
    <style>
    标签。

第1步:生成nonce值

首先,您需要生成一个随机数并将其保存到

res.locals
。猜测这个随机数应该很困难,因此我们将生成 32 个随机字节(256 个随机位)并将它们转换为十六进制字符串。

import * as crypto from "node:crypto";

// ...

app.use((_req, res, next) => {
  // Asynchronously generate a unique nonce for each request.
  crypto.randomBytes(32, (err, randomBytes) => {
    if (err) {
      // If there was a problem, bail.
      next(err);
    } else {
      // Save the nonce, as a hex string, to `res.locals` for later.
      res.locals.cspNonce = randomBytes.toString("hex");
      next();
    }
  });
});

第 2 步:告诉 Helmet 这个随机数

接下来,告诉 Helmet 这个随机数。更具体地说,告诉

script-src
指令。

在此示例中,我们计划将此随机数与

<script>
标签一起使用。如果您想使用内联样式,请使用
styleSrc
指令。

app.use(
  helmet({
    contentSecurityPolicy: {
      directives: {
        scriptSrc: [
          "'self'",
          // Include this nonce in the `script-src` directive.
          (_req, res) => `'nonce-${res.locals.cspNonce}'`,
        ],
      },
    },
  }),
);

第 3 步:将随机数放入 HTML 中

最后,您需要设置

nonce
<script>
标签的
<style>
属性。

您可能正在使用 Pug 或 EJS 等模板引擎,但我们会为此示例做一些更简单的事情,仅内联发送 HTML。

app.get("/", (_req, res) => {
  // When rendering the `<script>` tag, include the nonce.
  res.send(`
    <script nonce="${res.locals.cspNonce}">
      console.log("Hello world!");
    </script>
  `);
});

如果您已正确完成所有操作,您应该会看到“Hello world!”在控制台中。每次刷新页面时,您还应该看到不同的

nonce
值。

完整的应用程序代码

这是此示例的完整源代码。

import express from "express";
import helmet from "helmet";
import * as crypto from "node:crypto";

const app = express();

app.use((_req, res, next) => {
  // Asynchronously generate a unique nonce for each request.
  crypto.randomBytes(32, (err, randomBytes) => {
    if (err) {
      // If there was a problem, bail.
      next(err);
    } else {
      // Save the nonce, as a hex string, to `res.locals` for later.
      res.locals.cspNonce = randomBytes.toString("hex");
      next();
    }
  });
});

app.use(
  helmet({
    contentSecurityPolicy: {
      directives: {
        scriptSrc: [
          "'self'",
          // Include this nonce in the `script-src` directive.
          (_req, res) => `'nonce-${res.locals.cspNonce}'`,
        ],
      },
    },
  }),
);

app.get("/", (_req, res) => {
  // When rendering the `<script>` tag, include the nonce.
  res.send(`
    <script nonce="${res.locals.cspNonce}">
      console.log("Hello world!");
    </script>
  `);
});

app.listen(3000);

有关更多信息,请参阅头盔文档

© www.soinside.com 2019 - 2024. All rights reserved.