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

React应用中在textarea内展示自定义标签并规避XSS漏洞的方法

React中安全实现带标签的可输入区域方案

原生<textarea>只能展示纯文本,没法渲染带样式的标签,要实现你要的效果,得换个思路,同时严格防范XSS风险,具体方案如下:

核心思路:用contenteditable容器模拟输入框,搭配输入净化

放弃原生textarea,使用contenteditable="true"的div/section模拟输入区域,同时对用户输入的内容做严格过滤,只允许安全的标签结构。

1. 依赖XSS净化库

用DOMPurify来处理用户输入,它会自动过滤掉危险的HTML标签和属性,只保留你允许的内容。先安装依赖:

npm install dompurify

2. 实现自定义标签输入组件

下面是一个简化的React组件示例,实现标签添加、删除,同时保证输入安全:

import React, { useState, useRef } from 'react';
import DOMPurify from 'dompurify';

const TagInput = () => {
  const [content, setContent] = useState('');
  const inputRef = useRef(null);

  // 处理输入内容变化,净化后更新
  const handleInput = () => {
    if (!inputRef.current) return;
    // 净化内容,只允许span标签和特定类名、属性
    const cleanContent = DOMPurify.sanitize(inputRef.current.innerHTML, {
      ADD_TAGS: ['span'],
      ADD_ATTR: ['class', 'data-tag'],
      ALLOWED_ATTR: ['class', 'data-tag'],
      FORBID_TAGS: ['script', 'iframe', 'a'], // 禁止危险标签
    });
    setContent(cleanContent);
  };

  // 添加标签:用户输入逗号或回车时触发
  const handleKeyDown = (e) => {
    if (e.key === ',' || e.key === 'Enter') {
      e.preventDefault();
      const selection = window.getSelection();
      if (!selection.rangeCount) return;
      const range = selection.getRangeAt(0);
      const tagText = range.startContainer.textContent.trim();
      if (!tagText) return;

      // 创建标签元素
      const tagElement = document.createElement('span');
      tagElement.className = 'tag';
      tagElement.dataset.tag = tagText;
      tagElement.innerHTML = `${tagText} <button onclick="this.parentElement.remove()">×</button>`;

      // 替换选中的文本为标签
      range.deleteContents();
      range.insertNode(tagElement);
      // 在标签后插入空白文本节点,保证继续输入
      range.insertNode(document.createTextNode(' '));
      range.setStartAfter(tagElement.nextSibling);
      range.setEndAfter(tagElement.nextSibling);
      selection.removeAllRanges();
      selection.addRange(range);

      // 手动触发输入处理
      handleInput();
    }
  };

  return (
    <div
      ref={inputRef}
      contentEditable
      onInput={handleInput}
      onKeyDown={handleKeyDown}
      dangerouslySetInnerHTML={{ __html: content }}
      style={{
        border: '1px solid #ccc',
        padding: '8px',
        minHeight: '100px',
        borderRadius: '4px',
      }}
    />
  );
};

export default TagInput;

3. 关键安全点

  • 严格过滤内容:通过DOMPurify的配置,只允许必要的标签(比如示例中的span)和属性,禁止所有可能触发XSS的标签(如script、iframe)。
  • 避免直接渲染用户输入:所有插入到页面的内容都必须经过DOMPurify净化,再通过dangerouslySetInnerHTML渲染。
  • 标签交互安全:删除按钮的事件直接绑定在元素上,避免使用eval或危险的事件绑定方式。

4. 替代方案:使用成熟的富文本编辑器

如果不想自己造轮子,可以选择自带XSS防护的富文本编辑器,比如:

  • react-quill:支持自定义格式,可配置允许的内容类型
  • slate.js:基于React的可定制富文本框架,能精确控制内容结构和安全规则

这些编辑器通常内置了输入净化机制,只需配置允许的标签格式即可。

内容的提问来源于stack exchange,提问作者Jeroen Beljaars

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 12:54:25