做Web服务的人,迟早会遇到一个局面:甲方甩来一个URL,问你“这站用的什么技术栈?有没有已知的开源组件?”如果没有趁手的指纹识别工具,只能手动右键查看源码、翻响应头、猜CMS,效率低不说,误判率还高。WhatWeb就是来解决这个问题的信息收集工具——它是用Ruby写的命令行指纹识别器,可以让你在一两秒内拿到目标站点的服务器类型、中间件版本、框架、CMS、JavaScript库等一串技术栈信息。这篇文章我会按实操顺序,把从安装、参数、结果解读到插件写法的完整链路过一遍,适合刚接触资产识别的新手,也适合想把它整合进安全检测流程的同行。
我最早用WhatWeb是在做一次内部资产盘点的时候,几十个站点挨个用浏览器看源码,看到眼瞎。后来换成这个工具,一条命令批量跑完,输出直接存成JSON交给脚本统计,整个人都轻松了。下面这些内容,都是我实际用出来的经验,不是照抄帮助文档。
1. 识别技术栈是整个检测流程的第一步
1.1 为什么信息收集阶段要先摸清技术栈
做安全评估、漏洞自查或者竞品分析,第一件事永远不是拿漏洞扫描器往上怼,而是先搞清楚对面是什么。这里有个很朴素的逻辑:漏洞和组件是绑定的。你只有知道目标是WordPress,才知道要查wp-json是否有异常暴露;知道后端是PHP,才会优先关注反序列化类问题;知道前端用了某个老版本jQuery,才能判断是否存在已知原型链污染利用面。
很多人喜欢直接跑全量漏洞扫描器,结果扫了半天全是中危低危,浪费带宽也浪费时间。我的习惯是先让WhatWeb把技术栈快速过一遍,再根据结果决定后续用哪类检测手段。比如识别到目标是某种开源CMS,那后续思路就是查该CMS的历史漏洞和加固配置;如果识别出来是完全自研的框架,那重点就变成端口、API、鉴权逻辑这些方向。完全不同的两条路,起点都在指纹识别这一步。
另外,技术栈识别也是日常资产梳理的好帮手。公司内部有成百上千个站点,哪个用了老版本组件、哪个已经下线但还在响应请求,用WhatWeb批量跑一遍,结果导出来一比对就一清二楚。我见过不少企业连一个站点清单都维护不明白,实际扫描出来的Web服务数量比资产台账多出两三倍,这工具在这个场景下比任何资产扫描器都直接。
1.2 WhatWeb与其他识别方式相比的取舍
市面上做技术栈识别的工具不少,手动层面有浏览器插件,命令行层面有Nmap脚本、httpx这类工具,甚至直接拿curl看响应头也能识别一部分。Why我会在多数场景下优先选WhatWeb?
第一,它的覆盖面广。WhatWeb内置的插件数量非常庞大,我常用的版本里已经有1800多个插件,覆盖常见CMS、开源框架、统计组件、JavaScript库、Web服务器中间件、CDN特征等等。它会把一次请求拿到的内容同时丢给所有插件去匹配,而不是像手动测试那样逐个猜测。
第二,它适合批量与自动化。一条命令可以直接接收一个URL列表文件,跑完输出结构化的日志,JSON格式可以直接喂给后续分析脚本或导入可视化平台。浏览器插件只能偶尔手动看一眼,根本扛不住“几百个站点”这种量级。
第三,它的插件机制足够开放。遇到特殊框架没识别出来,可以自己写一个几十行的Ruby插件往plugins目录一丢,下次再遇到就能自动识别。这个能力在应对自研系统和冷门中间件时非常重要。
当然它也有短板。它的识别主要基于HTTP响应内容、响应头、HTML特征这类信息,如果目标前端是纯自研页面、没有任何开源组件特征,那它能给出的结论就非常有限。另外它对JavaScript执行类指纹(比如前端渲染后才会出现的特征)基本无能为力,这类情况还是得靠浏览器插件或真实访问页面去补。我的原则是:WhatWeb做第一轮粗筛,浏览器和手工验证做第二轮精查,两者互补而不是互替。
2. 上手之前:安装方式与核心参数速览
2.1 三种安装路径
安装这个事,根据你手上的环境不同,我试过三种走法,都不复杂。
第一种是包管理器直接装。在Debian、Ubuntu一类的发行版上,只需要执行sudo apt install whatweb,装完直接whatweb -v看版本。我之前在测试环境里用的就是这么装的,依赖自动解决,非常省心。如果不是这类系统,可以看看仓库里有没有对应的包,优先用包管理器,省去Ruby环境折腾。
第二种是源码方式。从官方源码仓库把项目克隆下来,进入目录后执行bundle install安装依赖,然后用./whatweb直接跑。这种方式的优势是能拿到最新版插件和功能,适合希望跟着社区更新节奏走的人。我在需要用到新插件时也会切到源码版。注意Ruby版本不要太老,建议2.7以上,不然有些依赖会编译失败。
第三种是直接用虚拟机和预装环境。现在主流的渗透测试发行版里基本都预装了WhatWeb,如果你用的是这类系统,进终端直接敲命令就行。但预装版本的插件数量可能比源码版少一些,因为发行版打包有版本滞后。建议先跑一下whatweb -l | wc -l看看插件总数,再决定要不要升级到源码版。
装完之后先别急着扫,用whatweb --version确认一下版本号和插件目录路径。不同版本在参数细节上有一点差异,比如早期版本对--log-json参数支持得不够好,如果你在旧版本上想用新功能,优先考虑源码更新。
2.2 常用参数分组记忆
WhatWeb的参数看着多,但按用途分组之后很好记。我按自己的使用频率把它们分成五组:
- 基础控制组:
-v控制打印详细信息,-q安静模式只输出结果,--no-errors屏蔽错误信息,适合批量跑日志的时候用。 - 探测强度组:
-a后面跟0到4的数字,官方叫aggression level,我习惯叫探测力度。0是只做最基本的请求,4会尝试加载更多路径、尝试登录页探测等高消耗动作。日常用-a 3比较平衡,扫描内网资产可以用-a 1减少请求量。 - 请求定制组:
-U指定User-Agent,-H添加自定义Header,-c带Cookie。这三个参数在处理有访问限制的目标时几乎必用,后面实战部分我会给具体例子。 - 性能控制组:
--connect-timeout控制连接超时,--read-timeout控制读取超时,--max-threads控制并发线程数。批量扫描时这几个参数决定你是十分钟跑完还是跑一个小时。 - 插件与输出组:
-p指定只启用某些插件,-l列出所有可用插件,-o保存普通结果文件,--log-json保存成JSON格式,另外还有XML、YAML、SQL等日志格式可以选。
记忆这一坨不用死背,我的做法是挑几个顺手的长命令存到shell配置里当别名。比如我经常用的批量JSON输出组合:whatweb -q -a 3 --log-json results.json -i targets.txt,这一条涵盖了大半需求。
3. 实战操作:从单点探测到批量资产梳理
3.1 第一次扫描:默认模式
先从一个最基本的命令开始。假设我要对一个业务系统做评估,目标地址写成一个保留域名的形式做演示:
whatweb http://scanme.example.com这个命令会做一次默认级别的扫描,输出大概长这样(具体字段因版本而异):
| WebSite: http://scanme.example.com | Status: 200 | Absolute URL: http://scanme.example.com | Title: 某业务系统-登录 | IP: 203.0.113.10 | HTTPServer: Apache/2.4.54 | PoweredBy: PHP/7.4.30 | PHP: 7.4.30 | Apache: 2.4.54 | HTML5这条输出已经能告诉你很多信息:Web服务器是Apache且版本为2.4.54,后端是PHP 7.4.30,页面标题暗示这是一个业务系统的登录页。有了这些基础信息,后续可以进一步猜测:Apache 2.4.54配PHP 7.4.30,这个组合在不少老项目里很常见,顺手可以查查这个PHP版本有没有已知的安全补丁滞后问题。
默认模式是比较保守的,只对首页发起少量请求,速度很快,一般一个站点两三秒内就出结果。对于首次接触的未知资产,我建议先用默认模式快速看一眼,而不是一上来就开高强度扫描,原因很简单:你不知道目标会不会有什么奇怪的防护策略,先低压力试探一下更稳妥。
如果你不知道目标到底是HTTP还是HTTPS,直接写https://开头的地址就好,WhatWeb会按照给定的URL访问。如果写的是裸域名,它默认会尝试HTTP,然后在遇到重定向时跟随跳转。也可以直接用-u参数只扫URL路径,不过日常用法基本都是直接丢完整URL。
3.2 用探测力度与自定义请求控制行为
当默认模式给的信息不够,或者目标表面看不到什么特征时,就该加大探测力度了。这个参数直接改成-a 4试试:
whatweb -a 4 http://scanme.example.com力度开到4之后,WhatWeb会尝试访问更多的路径,比如常见的管理后台路径、配置文件路径、特殊目录等,相当于对目标站点做了几十次轻量级请求,以便让更多插件命中特征。我做过一次对比,同一个站点用-a 0只能识别出服务器类型,用-a 4能额外识别出背后的CMS、站点地图、甚至登录页所在的框架类型,差异非常明显。
但高力度不是免费的,它会让请求数量成倍增加,对目标来说也算是一种负载。我踩过的坑是拿-a 4去扫一个很老的业务系统,结果对面直接返回503拒绝服务,后来才知道那个系统本身性能就不好,每个请求处理得慢,并发一高就扛不住了。所以我的建议是:外部测试先用-a 3,内网或明确授权的环境再用-a 4,一直很稳。
自定义请求这件事在实战里更是常态。有些站点了访问终端,默认UA(通常是Ruby的默认UA)会被拦,这时候要换一个浏览器UA:
whatweb -U "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36" http://scanme.example.com还有的站点需要带特定的Header才能正常响应,比如某些反代理环境要求X-Forwarded-Host字段,这种情况用-H一个一个加:
whatweb -H "X-Forwarded-Host: scanme.example.com" -H "Accept-Language: zh-CN,zh;q=0.9" http://scanme.example.com另外,有的登录后才完整的站,可以先手动登录拿Cookie,再把Cookie带进去扫描,比如-c "sessionid=abc123; other=def"。这个功能很实用,因为很多指纹特征在登录后的页面才会暴露。
3.3 插件定向检测与输出格式管理
有时候我们并不想做全量匹配,而是想快速确认某个特定组件是否存在,比如怀疑目标用了某个特定版本的Wiki系统,那就可以只看对应插件的结果。WhatWeb支持用-p指定插件名进行定向检测:
whatweb -p "WordPress" http://scanme.example.com/blog这个用法在确认漏洞影响范围时特别好用。比如某个CMS被曝出漏洞,你想快速排查公司内网哪些站点中招,只需要写一个目标列表,然后指定该CMS的插件去批量扫描,结果会非常干净,输出里只保留你关心的结论,不会有一大堆无关的JS库、统计代码之类的噪音。
想查看自己手上的插件库有哪些能力,用:
whatweb -l这个命令会列出全部插件名称。配合grep可以快速筛选,比如:
whatweb -l | grep -i forum在需要同时检测多个组件时,插件名用逗号分隔,例如-p "WordPress,jQuery,MySQL"。如果你的扫描只想检查特定插件、又希望输出结果包含细节,可以配合-v参数,看到的输出会冗长一些但很有价值,尤其是定位“到底是什么特征让插件命中”的时候。
输出格式这块,我强烈建议正式扫描一定用结构化日志。命令行屏幕上的输出适合人看,但事后归档、统计、做报告都很难用。以下命令可以同时保存简单文本和JSON日志:
whatweb -q -a 3 --log-json scan_result.json -o scan_summary.txt http://scanme.example.com-o保存的是给人看的一行一个站点的汇总文本,--log-json保存的是包含每个插件命中细节的完整JSON。我通常会两个都留,做汇报时用文本版,做数据分析用JSON版。
3.4 批量扫描与结果汇总
批量场景才是WhatWeb真正的主场。做法就是准备一个目标列表文件,每行一个URL,然后一次性交给WhatWeb:
whatweb -i targets.txt --log-json batch.json --log-xml batch.xml -q -a 2 --max-threads 20简单解释一下:-i指定输入文件,--max-threads控制并发。这里我特意把并发控制在20,不是拍脑袋写的——过高并发容易触发目标限流,而且本机打开的socket数、连接超时处理都会成为瓶颈,实测20左右在性能和礼貌之间比较平衡。如果目标是内网且确认抗压,可以调到50,但对外部资产务必克制。
批量跑完后的JSON解析也是很爽的操作。我常写一个小脚本,把JSON里的IP、HTTPServer、Title、匹配到的插件名提取出来,形成一个汇总表,一眼看出哪些站点还在跑老版本Apache、哪些站点标题命名风格不一致可能已经无人维护。这种数据给运维团队看,比几十页扫描报告直观多了。
批量的另一个小技巧是先跑一遍-a 1的快速粗筛,把明显有问题的站点筛选出来,再针对可疑目标精扫。这样做的好处是资源利用效率更高,也避免高强度扫描在一堆无关目标上浪费大量时间。
4. 看穿输出结果:字段含义与插件机制解读
4.1 一条扫描结果的完整解剖
很多人拿到WhatWeb输出就只看个标题,其实每一行信息都有用法。我用实际经验把核心字段拆开讲。
首先是Status,就是HTTP状态码。200说明正常响应,301/302说明目标有跳转,需要看跳转到哪,因为跳转后的页面才是真正承载业务的地方;401/403说明有鉴权或访问控制,这时候指纹信息可能受限,得考虑带Cookie重扫。
其次是HTTPServer和PoweredBy,这两个字段直接暴露中间件和后端框架。比如HTTPServer: nginx/1.18.0,后面可以顺带查查这个版本在当前环境下的安全性;PoweredBy: ASP.NET则说明后端是Windows系技术栈。它们之间有时候是冲突的,比如前面说Apache后面又出现IIS特征,那大概率经过了反向代理或负载均衡。
然后是Title,页面标题。别小看这行字,很多站点的标题里直接写系统名称和版本号,比如“XX系统 v2.3.1”,这就是一条免费的情报。标题还能帮我判断目标性质,比如标题是纯英文乱码而页面内容是中文,大概率背后有模板渲染或前端框架,反而说明站点可能不是传统CMS。
最后是各个插件命中的字段。比如出现WordPress: 6.2.0、jQuery: 3.x这样的行,代表对应插件匹配到了特征。注意这里的插件字段名和值是“这个插件认为它发现了什么”,不一定百分之百准确。比如jQuery插件的3.x可能只是通过源码里的wp-includes路径推断的,实际版本未必是3.x。任何指纹工具的结论都只能当线索,不能当证据。
下面是我常用字段的速查表:
| 字段 | 含义 | 应用建议 |
|---|---|---|
| Status | HTTP状态码 | 判断站点存活与访问限制 |
| Absolute URL | 最终解析后的URL | 观察跳转后的真实路径 |
| IP | 解析到的IP地址 | 资产梳理时记录归属 |
| HTTPServer | Web服务器类型与版本 | 判断中间件及补丁状态 |
| PoweredBy | 后端框架提示 | 决定后续漏洞排查方向 |
| Title | 页面标题 | 快速判断站点名称与版本信息 |
| 插件命名字段 | 匹配到的组件指纹 | 作为技术栈结论的输入,需复核 |
| Cookies | 服务端返回的Cookie名 | 判断后端语言与认证机制 |
4.2 插件机制与自定义插件入门
插件机制是WhatWeb的灵魂。它本质上就是一个“特征库+匹配引擎”:每个插件定义若干条匹配规则,引擎把目标响应内容拿去逐条跑规则,跑中了就记录下来。默认的自带插件已经覆盖了绝大多数公开组件,但当你遇到一个内部自研框架、一个极少见的国产CMS时,内置插件往往匹配不到任何东西。这时候就该自己写插件了。
WhatWeb插件是Ruby脚本,结构非常清晰。下面是一个最小可用的例子,功能是识别一个我在模拟项目中定义的页面标记>Plugin.define "CustomMarkDemo" do author "me" version "1.0" description "Detects the>