踩坑记录(输入框出现xss攻击)

863 阅读7分钟

前言

有一天早上公司项目安全风险监测部门发了一个邮件过来,表示我们的项目有个安全漏洞,表情输入框组件存在反射型 XSS攻击。后来我也是在同事大佬们(娟姐和绿哥)的帮助下顺利修复。写个文分享一下,愿诸君前路平坦。。

问题展现

bug复现

项目背景

项目是react技术栈写的,中间用到一个需要可以输入颜文字表情包的输入框组件,我看项目引用的是 react-input-emoji 这个插件

组件UI

组件是这样的

image.png

复现xss攻击

只有复制事件出现问题哈,手动输入没问题的

我在输入框中粘贴“<img src=X onerror=prompt'1' />” 这段代码会出现下面情景的xss攻击,弹框出现一个输入框

image.png

输入框中复制粘贴上“img src=X onerror=prompt1 />”`会导致 XSS(跨站脚本攻击)的原因是因为输入框中的内容直接插入到页面中作为 HTML 代码执行。

<img> 标签的 src 属性被设置为 X,而 onerror 事件被设置为 prompt。这意味着,如果加载这个图片失败(如图片路径为无效路径),浏览器会执行 onerror 事件中的代码,即弹出一个提示框(prompt)。攻击者可以利用这个漏洞,将恶意的 JavaScript 代码作为 onerror 事件的一部分,从而执行任意的恶意操作,如窃取用户的敏感信息、劫持用户会话等。

github 评论区

github 问题反馈评论区这个问题也是存在的,作者未作出回复,应该是没修复哈,因为我也升级到最新版本看了,问题还是存在的。也不知道别人项目引用有没有这个问题哇?

image.png

问题原因

image.png

从源代码上看,这段代码定义了一个名为 handlePaste 的函数,用于处理粘贴事件。

  1. event.preventDefault(); - 这一行代码调用了事件对象的 preventDefault() 方法,用于阻止默认的粘贴行为。通常,当用户在文本区域进行粘贴操作时,浏览器会默认将剪贴板中的内容直接粘贴到文本区域中。通过调用 preventDefault(),可以阻止这种默认行为。
  2. let content; - 这一行代码声明了一个名为 content 的变量,用于存储粘贴的内容。
  3. if (event.clipboardData) { ... } - 这个条件语句检查浏览器是否支持 clipboardData 属性,以确保可以访问剪贴板中的数据。
  4. content = event.clipboardData.getData("text/plain"); - 这一行代码使用 clipboardData 对象的 getData() 方法,传入参数 "text/plain",以获取剪贴板中纯文本的内容,并将其赋值给 content 变量。
  5. content = pollute(content); - 这一行代码调用了一个名为 pollute 的函数,并将 content 变量作为参数传递给它。根据代码提供的信息,我们无法得知 pollute 函数的具体实现,但可以猜测它可能对粘贴的内容进行某种处理或修改。
  6. document.execCommand("insertHTML", false, content); - 这一行代码使用 document.execCommand() 方法来执行一个命令,具体命令是 "insertHTML"。该命令将 content 变量的值作为 HTML 插入到当前文档的光标位置或选中区域。

综上所述,该段代码的功能应该是在粘贴事件发生时,阻止默认的粘贴行为,获取剪贴板中的纯文本内容,并将处理后的内容插入到当前文档的光标位置或选中区域。

但是哈

根据提供的 handlePaste 函数的代码,它应该是并不能完全阻止 <img src=X onerror=prompt1 /> 这样的 XSS 攻击。

在 handlePaste 函数中,粘贴的内容首先被获取并存储在 content 变量中。然后,通过调用未提供具体实现的 pollute 函数对 content 进行处理。最后,使用 document.execCommand("insertHTML", false, content) 将处理后的内容插入到文档中。

然而,这段代码并没有对 content 的内容进行验证、过滤或转义,也没有提供任何明确的防御措施来防止 XSS 攻击。如果 pollute 函数没有正确处理 content 的内容,那么 <img src=X onerror=prompt1 /> 这样的恶意代码可能会被插入到文档中,导致 XSS 攻击的发生。

所以,提供的 handlePaste 函数本身应该是并不能阻止 <img src=X onerror=prompt1 /> 这样的 XSS 攻击的。

解决办法

阻止默认行为

最后对于我们项目的场景,粘贴文本触发,最后是选用的这个方法。选用也没啥考虑点,就是简单。大家可千万要先看项目情况再定哈。

inputRef = React.createRef()
const inputElement = this.inputRef.current?.textInput?.current
if (inputElement) {
  inputElement.addEventListener('paste', (e) => {
    e.stopPropagation()
  }, {capture:true})
}

首先,通过 React.createRef() 创建了一个名为 inputRef 的引用(ref)。这个引用将用于引用一个输入框元素。

然后,通过 this.inputRef.current?.textInput?.current 获取了实际的输入框元素。使用了可选链操作符 ?. 来确保在引用链中的任何一级属性为 null 或 undefined 时不会引发错误。

接下来,通过 addEventListener 方法向输入框元素添加了一个名为 'paste' 的事件监听器。这个事件监听器会在用户粘贴内容到输入框时触发。

在事件监听器的回调函数中,调用了 e.stopPropagation() 方法来阻止事件的进一步传播。这意味着事件不会继续传递给父元素或其他事件监听器。

最后,通过传递 {capture:true} 参数来指定事件捕获阶段进行事件监听。这意味着在事件捕获阶段中的其他事件监听器将无法处理该事件。

通过阻止默认行为和事件传播,来达到我们这个输入框粘贴场景阻止可能引起xss攻击的一个目的。

扩展

补充些前端防止xss攻击手段咯

  1. 输入验证:对用户输入的数据进行验证和过滤,确保只接受预期的数据格式。例如,可以使用正则表达式对输入进行过滤,只允许符合特定格式的数据通过。
  2. 转义字符:在将用户输入显示在网页上之前,对特殊字符进行转义处理,将其转换为它们的实体编码形式。例如,将"<"转义为"<"、">"转义为">"、"&"转义为"&"等。
  3. 内容安全策略(Content Security Policy,CSP):通过在 HTTP 头中设置 CSP,限制页面中可以加载和执行的内容来源。CSP 可以阻止恶意脚本的执行,并限制其他潜在的安全风险,如通过禁止内联脚本和限制外部脚本的来源等。
  4. HTTP-only 标记:将敏感的 Cookie 标记为 HTTP-only,这样浏览器将禁止通过 JavaScript 访问这些 Cookie,从而减少 XSS 攻击的影响范围。
  5. 使用安全的 DOM 操作:避免使用危险的 DOM 操作,如使用 innerHTML 直接插入用户输入的内容,而是使用更安全的方法,如 createElement 和 appendChild 来动态创建和插入 DOM 元素。
  6. 安全的跳转和链接:在跳转链接时,确保对用户输入的 URL 进行验证和过滤,避免恶意的 JavaScript 代码注入。可以使用正则表达式或库函数对 URL 进行校验。
  7. 使用安全的框架和库:选择使用经过安全审查和广泛使用的前端框架和库,这些框架和库通常会提供内置的安全机制和防护措施,从而减少 XSS 攻击的风险。

总结

虽然现在的框架啥的都做了安全补充,一般也不会出现这些问题。实话讲不是这次我都没遇到过这种。玩归玩闹归闹别拿安全开玩笑。还是那句 -----愿诸君前路平坦!!