如何规避上传文件名引发的XSS漏洞?
嘿,这个场景太典型了——当用户上传的可控文件名直接被渲染到页面DOM中时,很容易触发存储型XSS。我来从前端JavaScript和服务器端两个层面给你具体的防护方案:
前端JavaScript防护方案
核心思路是永远不要把未转义的用户可控内容直接插入到HTML结构中,以下是几种可靠的实现方式:
使用HTML转义函数处理文件名
把文件名中的特殊HTML字符(<,>,",',&)转成对应的HTML实体,这样浏览器会把它们当成纯文本解析,而不是执行代码。比如写一个通用的转义函数:function escapeHtml(unsafeStr) { return unsafeStr .replace(/&/g, "&") .replace(/</g, "<") .replace(/>/g, ">") .replace(/"/g, """) .replace(/'/g, "'"); } // 使用示例:展示文件名时先转义 const maliciousFileName = "<img src=x onerror=alert(document.cookie)>"; const safeFileName = escapeHtml(maliciousFileName); document.getElementById("file-name-container").innerHTML = safeFileName;用
textContent替代innerHTML
如果你只是要展示文件名文本,直接使用DOM元素的textContent属性,它会自动把内容当成纯文本处理,完全避免HTML解析:const fileName = "<img src=x onerror=alert(document.cookie)>"; document.getElementById("file-name-container").textContent = fileName;创建文本节点插入DOM
另一种更底层的方式是用document.createTextNode()生成纯文本节点,再添加到页面中:const fileName = "<img src=x onerror=alert(document.cookie)>"; const textNode = document.createTextNode(fileName); document.getElementById("file-name-container").appendChild(textNode);
服务器端防护方案
前端防护是最后一道防线,服务器端防护才是根本,从源头切断恶意内容的传播:
上传时重命名文件
不要保留用户上传的原始文件名,而是生成一个安全的随机文件名(比如UUID+文件扩展名),把原始文件名存在数据库中用于展示。这样即使原始文件名有恶意代码,也不会被渲染到页面上。举个Node.js的例子:const fs = require('fs'); const path = require('path'); const { v4: uuidv4 } = require('uuid'); // 处理文件上传逻辑 const uploadDir = './uploads'; const originalFileName = req.file.originalname; // 生成安全文件名:UUID+原文件扩展名 const safeFileName = `${uuidv4()}${path.extname(originalFileName)}`; // 重命名上传的文件 fs.renameSync(req.file.path, path.join(uploadDir, safeFileName)); // 将safeFileName和originalFileName关联存入数据库 db.query('INSERT INTO files (safe_name, original_name) VALUES (?, ?)', [safeFileName, originalFileName]);过滤/清理原始文件名
如果必须保留原始文件名展示,可以在服务器端先对文件名进行清理:移除所有HTML特殊字符,或者限制文件名只能包含字母、数字和-_.这类安全符号。比如Python中的处理:import re def sanitize_filename(filename): # 只保留字母、数字和安全符号,移除其他字符 return re.sub(r'[^\w\-.]', '', filename) malicious_filename = "<img src=x onerror=alert(document.cookie)>" safe_filename = sanitize_filename(malicious_filename) # safe_filename 会变成 "imgsrcxonerroralertdocumentcookie"输出文件名时做HTML转义
即使前端已经做了转义,服务器端在返回文件名(比如通过接口返回给前端)时,也可以提前做HTML转义,实现双重防护。比如在Java中使用org.apache.commons.text.StringEscapeUtils.escapeHtml4()方法。
内容的提问来源于stack exchange,提问作者gstackoverflow

