You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 12:45:33