简介:FofaViewer(佛法)是一款面向网络安全研究与渗透测试人员的批量搜索爬虫工具,基于FOFA网络空间搜索引擎实现资产快速定位与数据抓取。压缩包内共2个文件:fofaviewer.jar为主程序,可直接运行,负责批量搜索、条件筛选、结果排序以及CSV/Excel导出;config.properties用于配置API Key、搜索参数和输出行为,便于按需调整。资源包约32.69MB,压缩包整体轻量、易部署,覆盖常见JDK环境,适合从事网络资产梳理、漏洞挖掘与风险评估的中高级安全工程师。当前已有2015人学习下载。借助该工具,使用者可通过关键字、域名、IP范围、服务类型等条件组合完成大规模网络资产检索,利用定时任务持续跟踪资产变化,把FOFA的海量数据转化为可分析、可落地的安全情报,实现自动化闭环,提高日常渗透测试与安全运营效率。
1. 为什么做 FOFA 批量搜索时,大家都会装一个 FofaViewer
做资产梳理和告警排查时大家基本绕不开 FOFA,而 FofaViewer 是 FOFA 生态里最常见的开源客户端,圈子里喜欢把它连读成“佛法”,所以就有了“FofaViewer 佛法批量搜索爬虫工具”这个说法。先讲个反直觉的场景:FOFA 网页版单条查询很快,可真要把几万条结果导出来继续做关联分析时,网页端反而帮不上忙,导出数量受限、字段截断,手动翻页翻到怀疑人生。FofaViewer 解决的就是这个断层:先批量搜索,再把结果导成结构化文件,最后配一段 Python 脚本做二次请求,把目标服务里的关键信息批量拉下来。适合谁用?做资产测绘、攻防对抗前信息收集、互联网资产暴露面梳理的从业者,都能在这里面拿到一套能直接复用的干活流程。
2. 从 FOFA 到 FofaViewer:网络空间测绘的爬虫原理与选型理由
2.1 FOFA 的数据从哪来:先搞清楚你在爬什么
很多人把 FOFA 当成一个普通的“搜索框”,输入 app="Apache-Shiro" 就直接看结果。但理解网络爬虫原理的人都清楚,FOFA 本质是一个持续运行的抓取系统,它在全网范围内主动发起连接,探测 IP 在 80、443、22、3306 等端口上开放的服务,再结合 TLS 证书、响应头、页面正文等特征给资产打标签。你输入查询语句时,其实是在查询 FOFA 已经建好的资产指纹库,而不是实时爬全网。
这一点直接决定了 FofaViewer 这类工具的定位:它不是一个“爬虫程序”,而是一个“爬虫结果的拾取客户端”。查询速度非常快,是因为数据已经被 FOFA 侧提前抓完、存好、建了索引。你要做的是把命中结果里的 host、ip、port、protocol、title 这些字段取出来,再决定要不要对这些目标发起自己的 Python 请求。
明白这一点,你就能想清楚一件事:用 FofaViewer 批量搜索时,真正消耗的是 API 配额,而不是网络带宽。配额用超了,导出会失败;而当你拿着导出的清单去做二次请求时,才进入 requests 爬虫的范畴,要被目标站点限速和封 IP。
2.2 FofaViewer 真正的价值:把查询、缓存、导出拆开
FofaViewer 这类 GUI 客户端的好处在于,它把“查询”和“导出”之间的操作成本降到最低。网页版 FOFA 虽然也支持导出,但免费档位和低等级会员档位通常只给很少的导出数量,而且字段选择、翻页策略、请求间隔都不好控制。用 FofaViewer 时,你在图形界面里输入查询语句,点一下查询,结果会分页加载到本地表格里,之后通过导出功能把当前结果集落盘。
我一般会建议团队把 FofaViewer 当成“离线查询终端”来用:在一次授权的排查任务里,把若干条查询语句的命中结果全部导出存好,后续分析不再重复消耗 FOFA API。这样有几个实际好处:第一,每次查询都有本地留痕,日后追溯取证不用重新调 API;第二,导出的 CSV 或 JSON 可以直接被 Python 脚本消费,形成稳定的处理管道;第三,FofaViewer 内置了查询语法补全和历史记录,省去你反复查官方语法文档的时间。
当然,它也不是万能的。FofaViewer 本身下载、展示、导出这些事做得不错,但如果你需要自动化定时跑查询,GUI 客户端就不适合了,那应该直接调 FOFA API。所以多数人的落地组合是:FofaViewer 做人工探索和快速导出,Python 脚本做批量处理和定时巡检,两层接起来才叫真正的“批量搜索爬虫工具”。
2.3 直连 FOFA API 的最小验证:requests 爬虫入门脚本
不管你有没有装 FofaViewer,先跑通 FOFA API 是最稳妥的第一步。因为 FofaViewer 的登录本质上也是替你保管账号和 API Key,然后替你去请求 FOFA 服务端。下面这段 Python 脚本就是最精简的 API 验证方式,也适合拿来理解 FOFA 的查询参数结构:
import requests import base64 import json # 在 FOFA 个人中心获取,不要硬编码在分享出去的脚本里 email = "your_account@example.com" api_key = "your_api_key_here" # 查询语法,先用一条最简单的语句验证连通性 query = 'app="Apache-Shiro"' # FOFA API 要求 query 先做 base64 编码,再进行 URL 传输 qbase64 = base64.b64encode(query.encode("utf-8")).decode("utf-8") params = { "email": email, "key": api_key, "qbase64": qbase64, "size": 10, "fields": "host,ip,port,protocol,title,server,cert,domain", } resp = requests.get( "https://fofa.info/api/v1/search/all", params=params, timeout=30, ) data = resp.json() # error 为 true 时说明认证失败或配额异常,errmsg 有具体原因 if data.get("error"): print("API 报错:", data.get("errmsg")) else: print("命中总数:", data.get("size")) for row in data.get("results", []): print(row)这段代码的逻辑很简单:把查询语句转成 base64,带上账号和 API Key,声明要返回的字段,然后发 GET 请求。这里的参数需要逐一说明:email 和 key 是 FOFA 侧的认证凭据,对应你在个人中心申请到的 API Key;qbase64 的值必须是查询语句的 base64 编码,而不是明文,否则服务端会拒绝解析;size 控制返回条数,验证时不要填 10000 去浪费配额;fields 决定返回字段顺序,你填什么它就按顺序返回什么,FOFA 不会主动帮你补字段。
确认 API 能通之后,你再去用 FofaViewer,就能理解它的内部行为了:它帮你自动完成 email、key 的携带,帮你把查询语句做 base64 编码,然后把结果解析到表格里。后面所有基于 Python 的批量处理,本质都是把这份代码里的参数换掉、把返回结果接上你自己的业务逻辑。
3. 部署 FofaViewer 与第一次登录:Java 环境、API 密钥和连接配置
3.1 部署需要的最小环境:JDK 和启动命令
FofaViewer 是 Java 图形界面程序,所以部署的最小环境是 JDK 或 JRE 8 以上。很多新手拿到压缩包后先找 exe 双击,结果提示“未能启动 Java Application”,其实问题出在系统里根本没有装 Java 运行时。先打开终端验证一下环境:
# 检查 Java 环境 java -version # 如果输出里能看到 Java HotSpot 或 OpenJDK 版本信息,说明环境可用在 Windows 上,如果 java -version 提示“不是内部或外部命令”,说明 JAVA_HOME 没有配置,或者 JDK 没有装上。常见做法是安装 JDK 8 或 JDK 11,然后在系统环境变量里设置 JAVA_HOME 指向 JDK 安装目录,再把 %JAVA_HOME%\bin 加到 Path。这里有一个实际踩坑点:JDK 17 以上可能因为模块化限制导致启动后窗口空白或按钮错位,所以做资产梳理的工具环境,我一般保守装 JDK 11。
装好 Java 后,解压 FofaViewer 的发行包,找到以 .jar 结尾的主程序文件,在命令行里进入该目录执行:
# 启动 FofaViewer 主界面 java -jar FofaViewer.jar如果是 Linux 服务器或者没有图形界面的环境,也可以用 Xvfb 等方式虚拟显示运行,但那样操作起来很别扭。更推荐的做法是:在本机跑 FofaViewer 完成人工查询和导出,在服务器上跑 Python 脚本消费数据。GUI 工具和批处理脚本各干各的,反而比全部塞进服务器更顺手。
3.2 API 密钥与 FofaViewer 的配置项
FofaViewer 首次启动后,一般会有一个设置或登录面板,需要填写 FOFA 的邮箱和 API Key。填完之后,工具会校验身份,并把 Key 存储在本地配置里,之后所有查询请求都由工具自动带上认证信息。这里要特别提醒:API Key 是你的账号凭证,不要把它提交到 Git 仓库、不要写进分享出去的 Python 脚本里。我见过有人直接把 Key 放在 FofaViewer 同目录的配置文件中,然后把整个目录打成压缩包发到群里,结果账号被刷爆配额。
在工具配置里,除了账号信息,比较好的习惯是设置默认查询大小和导出模板。比如默认每页 100 条、导出字段固定为 host,ip,port,protocol,title,server,cert,domain,这样每次导出的文件结构都一样,后续脚本解析更省事。参数的命名在不同版本上略有差别,但核心无非是“每页条数”“超时时间”“导出字段顺序”这几项,按自己的使用频度调一次就基本不用动了。
FofaViewer 并不是一个黑匣子:你点“查询”时,它底层就是发了一个类似 2.3 节那样的 HTTP 请求。如果遇到查询报错,优先检查三个方面:第一,账号是否欠费或配额超限;第二,查询语句里的引号是否成对、关键字是否写错;第三,系统时间是否准确。曾经遇到过一个很隐蔽的问题:本机时间慢了五分钟,导致签名类认证失败,FofaViewer 一直提示连接异常。
3.3 用脚本验证认证有效,再回填到工具
如果你有多个 FOFA 账号,或是不确定当前 Key 是否还有效,不要直接去 FofaViewer 里反复试,那样既慢又容易被服务端判定异常。用下面这段脚本先做一次最小验证,输出明确结果后再回填配置:
import requests import base64 def check_fofa_key(email: str, api_key: str) -> bool: """用一条最小查询验证 API Key 是否有效,避免消耗过多配额。""" query = 'ip="1.1.1.1"' qbase64 = base64.b64encode(query.encode("utf-8")).decode("utf-8") params = { "email": email, "key": api_key, "qbase64": qbase64, "size": 1, "fields": "host", } resp = requests.get( "https://fofa.info/api/v1/search/all", params=params, timeout=15, ) body = resp.json() if body.get("error"): print("Key 不可用:", body.get("errmsg")) return False print("Key 有效,查询成功") return True # 示例调用 check_fofa_key("your_account@example.com", "your_api_key_here")这里用 ip="1.1.1.1" 做探测,是因为它一定能查询到结果,且命中数量极小,不会浪费配额。脚本的逻辑是先判断返回体中的 error 标志位,再决定是否需要进一步处理。若返回 errmsg 为“Authentication failed”之类,说明账号密码或 Key 不匹配;若提示“Query rate limit exceeded”,说明配额被限,需要等一段时间再试。
我把这个脚本存在本地,换任何账号或 Key 时先跑一遍,确认有效再填进 FofaViewer。这个习惯能帮你把认证层面的问题和业务层面的问题迅速隔离开,不至于在工具里排查半天才发现是 Key 本身失效了。
4. 批量搜索与导出:查询语法、字段选择和爬取清单生成
4.1 查询语法:先会用两条最常用的语句
FofaViewer 的查询框和 FOFA 网页版的语法是一致的,核心逻辑是“协议 + 端口 + 指纹 + 逻辑关系”的组合。最基础的用法是关键字直接查询,比如输入 app="Nginx",会返回 FOFA 指纹库中识别为 Nginx 服务的资产;输入 port="8080",会返回所有开放 8080 端口的资产。实际排查中,我更频繁使用的是组合查询,比如把组件特征和业务关键字一起限定:
app="若依" && status_code="200" && country="CN"这条查询的意思是:关联“若依”这个应用指纹,且 HTTP 状态码为 200,且地理位置在中国。批量搜索时,条件限定得越严格,导出数据的质量越高,后续二次请求的命中率也越高。另一条非常实用的是排除语句:
title!="403 Forbidden" && port!="22"它用感叹号表示“不等于”,可以快速滤掉明显无价值的资产。FOFA 语法支持 &&(与)、||(或)、!(非)三种逻辑,配合引号包裹的精确匹配,足够覆盖 95% 的资产排查需求。不要一上来就写很长的嵌套语句,先在 FofaViewer 里用小范围条件预览结果数量,再逐步收紧条件,这是减少无效导出的最好办法。
4.2 导出字段选配:FOFA 返回的关键字段
FOFA 查询结果里字段很多,但做批量爬虫时常用的就是那么几个。字段选配直接影响导出文件大小和后续解析成本,所以我在 FofaViewer 里固定了一套字段模板,整理如下:
| 字段名 | 含义 | 用于二次请求时的作用 |
|---|---|---|
| host | 服务主机名或域名 | 拼接 URL 的主机部分 |
| ip | 资产 IP 地址 | 定位源 IP,做归并与反查 |
| port | 开放端口 | 服务端口,决定协议拼接 |
| protocol | 服务协议 | 判断该资产是否走 http/https |
| title | 页面标题 | 快速判断页面内容归属 |
| server | 响应头里的 Server 字段 | 识别 Web 服务中间件 |
| cert | TLS 证书信息 | 识别自签名证书等高危资产 |
| domain | 所属域名 | 做资产归属归类 |
| status_code | HTTP 状态码 | 过滤死链和不可访问的资产 |
这份字段配置的逻辑是:保留足够多的定位信息,又不携带 FOFA 内部的附加标签字段。导出后,脚本按 protocol 过滤出 http 和 https 服务,再按 host 和 port 拼接出可直接请求的 URL。拼接规则是:如果 host 已经包含协议前缀,直接用;如果没有,就按 protocol://host:port 组装。很多初写脚本的人在这一步翻车,就是没考虑到 host 字段在不同场景下可能是域名、可能是 IP:端口、也可能已经带了 scheme。
4.3 用 FofaViewer 导出 JSON 后生成可请求清单
导出这一步,FofaViewer 通常会让你选择导出格式,常见的有 CSV、JSON 和纯文本表格。我比较推荐 JSON,因为它保留了字段名,解析不容易错位。导出后,把文件放到脚本目录,用下面的代码生成可继续请求的 URL 清单:
import json # FofaViewer 导出文件的路径,按实际文件名调整 with open("fofa_export.json", "r", encoding="utf-8") as f: raw = json.load(f) # 兼容两种常见结构:纯数组 或 {results: [...]} if isinstance(raw, list): rows = raw else: rows = raw.get("results", []) web_assets = [] for row in rows: # 字段名以导出时配置为准,这里兼容常见命名 protocol = str(row.get("protocol", "")).lower() if protocol not in ("http", "https"): continue host = str(row.get("host", "")).strip() if not host: continue # 如果 host 没带协议前缀,就用 protocol 和 port 拼一个完整 URL if not host.startswith(("http://", "https://")): port = row.get("port", "") if port: host = f"{protocol}://{host}:{port}" else: host = f"{protocol}://{host}" web_assets.append({ "url": host, "ip": row.get("ip", ""), "port": row.get("port", ""), "title": row.get("title", ""), "server": row.get("server", ""), }) print("有效 Web 资产数量:", len(web_assets)) for asset in web_assets[:5]: print(asset)这段代码做的事很简单:读文件、取结果数组、按 protocol 过滤、拼 URL。需要注意的一点是,FofaViewer 导出的 JSON 结构可能随版本略有不同,有的是纯数组,有的是带 total 和 results 的对象。所以脚本里做了两层兼容,先判断顶层类型再取值。输出前三行用于确认解析正确,确认无误后再进入批量请求阶段。
4.4 给清单加请求头与限速:把 requests 爬虫跑稳
有了 URL 清单,接下来的动作才是真正的请求爬虫。下面这段代码基于 Python requests,给请求加了浏览器请求头、关闭证书校验、设置超时,并在每次请求之间随机等待,避免把目标站点和服务端同时惹毛:
import requests import time import random # 使用 Session 复用连接,避免每次握手都重新建立 session = requests.Session() session.headers.update({ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", }) # verify=False 是应对自签名证书的临时手段,正式环境建议改为指定 CA requests.packages.urllib3.disable_warnings() def fetch_title(url: str, timeout: int = 10) -> str: """请求一个 URL,返回页面标题,失败返回空字符串。""" try: resp = session.get(url, timeout=timeout, verify=False) if resp.status_code != 200: return "" # 简单抽取 title 标签,不引入额外 HTML 解析库 text = resp.text if "<title>" in text.lower(): title = text.lower().split("<title>")[1].split("</title>")[0] return title.strip()[:200] return "" except requests.RequestException: return "" # 示例:只处理前 100 条,实际使用时按配额和需求调整 for asset in web_assets[:100]: title = fetch_title(asset["url"]) if title: asset["real_title"] = title print(asset["url"], "->", title) # 随机等待 0.5 到 1.5 秒,降低被限速的概率 time.sleep(random.uniform(0.5, 1.5))这里面的参数值得展开讲:User-Agent 是请求身份标识,很多目标站点的 Java Controller 层会针对常见爬虫 UA 做防护,识别到 requests 默认的 python-requests 标识后直接返 403,所以手动改成浏览器 UA 是第一步;timeout 参数控制单请求最长时间,避免某个死 IP 把整个任务卡死;verify=False 是为应对目标站点证书链不完整的问题,但在正式环境里如果有条件,还是把目标站点的证书链配好,关闭校验证书始终是下策。随机等待的作用是打散请求节奏,规避简单的频率检测。
如果是几千条资产的大清单,单进程跑完要很久,可以考虑按分布式爬虫的思路拆分任务:把 URL 列表切成多个分片,放到多个进程里各自消费,每个进程控制自己的并发数和等待时间。但这里有个边界:FOFA 侧限制的是 API 调用频率,目标站点限制的是单 IP 请求频率。多进程能提高吞吐,却不能突破单 IP 的限速瓶颈,所以我的经验是优先用单进程限速跑,只有面对大量不同目标站点时才做并发拆分。
5. FofaViewer 批量爬虫的避坑清单:导出上限、请求频率和字段误用
5.1 导出数据量一大,文件缺失或解析为空
现象:在 FofaViewer 里明明看到几万条结果,导出到 JSON 后,脚本一读只有几十条,甚至文件内容为空。
原因:FofaViewer 的导出会受账号 API 配额限制。FOFA 对不同等级账号有 strict 的数量限制,不是界面显示多少就能导出多少。另外,导出较大结果集时,服务端可能返回分页数据,导出功能在某些版本里只处理了当前页,导致文件只有当前页内容。
解决:避免一次性导出超大结果集。先在查询语句里用 country、region、port 等维度做切分,把一批大结果拆成多个小批次导出。比如把全量查询拆成“按省份逐个查询”,每个省份单独导出一份 JSON,再用脚本合并。这个动作虽然琐碎,但能明显减少半路失败的概率。
5.2 查询语句合法,但 API 提示错误或超时
现象:同样的查询在 FofaViewer 查询框里能出结果,换成 Python 脚本请求 API 却报错,或者长时间卡住不返回。
原因:问题往往出在 base64 编码上。FOFA API 要求 query 做 base64 编码,但标准 base64 结果里可能包含 + 和 / 字符,在 URL 传输时会被解释成空格或路径分隔符,导致服务端解码失败。另一个隐蔽原因是查询语句里包含中文指纹,例如 app="若依",如果编码前没有确保 UTF-8 编码,转出来的 base64 就是错的。
解决:统一使用 base64.urlsafe_b64encode 编码,它会把 + 替换成 -,把 / 替换成 _,从根源上规避 URL 转义问题。另外在拼接请求时,建议把整个 params 字典交给 requests 处理,不要手动拼接 URL 字符串,requests 会自动处理参数编码。
import base64 query = 'app="若依" && country="CN"' # urlsafe 编码,避免 + 和 / 进入 URL 后出问题 qbase64 = base64.urlsafe_b64encode(query.encode("utf-8")).decode("utf-8") print(qbase64)5.3 二次请求时访问了一堆非 Web 资产
现象:拿着清单里的 host 去请求,一半以上超时,或者返回一堆证书错误,仔细一看这些资产是 SSH、FTP、Redis 之类的非 Web 服务。
原因:FOFA 查询的是开放端口和服务,如果你的查询条件没有限定 protocol,结果里自然混入非 HTTP 资产。host 字段可能是纯 IP,也可能是 IP:端口,端口不是 80/443 时,直接按 http 协议去请求当然会失败。
解决:在生成 URL 清单前,严格过滤 protocol 字段。只保留 protocol 为 http 或 https 的记录,再检查 port 是否为常规 Web 端口。更稳妥的做法是先用 status_code 字段过滤出曾经返回过 HTTP 状态码的资产,相当于用 FOFA 的历史探测结果帮你筛掉一批死资产。脚本里加一行判断,能省下大量无效请求的时间。
5.4 目标站点 Java 后端对爬虫做了防护,返回 403
现象:requests 请求拿到的全是 403 或 302,而浏览器打开同样的页面正常。
原因:目标站点的 Java Controller 层做了反爬防护,识别到非浏览器 UA、高频访问或缺少关键请求头后拒绝响应。常见的防护包括 UA 黑名单、频率限制、CSRF Token 校验和验证码。
解决:先升级请求头,模拟完整浏览器环境,包括 User-Agent、Accept、Accept-Language、Referer。如果仍然 403,把请求频率进一步调低,并增加重试逻辑:第一次失败后等待 3 秒再重试,连续失败 3 次就放弃当前目标,不要死磕。代码实现时,把 5.4 节 fetch_title 里的异常处理改成带上重试计数的版本,效果更好。
5.5 FofaViewer 启动失败或运行时界面卡死
现象:双击启动脚本后没有窗口,或者界面打开后点击查询就无响应。
原因:多数是 Java 版本不兼容引起的。JDK 版本过高可能导致 Swing/AWT 组件加载异常;也可能是系统 PATH 中同时存在多个 Java 版本,工具加载了错误版本。
解决:在命令行窗口里手动运行 java -jar FofaViewer.jar,看控制台输出的异常日志。如果提示找不到类或版本不支持,就切换到 JDK 11 再试。稳定运行后,不要频繁切换 Java 版本,避免 GUI 界面出现字体混乱、按钮失效之类的玄学问题。每次升级工具前,先备份本地导出文件和配置文件,再做替换,后悔药还是备着好。
6. 把批量搜索变成巡检:结果去重与增量发现脚本
批量搜索最大的价值不是跑一次,而是持续跑。FOFA 的资产面是动态变化的,今天没开放的端口明天可能就暴露出来。所以我会在每次 FofaViewer 导出后,把结果落进一个本地 JSONL 基线文件,下次导出后再跑一次对比,只输出新增资产。这个增量发现脚本是整套流程里回报最高的部分:
import json import hashlib import pathlib import datetime BASE_LINE = "baseline.jsonl" def asset_fingerprint(asset: dict) -> str: """给资产生成唯一指纹,使用 protocol + host + ip + port。""" raw = f"{asset.get('protocol')}://{asset.get('host')}|{asset.get('ip')}|{asset.get('port')}" return hashlib.sha1(raw.encode("utf-8")).hexdigest() # 读取历史基线 known = {} if pathlib.Path(BASE_LINE).exists(): with open(BASE_LINE, "r", encoding="utf-8") as f: for line in f: line = line.strip() if not line: continue item = json.loads(line) known[item["fp"]] = item # web_assets 来自第四节的导出解析结果,这里直接复用 new_assets = [] for asset in web_assets: fp = asset_fingerprint(asset) if fp not in known: asset["fp"] = fp asset["first_seen"] = datetime.date.today().isoformat() new_assets.append(asset) print("新增资产:", asset.get("host")) # 追加写入基线,保留历史出现过的资产 with open(BASE_LINE, "a", encoding="utf-8") as f: for asset in new_assets: f.write(json.dumps(asset, ensure_ascii=False) + "\n") print(f"本次新增 {len(new_assets)} 条,基线累计 {len(known) + len(new_assets)} 条")这段脚本的核心是 asset_fingerprint 函数:用协议、主机、IP、端口四个维度拼出一串唯一标识,只要这四者有一个变化,就视为新资产。指纹用 SHA1 而不是直接拼接文本,是为了避免极端情况下特殊字符影响判断。基线文件按行追加,天然支持后续去重——下次再看到相同指纹,直接跳过;就算资产已被下线,它仍然留在历史基线里,供追溯使用。
真正操作时,我会把 FofaViewer 的查询条件写进一个文本文件,每周固定跑一次:先人工在 GUI 里查、导出,再执行 4.3 节的清单生成脚本,最后跑本节增量脚本,把新增资产的标题和 Server 字段打出来看一眼。这样每次巡检只需要十几分钟,但能及时发现新暴露的端口和新增组件。过去我习惯一次性导出所有结果然后拼命请求,结果既被限速又拿不到有效数据,后来改成“小批次查询、限速请求、增量对比”的方式,效率反而翻了一倍。这套方法不见得适合所有场景,但只要你在用 FofaViewer 做批量搜索,我建议至少把增量对比这一步加上,希望帮到你。
本文还有配套的精品资源,点击获取