如何将Web Component中的innerHTML替换为innerText/textContent以防范XSS?
替换innerHTML为innerText/textContent防范XSS的改造方案
你的代码目前通过innerHTML一次性渲染模板,虽然用了ESAPI编码,但评审建议的innerText/textContent能从根源避免HTML解析带来的XSS风险,以下是具体改造步骤:
核心思路
不再通过拼接HTML字符串的方式渲染内容,而是通过原生DOM API创建每个元素,分别设置元素的属性和文本内容,确保不可信字符串仅作为纯文本插入。
改造后的完整代码
import RealBase from '../real-base'; import productCardTitleCss from '../../../css/_product-card-title.scss?inline'; import baseCss from '../../../css/_base.scss?inline'; // 后续若无需属性编码传递,可移除ESAPI依赖 const ESAPI = require('node-esapi'); class RealProductCardTitle extends RealBase { constructor() { super(); this.defaultClass = 'real-product-card-title'; } connectedCallback() { super.connectedCallback(); this.render(); } static get observedAttributes() { return ['heading', 'form']; } get heading() { return this.getAttribute('heading') || ''; } set heading(value) { // 改用textContent后无需HTML编码,直接存原始值 this.setAttribute('heading', value || ''); } get form() { return this.getAttribute('form') || ''; } set form(value) { this.setAttribute('form', value || ''); } attributeChangedCallback() { this.render(); } render() { const { heading, form } = this; this.classList.add('real-block'); this.classList.add(this.defaultClass); if (!this.shadowRoot) { this.attachShadow({ mode: 'open' }); } // 清空shadowRoot原有内容 this.shadowRoot.innerHTML = ''; // 1. 创建样式元素并加入shadowRoot const styleEl = document.createElement('style'); // 项目内CSS为可信资源,直接用textContent插入 styleEl.textContent = `${productCardTitleCss}${baseCss}`; this.shadowRoot.appendChild(styleEl); // 2. 创建real-heading组件 const headingEl = document.createElement('real-heading'); headingEl.classList.add('real-product-card-title-heading'); // 根据real-heading组件逻辑设置属性或文本 headingEl.setAttribute('input', heading); // 若组件需直接显示heading文本,补充:headingEl.textContent = heading; this.shadowRoot.appendChild(headingEl); // 3. 创建form信息容器 const formEl = document.createElement('div'); formEl.classList.add('real-product-card-title-attributes', 'real-inline-block'); // 核心:用textContent插入不可信字符串,浏览器会自动转义HTML标签 formEl.textContent = form; this.shadowRoot.appendChild(formEl); } } window.customElements.define('real-product-card-title', RealProductCardTitle);
关键修改说明
- 移除innerHTML模板拼接:拆分DOM元素创建流程,彻底避免字符串拼接带来的注入风险。
- style元素用textContent:项目内CSS属于可信资源,直接用textContent插入即可,无需额外处理。
- form内容用textContent插入:这是防范XSS的核心改造,
formEl.textContent = form会将所有内容作为纯文本渲染,任何HTML标签都会被转义,不会被浏览器解析执行。 - 属性设置优化:因为不再通过HTML字符串传递属性,heading和form的setter可移除ESAPI编码逻辑,简化代码。
额外提示
如果real-heading组件的input属性仅用于控制内部文本显示,建议直接给组件设置textContent而非属性,进一步降低风险;若属性本身有业务用途,直接用setAttribute即可,属性值的HTML解析风险远低于innerHTML。
内容的提问来源于stack exchange,提问作者Ajit Goel
相关产品推荐
相关产品推荐

