如何使 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
}
近年来,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
}