当前位置: 首页 > 后端技术 > Node.js

网站安全监控-节点实战

时间:2023-04-03 19:22:19 Node.js

本文首发于个人博客,转载请保留出处http://jsorz.cn/blog/2016/11/node-action-in-website-detection。html(这里是一篇拖了将近一年的文章。。。)去年这个时候接手了一个累死人的项目。总体目标是:1.为用户网站提供安全监控服务。用户在我们的管理系统中添加站点并勾选需要检测的项目;2、然后后台爬虫系统完成用户站点链接的存储,定时对站点内的链接进行各种检测任务;3、然后前端系统向用户下发网站监控报告。中间一步,最重要的爬虫系统,第一次学习使用Nodejs。节点开始安装nvmcurl-o-https://raw.githubusercontent.com/creationix/nvm/v0.25.2/install.sh|bashnode0.12.xnvminstall0.12cnpmmirrornpminstall--registry=http://r.cnpmjs.org-gcnpmdebugtoolcnpminstall-gnode-inspectornode-debugyourApp.js教程Node.js包教学不包含中文APIdocumentSuperagent有更多的语法配置项。如果superagent是$.post(),那么request就是$.ajax()cheerio用于DOM解析,提供类似jqueryselector的接口。它是GithubPromise中异步进程控制Q的一个实现。承诺不是万灵药。转换复杂且动态确定的执行链很烦人。异步有很多喜欢。有人说它有毒。我觉得用eventproxy@朴黎的基于事件的流程控制很简单。我觉得兼容promise或者async摘要里说了,主程序设计非常好用。本项目的目标分为3个步骤:添加检测任务=>爬虫执行任务=>前端报表展示。但是在实际实现中,需要对爬虫进行细分,最后会有6步流程。1.添加监控站点在web端添加站点任务,输入客户概要信息和监控站点列表,即可更改检测类别周期和具体检测项目等配置。“可用性”检测主要是网站的一些基本信息,通常可以在首页的http头中找到。但是“内容检测”需要检测网站中的所有页面,所以会有“检测深度”和“最大页面数”的配置。巨大的压力。此外,还有一个“安全检测”模块。具体检测项目包括:ActiveX木马、iframe木马、js木马、WebShell后门、暗链接、后台地址检测。由于本人对安全了解太少,在本系统中的实现也很简单,就不多说了。..2.站点入库由于上面的“内容检测”和“安全检测”需要检测网站首页下的所有其他页面,所以我们需要维护一张site表和site_link表,记录该网站的域名和ip信息网站,并保存每个页面的链接信息,包括页面相对于首页的深度级别,页面链接是否来自同源、同域或外部链接,以及发现时间和状态信息页面的。另外需要注意的是,从首页开始抓取链接,再抓取其后继链接到链接,整个过程要采用“广度优先”的策略。“检测深度”和“最大页数”此时也有限制。如果当前页面深度为N,且N+1>检测深度,则该页面将不再推送爬行队列中的后续链接。3、定时生成检测任务配置站点任务并将站点链接入库后,可以按照上面配置的检测周期定时生成任务。例如,当内容检测期到来时,需要提取站点下所有匹配的页面链接,针对每个页面的每个检测项生成检测任务。需要注意的是,在生成任务时,需要按照页面链接的顺序遍历,而不是先遍历检测项。这样做的好处是当执行特定的爬虫时,队列中相同url的检测任务会连续,这样爬虫就可以组合起来进行爬取。同一个url只需要抓取一次就可以给不同的检测任务执行。4.爬虫执行检测任务。如果第3步的生成任务是“生产者”,那么现在就轮到“消费者”了。爬虫系统必须支持并发,多机+多线程。在一台机器上,一次从任务队列中取出100个任务。如步骤3所述,检测任务按照同一个url连续排列,那么这100个检测任务可以抽取20个任务组(举例),每个任务组只能抓取一个页面,然后将内容传递给每个检测执行项目。每个检测项的执行结果以日志的形式存储。如果爬虫的机器不是那种“树莓派”微型机器,可以先将日志以文件的形式保存在本地,为爬虫预留带宽。或者将每个检测项的结果日志发送到指定的地方,这样会占用较多的带宽,需要专门的日志服务器。5、定时计算日志爬虫任务执行过程非常连续,又非常离散。连续是因为完成一项任务后,将完成下一项任务。Discrete表示日志是离散的,一页一个检测项,单页一个。一次的执行日志意义不大,只有结合整个站点、时间段、检测类别等维度才有价值。从前面的步骤可以看出,站点链接和周期性生成任务的存储会有一定的“顺序”控制,全部存储完成后才会进入下一步。爬虫系统是非常离散的,可能会分布到多台机器上。只要任务队列里有东西,就会取出来执行,不会区分任务来自哪个站点或类别。因此,如果日志分布在每台爬虫机器上,就需要一个定时脚本将它们汇总计算后传回数据中心。而如果有统一的日志服务器,还需要将原始日志计算成有效数据。6.前端报表查询前端系统直接查询数据中心,可以得到站点级和检测类别级的报表。之前的“生产者”在生成检测任务时,会在数据库中标记一些任务模块的时间信息,前端系统也可以查看一些调度状态信息。架构图整个系统分为前端系统、任务生产者、爬虫系统、日志计算同步过程四个部分。架构图如下。您可以看到箭头代表数据流的方向。爬虫的调度和执行是典型的“生产者-消费者”模型,只有一个生产者和N个消费者爬虫进程。所谓检测任务队列,其实就是数据库中的一张表。多个爬虫进程或多个爬虫机器共享这张表。只要从表头开始读,读完就需要重新设置状态位。防止其他进程重复读取。当任务执行失败时,会将检测任务的记录移动到表尾,并设置失败次数的字段。在本文的第一节中,爬虫实现思路的技术框架中列举了一些库。在页面爬取方面,使用superagent获取页面内容,使用cheerio进行文档分析。对于一些不需要解析内容的爬取任务(比如查询某个页面的头部信息,或者查看某个页面是否有200状态),使用request发送请求。对于爬虫系统中最关键的异步流控,我在实现上尝试了多种风格,在数据库读写层面使用promise风格,在检测项内部使用async,在爬虫实例中使用eventproxy进行流式处理控制。目录结构base/继承和通用相关的conf/各种配置项(检测项的配置、规则的配置)常量/各种常量的定义和配置数据库/数据库配置和连接池模型/数据库表对应的jsonschemaadao/Dao对应模型CRUD封装util/通用数据helperfactory/工厂封装类,统一任务构建流程模块/具体检测项(爬虫承载的具体任务实现)basic/基础信息可用性检测content/内容检测大类secure/安全检测categorycommon/站点存储、链接存储、爬取页面内容也被抽象成一个通用的taskcrawler/爬虫对象和pool管理(1个爬虫实例只负责1个页面请求)scheduler/监控调度过程(每轮执行一次偶尔)runner.js用于执行检测任务(任务消费者)siteRunner.js是runnerapp.js的主程序,用于“站点链接存储”,用于启动runnerappSite.js主程序之一,用于启动SiteRunnerappPath.js,启动低频高请求检测任务(详见下文地址类型检测类)。从上面的结构可以看出爬虫的实现过程。程序运行时的调用流程为:app.js=>runner.js=>爬虫=>具体检测项。app.js其中,app.js只是一个程序入口,为runner构造了一些参数。代表参数有:runner在每轮执行中取出的task数量(即一个consumer每次消费的数量),和轮次执行之间的间隔(因为执行时会有N次请求一轮任务,如果没有间隔限制,同时等待请求响应的线程过多,就会爆炸)。runner.jsrunner.js首先提供了一个支持时间间隔的循环执行接口,这里使用async库实现。RunnerBase.prototype={开始:函数(){varthat=this;那._count=0;async.whilst(function(){if(that.loopCount){returnthat._countRunner->app.jsSITE:'SITE_TASK',//站点存储类->SiteRunner->appSite.jsPATH:'PATH_TASK'//路径检测类->Runner->appPath.js};这里定义了两种类型的任务属性:INCLUSIVE表示该任务可以与目标url相同的其他任务组合,只需要请求一次页面,并传递页面上下文的内容,请参考部分实现代码crawler.js的详细信息。而EXCLUSIVE是指任务需要发送自己的请求,比如检查Whois信息,或者检查是否有一些后门路径。此类任务无法合并。而runnerType是另外一个层次的分类。在主程序设计的第二节中提到站点链接存储在库中。我在实现的时候做了一层抽象,将“站点存储”和“链接存储”作为一个检测任务,分别放在一个单独的队列表中。通过这种方式,可以共享一些爬虫和运行器逻辑。另外,对于路径检测,比如Webshel??l地址检测,或者敏感路径检测,由于规则的字典非常庞大,一个检测项可能有上千个请求。因此,对于这种“低频”、“高要求”的检测项目,我也将任务放在另一个独立的队列表中,同样由另一个程序入口启动。ParallelandPipeline总结了整个爬虫调度和检测项的工作流程,同时是“串行”和“并行”。runner执行时,每一轮会取出一堆task,生成多个crawler实例,是并行的。在每个爬虫内部,可以理解为类似工厂的流水线模式,先请求页面url,然后将页面内容一一传递给每个检测项,完成相应的提取或检测工作。每个检测项都会在内部写入自己的日志文件。最后,所有检测任务完成后,经过一段时间后进入下一轮runner执行。注意,检测项是根据目标url进行分组的,在爬虫实例上执行。同组的检测项执行完毕后,相应的爬虫实例也应该被销毁。runner对象的实例一直存在,它会像定时器一样每隔十秒触发另一轮执行。同时,一个runner实例应该对应一个进程,多个runner实例应该运行在多个爬虫服务器上。总结与不足本项目从去年11月份开始需求分析,做界面demo,到任务分解组装流程设计,再到爬虫实现。我也是从零开始学习nodejs的。一开始整个过程??只有我一个人。对自己的挑战有了很大的提升。用了两三个月,才有人帮我做日志恢复计算和前端上报。之后,项目就交给了他们。他们后来在安全检测项目和多机部署和通信方面做了一些增强。我在单机(MacAirBook@2012)上运行的效率平均为1小时,爬取近万页,完成剩余内容检测项。上图中,一轮取出的任务数量和并行爬虫数量对机器的性能和网络的速度都有要求。如果入不敷出(每轮间隔内只执行少量爬虫),最终会出现很多内存错误,或者TCP连接过多,得不到结果。我尝试后得到的经验参数是每10秒取50个任务,根据目标url合并后大概有20~30个任务组。注:使用云服务器,内存2G,带宽2M。还是很简单粗暴。爬虫系统和检测项目还是比较简单的。可以从以下几个方面改进:nodejs的集群尝试是否可以提高爬取效率。数据库“同步写入”锁问题,多台爬虫机器共享同一个数据库的任务队列存在性能瓶颈(或者集中存储的通病)和内容检测的篡改识别算法。目前只有content-length和md5用于识别页面变化。引入PhantomJS,对网页进行截图,警告页面被恶意篡改,增强安全性检测强度,识别各种挂马和暗链代码模式,支持扩展检测项,让更有经验的安全人员编写python代码工程师也要接入系统TODO在开发过程中也会记录遇到一些问题后,nodejs爬虫实现中遇到的坑,有空会慢慢梳理补充,抽象出一个业务独立可配置的”用于学习和交流的生产者-消费者”模式网站检测框架。首发于个人博客,转载请保留出处http://jsorz.cn/blog/2016/11/node-action-in-website-detection.html