如何使 Shadow DOM 中的样式元素与严格的 CSP 一起工作?

How to make style element in Shadow DOM work with strict CSP?

近年来,Web 组件标准已成为 Web 的标准 low-level GUI API。它的主要功能之一是 Shadow DOM,它封装了 DOM,大大简化了 CSS 选择器。

一个基本的例子可能是,

const template = document.createElement('template');
template.innerHTML = /*html*/`
  <style>
    p {
      color: red;
    }
  </style>
  <p>Hello, world!</p>
`;

class HelloWorld extends HTMLElement {
  constructor() {
    super();
  }

  connectedCallback() {
    this.attachShadow({ mode: 'open' });
    this.shadowRoot.appendChild(template.content.cloneNode(true));
  }
}

customElements.define('hello-world', HelloWorld);

在我的 HTML 中,我会这样调用我的组件,

<!DOCTYPE html>
<html>

<head>
  <script type="module" src="hello-world.js" defer></script>
</head>

<body>
  <hello-world></hello-world>
</body>

</html>

这种方法正在巩固成为行业基准,Lit、React、Angular、Vue 等框架处于领先地位。现在假设我想应用严格的 CSP。

为此,我从以下政策入手,

Content-Security-Policy "default-src 'self';"

但它不起作用,因为 Web 组件定义中的样式元素是内联的。我知道的唯一解决方案是添加随机数、散列或 unsafe-inline。我不会考虑 unsafe-inline,因为它会降低安全性,从而否定问题的要点,即保持严格的 CSP。让我们考虑其他两种选择。

这是我开始猜测的地方,如果我错了,请纠正我。除了最简单的情况外,制作哈希或随机数是不可能的。只有完全静态的内容才能使用哈希。一旦内容变成动态的,hash 就出错了,CSP 会屏蔽内联样式。这与为定义 Web 组件的 js 文件中嵌入的 HTML 创建散列无关。随机数提出了一个不同的,同样困难的问题。假设我生成一个随机数并将其包含在我的 CSP header 中。如何将其注入到js文件中?

除了在 CSP 规范中添加允许由允许的脚本添加内联样式之外,我想不出一个合理的解决方案。

如何才能做到这一点?

2017 年的建议是使用 <link> 元素

以下是 Apple (rniwa)、Google (hayatoito) 和 Mozilla (annevk) 的主要开发人员评论:

2017 年的一期...也许打开它或创建一个新的。

顺便说一句,(与随机数无关)
connectedCallback 中附加 shadowRoot 会在移动 DOM 元素时导致错误,因为 shadowRoot 只能添加一次(不能删除)

一切都可以链接在 constructor:

  constructor() {
    super() // sets and returns 'this'
       .attachShadow({ mode: 'open' })  // sets and returns this.shadowRoot
       .append(template.content.cloneNode(true)); // no need for appendChild
  }