基于BeautifulSoup4区分文本与HTML元素,拆分XSS payload结构
可行的拆分方案:基于DOM解析的结构化提取
你遇到的核心问题其实是字符串匹配无法区分“属性内的HTML内容”和“真正的DOM元素”,直接用正则或者字符串切割很容易被payload里的特殊字符(比如<>)或者嵌套结构坑。靠谱的做法是先把这段HTML转换成DOM树,再按规则遍历提取,这样能精准区分元素、属性、属性值(包括带HTML的payload)。
核心思路
- 用DOM解析替代字符串处理:不管payload里是纯文本还是包含HTML片段,DOM解析都会把属性值当成完整的字符串,不会把属性里的
<script>之类的当成新的DOM节点。 - 遍历DOM树的规则:
- 遇到元素节点:先提取标签名(统一小写,符合HTML规范)
- 遍历元素的所有属性:依次提取属性名和属性值
- 只遍历元素子节点中的元素节点:元素内的文本节点(比如
<div>xxx</div>里的xxx)如果不是你需要的,可以直接跳过,避免干扰属性值的提取。
具体实现示例(JavaScript)
不管是在浏览器控制台还是Node.js环境(用jsdom模拟DOM),都可以用这个思路实现:
浏览器环境代码
function extractXssStructure(html) { const parser = new DOMParser(); const doc = parser.parseFromString(html, 'text/html'); const output = []; // 递归遍历DOM节点 function traverse(node) { if (node.nodeType === Node.ELEMENT_NODE) { // 加入元素标签名 output.push(node.tagName.toLowerCase()); // 遍历所有属性,加入属性名和属性值 Array.from(node.attributes).forEach(attr => { output.push(attr.name.toLowerCase()); output.push(attr.value); }); // 只遍历子元素节点,跳过文本节点(避免提取元素内的无关文本) Array.from(node.childNodes).forEach(child => { if (child.nodeType === Node.ELEMENT_NODE) { traverse(child); } }); } } // 从解析后的body第一个子节点开始遍历 traverse(doc.body.firstChild); return output.join(' '); } // 测试示例1:基础payload const basicHtml = '<picture><source srcset="x"><img onerror="***payload***"></picture>'; console.log(extractXssStructure(basicHtml)); // 输出:picture source srcset x img onerror ***payload*** // 测试示例2:payload包含HTML片段 const htmlWithHtmlPayload = '<picture><source srcset="x"><img onerror="<script>alert(1)</script>"></picture>'; console.log(extractXssStructure(htmlWithHtmlPayload)); // 输出:picture source srcset x img onerror <script>alert(1)</script>
Node.js环境(需要安装jsdom)
先安装依赖:npm install jsdom,然后代码:
const { JSDOM } = require('jsdom'); function extractXssStructure(html) { const dom = new JSDOM(html); const doc = dom.window.document; const output = []; function traverse(node) { if (node.nodeType === node.ELEMENT_NODE) { output.push(node.tagName.toLowerCase()); Array.from(node.attributes).forEach(attr => { output.push(attr.name.toLowerCase()); output.push(attr.value); }); Array.from(node.childNodes).forEach(child => { if (child.nodeType === node.ELEMENT_NODE) { traverse(child); } }); } } traverse(doc.body.firstChild); return output.join(' '); } // 测试同浏览器环境
关键注意点
- 不要解析属性值内的HTML:DOM解析会自动把属性值当成字符串,所以就算payload里有
<>这类字符,也会完整保留,不会被当成新的DOM节点处理,完美解决你说的“追加子元素内容异常”的问题。 - 统一大小写:HTML标签和属性名不区分大小写,转换成小写后输出更规范。
- 跳过无关文本节点:如果你的payload结构里偶尔有元素内的文本(比如
<div>test <img onerror="..."></div>),可以根据需求决定是否提取文本——只需要在遍历子节点时加入文本节点的处理逻辑即可。
内容的提问来源于stack exchange,提问作者Login
相关产品推荐
相关产品推荐

