NodeJS Headless 动态漏扫爬虫学习记录(爬虫篇)
字数 1136 2025-08-25 22:58:55

NodeJS Headless 动态漏扫爬虫学习指南

1. 前言与背景

Headless Chrome 动态爬虫技术是Web安全领域的重要工具,本文基于NodeJS和Puppeteer实现动态漏扫爬虫。

技术背景

  • Puppeteer: 谷歌推出的Headless Chrome NodeJS API
  • Pyppeteer: Python封装的Puppeteer API(存在更新慢、BUG多等问题)
  • 动态爬虫优势: 能够处理JavaScript渲染的页面,获取传统爬虫无法获取的内容

2. JavaScript基础

2.1 事件模型

JavaScript事件模型分为三种:

  1. 内联事件: 直接在HTML标签中定义的事件
    <div onclick="alert('1')">123</div>
    
  2. DOM0级事件: 通过元素属性绑定的事件
    element.onclick = function() {...}
    
  3. DOM2级事件: 使用addEventListener绑定的事件
    element.addEventListener('click', function() {...})
    

2.2 原型链

JavaScript是基于原型的语言,每个对象都有一个原型对象,形成原型链:

function Cat() {
    this.color = 'test'
}
var cat = new Cat()
console.log(cat.__proto__ === Cat.prototype) // true

属性查找顺序:实例对象 → 原型 → 原型的原型 → ... → null

3. URL抓取策略

3.1 基础URL抓取

function getSrcAndHrefLinks(nodes) {
    let result = [];
    for(let node of nodes){
        let src = node.getAttribute("src");
        let href = node.getAttribute("href");
        let action = node.getAttribute("action");
        if (src) result.push(src);
        if (href) result.push(href);
        if(action) result.push(action);
    }
    return result;
}
const links = await page.
$$
eval('[src],[href],[action]', getSrcAndHrefLinks);

3.2 动态URL抓取

通过触发DOM事件获取动态生成的URL:

  1. 收集页面所有事件
  2. 模拟触发这些事件
  3. 捕获事件触发生成的URL

4. DOM事件收集与触发

4.1 Hook事件收集

DOM0级事件Hook

function dom0Hook(that, event_name) {
    console.log("tagname: " + that.tagName + ", event_name:" + event_name);
}
Object.defineProperties(HTMLElement.prototype, {
    onclick: {
        set: function(newValue){
            onclick = newValue;
            dom0Hook(this, "click");
        }
    },
    onchange: {
        set: function(newValue){
            onchange = newValue;
            dom0Hook(this, "change");
        }
    }
});

DOM2级事件Hook

let oldEvent = Element.prototype.addEventListener;
Element.prototype.addEventListener = function(event_name, event_func, useCapture) {
    console.log("tagname: " + this.tagName + ", event_name:" + event_name);
    oldEvent.apply(this, arguments);
};

4.2 内联事件收集

function trigger_inline(){
    var nodes = document.all;
    for(var i = 0; i < nodes.length; i++) {
        var attrs = nodes[i].attributes;
        for(var j = 0; j < attrs.length; j++) {
            attr_name = attrs[j].nodeName;
            attr_value = attrs[j].nodeValue;
            if(attr_name.substr(0, 2) == "on") {
                console.log(attrs[j].nodeName + ' : ' + attr_value);
                eval(attr_value);
            }
            if(attr_name in {"src":1, "href":1} && attrs[j].nodeValue.substr(0,11) == "javascript:") {
                console.log(attrs[j].nodeName + ' : ' + attr_value);
                eval(attr_value.substr(11));
            }
        }
    }
}

4.3 事件触发

function executeEvent() {
    var firedEventNames = ["focus", "mouseover", "mousedown", "click", "error"];
    var firedEvents = {};
    var length = firedEventNames.length;
    for(let i = 0; i < length; i++) {
        firedEvents[firedEventNames[i]] = document.createEvent("HTMLEvents");
        firedEvents[firedEventNames[i]].initEvent(firedEventNames[i], true, true);
    }
    
    var eventLength = window.eventNames.length;
    for(let i = 0; i < eventLength; i++) {
        var eventName = window.eventNames[i].split("_-_")[0];
        var eventNode = window.eventNodes[i];
        var index = firedEventNames.indexOf(eventName);
        if(index > -1) {
            if(eventNode != undefined) {
                eventNode.dispatchEvent(firedEvents[eventName]);
            }
        }
    }
    let result = window.info.split("_-_");
    result.splice(0, 1);
    return result;
}

5. 导航处理

5.1 前端跳转处理

使用定制版Chrome浏览器Hook前端跳转:

  • 修改Chromium源码实现导航Hook
  • 禁止页面前端跳转并收集跳转URL
  • 底层Hook所有非默认事件

5.2 后端跳转处理

await page.on('response', interceptedResponse => {
    let status = interceptedResponse.status();
    if(status.toString().substr(0, 2) === "30") {
        console.log("url: " + interceptedResponse.url());
        console.log("status: " + status);
        console.log("headers: " + interceptedResponse.headers().location);
        // 添加进任务队列
        cluster.queue(interceptedResponse.headers().location);
    }
});

6. Cookie管理

6.1 初始Cookie设置

从文本或数据库读取Cookie,在爬行前设置

6.2 Cookie同步插件

function updateCookie(domain, name, value){
    let api = "http://127.0.0.1/add-cookie";
    $.post(api, {
        "domain": domain,
        "name": name,
        "value": value,
    }, function(data, status) {
        console.log(status);
    });
}

chrome.cookies.onChanged.addListener((changeInfo) => {
    if(changeInfo.removed === false){
        updateCookie(changeInfo.cookie.name, changeInfo.cookie.value, changeInfo.cookie.domain);
    }
});

7. URL去重策略

7.1 参数分析

处理不同类型的参数:

  1. 纯字母参数:保留(通常表示路由功能)
    ?m=home&c=index&a=index
    
  2. 字母数字混合:选择性保留
    ?type=202cb962ac59075b964b07152d234b70
    
  3. 纯数字、URL编码:去重
    ?id=1 → ?id={int}
    ?msg=%E6%B6%88%E6%81%AF → ?msg={urlencode}
    

7.2 相似页面去重

使用网页结构相似度算法进行去重

8. 性能优化

8.1 图片资源优化

await page.setRequestInterception(true);
await page.on('request', interceptedRequest => {
    if(interceptedRequest.resourceType() === 'image' || interceptedRequest.url().endsWith('.ico')) {
        let images = fs.readFileSync('public/image.png');
        interceptedRequest.respond({
            'contentType': 'image/png',
            'body': Buffer.from(images)
        });
    } else {
        interceptedRequest.continue();
    }
});

8.2 拦截logout请求

await page.on('request', interceptedRequest => {
    if(interceptedRequest.url().indexOf("logout") !== -1){
        interceptedRequest.abort();
    } else {
        interceptedRequest.continue();
    }
});

8.3 并发处理

使用puppeteer-cluster库实现单Chrome多tab并发

9. 开源实现

项目地址: https://github.com/Passer6y/CrawlerVuln

10. 总结

本文详细介绍了基于NodeJS和Puppeteer的动态漏扫爬虫实现,包括:

  • URL抓取策略
  • DOM事件处理
  • 导航锁定机制
  • Cookie管理
  • 去重算法
  • 性能优化技巧

通过合理组合这些技术,可以构建一个高效的动态漏扫爬虫系统。

NodeJS Headless 动态漏扫爬虫学习指南 1. 前言与背景 Headless Chrome 动态爬虫技术是Web安全领域的重要工具,本文基于NodeJS和Puppeteer实现动态漏扫爬虫。 技术背景 Puppeteer: 谷歌推出的Headless Chrome NodeJS API Pyppeteer: Python封装的Puppeteer API(存在更新慢、BUG多等问题) 动态爬虫优势: 能够处理JavaScript渲染的页面,获取传统爬虫无法获取的内容 2. JavaScript基础 2.1 事件模型 JavaScript事件模型分为三种: 内联事件 : 直接在HTML标签中定义的事件 DOM0级事件 : 通过元素属性绑定的事件 DOM2级事件 : 使用addEventListener绑定的事件 2.2 原型链 JavaScript是基于原型的语言,每个对象都有一个原型对象,形成原型链: 属性查找顺序:实例对象 → 原型 → 原型的原型 → ... → null 3. URL抓取策略 3.1 基础URL抓取 3.2 动态URL抓取 通过触发DOM事件获取动态生成的URL: 收集页面所有事件 模拟触发这些事件 捕获事件触发生成的URL 4. DOM事件收集与触发 4.1 Hook事件收集 DOM0级事件Hook DOM2级事件Hook 4.2 内联事件收集 4.3 事件触发 5. 导航处理 5.1 前端跳转处理 使用定制版Chrome浏览器Hook前端跳转: 修改Chromium源码实现导航Hook 禁止页面前端跳转并收集跳转URL 底层Hook所有非默认事件 5.2 后端跳转处理 6. Cookie管理 6.1 初始Cookie设置 从文本或数据库读取Cookie,在爬行前设置 6.2 Cookie同步插件 7. URL去重策略 7.1 参数分析 处理不同类型的参数: 纯字母参数:保留(通常表示路由功能) 字母数字混合:选择性保留 纯数字、URL编码:去重 7.2 相似页面去重 使用网页结构相似度算法进行去重 8. 性能优化 8.1 图片资源优化 8.2 拦截logout请求 8.3 并发处理 使用puppeteer-cluster库实现单Chrome多tab并发 9. 开源实现 项目地址: https://github.com/Passer6y/CrawlerVuln 10. 总结 本文详细介绍了基于NodeJS和Puppeteer的动态漏扫爬虫实现,包括: URL抓取策略 DOM事件处理 导航锁定机制 Cookie管理 去重算法 性能优化技巧 通过合理组合这些技术,可以构建一个高效的动态漏扫爬虫系统。