简介:FofaViewer(又称“佛法”)是一款基于FOFA搜索引擎的批量搜索爬虫工具,专为网络安全研究人员、渗透测试工程师及资产测绘人员设计,可快速定位和梳理互联网公开资产,辅助漏洞挖掘、攻击面分析与风险评估。其依托FOFA庞大的网络资产数据库,通过图形化界面实现高效数据抓取。资源为跨平台Java工具包,共2个文件:fofaviewer.jar为可执行主程序,内置友好界面;config.properties为配置文件,用于填写FOFA API Key、调节搜索限制与输出设置;压缩包整体仅32.69MB,体积小巧,下载后即可运行。工具支持按关键字、域名、IP范围、协议类型等条件进行批量检索,结果可导出为CSV或Excel,便于后续数据处理;同时支持定时搜索任务,帮助持续监控资产变化。通过简单配置API凭证,即可在界面中完成搜索、筛选、排序与导出全流程。已有2015人学习下载,适合需要高效开展网络资产发现、批量数据抓取与安全分析的专业人员。
1. 别再手动翻页点收藏,批量网络资产搜索该交给 FofaViewer
很多安全工程师查 FOFA 还停在浏览器里翻页,搜完复制粘贴,重复劳动全耗在点“下一页”上。FofaViewer 把 FOFA 的查询能力搬到了本地 GUI,配合一份config.properties就能把批量查询、翻页、导出做成一次可重复的工程动作。我推荐它的理由不是省鼠标,而是它让“整理一批网络资产”这个行为从手动劳动变成了可留痕、可回放的流程。如果你在攻防演练前收集目标系统的暴露面,或是在日常监控里对比某类设备的上线情况,这个工具能覆盖从构思 query 到拿到 CSV 的完整链路。当然,前提是你在授权范围内使用它的数据结果。下面我从压缩包内部结构开始拆,一直讲到拿它做资产变更监控,中间会顺带把 FOFA 语法和几个配置参数一并讲透。
2. 解包 FofaViewer_1.1.11_JDK8:JDK8 适配与 config.properties 凭证链路
2.1 压缩包里有什么,为什么要用 JDK8
解压后是典型的免安装 Java 客户端,核心文件只有fofaviewer.jar和config.properties。没有注册表写入,也不依赖运行时安装,属于解压即用型。但这里有一个前置条件:必须用 JDK 8。FofaViewer 的界面层基于 JavaFX,而 JavaFX 从 JDK 11 开始被移出标准 JDK,直接丢给高版本解释器会报ClassNotFoundException: javafx.application.Application。所以别看到java -version显示 17 就去启动,老老实实准备一个 JDK 8u202 以上的版本。
# 指定 JDK8 路径启动,避免高版本 JavaFX 缺失导致闪退 /usr/lib/jvm/java-8-openjdk-amd64/bin/java -Xms512m -Xmx1g -jar fofaviewer.jar这段命令强制指定 JDK8 的完整路径,-Xms512m是堆内存初始值,-Xmx1g是最大堆。GUI 客户端不需要吃太多内存,1G 足够渲染几万行表格;真正耗内存的是你后端解析 CSV 的过程,工具本身只做请求和展示,堆调太大反而拖慢启动。如果你的机器多版本共存,务必写成绝对路径,否则系统 PATH 里那个新版本会把启动过程拦下来。
启动后主界面直接出来,不需要走登录弹窗,因为鉴权全部从本地配置文件读取。接下来要做的就是把 FOFA 账号凭证填进去。
2.2 配置 API 凭证:config.properties 的最小可跑形态
FOFA 的 API 鉴权需要两个字段:账号邮箱和 API Key。API Key 在 FOFA 官网个人中心里生成,会员等级对应单次查询条数上限。FofaViewer 把这些信息统一收在config.properties里。不同版本包的键名可能略有差异,但核心字段是固定的,下面这份是最小可跑示例:
fofa_email=yourname@example.com fofa_api_key=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx default_size=1000 page_max=5 thread_count=4 timeout_sec=10 export_format=csv result_fields=host,ip,port,protocol,title,domainfofa_email和fofa_api_key是请求签名的原材料,缺一个都会在查询时收到401 auth fail。default_size控制单次请求返回条数,FOFA 服务端有硬上限,设 1000 是比较稳的起点,想拉大之前先确认账号额度。page_max决定翻页次数,理论上总数据量等于default_size * page_max,但最终以账号权限和查询命中数为准。thread_count是并发线程数,不要设太高,FOFA 对单 IP 的 QPS 限制很严格,默认 4 足够。result_fields控制返回列的顺序,这直接决定导出后的列头结构。
配置完成后,建议不要一上来就做大范围搜索,先用title="test"这种窄条件试一次,看右侧结果数量是否正常变化。如果搜索按钮一直无响应,先确认fofa_email用的邮箱是否就是注册 FOFA 账号的那个邮箱,API Key 是否在个人中心重新生成过。很多老用户手里拿的还是旧版页面的 Key,已经过期但 GUI 只笼统报auth fail,不会告诉你具体哪个字段失效,只能两边同时替换一次。
下面这张表能帮你快速定位常见启动与查询报错:
| 报错特征 | 优先排查配置 | 调整方向 |
|---|---|---|
| 启动报 JavaFX 类缺失 | JDK 版本 | 换 JDK 8u202+,不要用 11/17 运行 |
查询返回401 auth fail | fofa_email / fofa_api_key | 检查 Key 是否多复制了空格或换行 |
| 每次只出 100 条 | default_size | 调大,确认账号额度 |
| 翻到第三页报错 | thread_count / timeout_sec | 降低线程数,增加超时时间 |
| 导出的 xlsx 打不开 | export_format | 改用 csv 或换 Excel 插件 |
2.3 让包里的 key 安全落地:本地配置的权限边界
config.properties是明文文件,密钥等于直接躺在磁盘上。这个包没有做混淆或加密,所以你拿到后第一步不是改参数,而是收权限。Linux 和 macOS 上执行chmod 600 config.properties,Windows 上放到当前用户目录并取消继承权限。这一步的意义在于 jar 包不内嵌任何敏感信息,它在运行时现场读文件,谁读得到文件谁就能用你的 FOFA 余额。常看见有人图方便把配置连同结果一起传网盘,这是把数据泄露写成必然事件。正确姿势是配置文件留在本机,工作目录里只保留 jar 和导出结果。
到这里你已经具备跑通链路的基础:JDK8 启动、配置鉴权、点击搜索。但让批量搜索真正高效的关键在查询语法,FofaViewer 能发挥多少价值,完全取决于你输入的 query 表达得准不准。
3. 批量搜索不是粘贴关键字:FOFA 查询语法与 FofaViewer 执行策略
3.1 FOFA 的字段运算符是批量搜索的命门
FofaViewer 只是个 GUI 壳,真正决定数据质量的是 FOFA 查询表达式。很多人把网页版title="后台"直接粘进来,以为逻辑通了,实际和网页端行为差很多。FOFA API 不保留 UI 层的隐式过滤,所有条件必须显式写成“字段=值”,条件之间用&&、||、!=连接。最常见的批量筛选字段如下:
| 字段 | 含义 | 示例 | 典型用途 |
|---|---|---|---|
host | 主机名,可带端口 | host=".example.com" | 圈定某个企业域名 |
ip | 单个 IP 或 CIDR | ip="1.2.3.0/24" | 扫某个网段暴露面 |
port | 端口号 | port="3389" | 找公网 RDP 主机 |
protocol | 应用层协议 | protocol="ssh" | 定位 SSH 服务 |
status_code | HTTP 状态码 | status_code="200" | 过滤存活 Web 服务 |
body | 响应体内容 | body="wps" | 响应体特征识别 |
banner | 底层服务指纹 | banner="weblogic" | 识别非 HTTP 中间件 |
字段与值必须用双引号包裹,裸写ip=1.2.3.4会直接触发 FOFA 的语法检查报错。所有逻辑符在 API 模式下必须自己写全,网页端展示的标签筛选和 API 接口不是同一套语义,直接把网页上复制来的条件贴进 FofaViewer,有可能出现结果差异。这个字段优先级的概念,同时也是后续拼 query 时的基本功。
在组合条件时,还要注意 FOFA API 的运算符优先级。&&优先级高于||,但没有隐式括号。举例来说,title="admin" || title="manage" && country="CN"的解读是前半段先做 OR,再与country做 AND,而不是两个 title 各自与 country 做 AND。所以我一般会给每个分支显式加括号:(title="admin" || title="manage") && country="CN"。这个细节在 GUI 里很容易验证,在脚本里对字符串拼接尤其常见,漏掉括号就可能多出大量非预期结果。
批量搜索应当从宽到窄做多轮收敛。第一轮用最少条件探道路,第二轮加特征,第三轮拿完整表达式翻页。
3.2 FofaViewer 如何把“批量”落地为可翻页任务
GUI 里批量的实现看起来只是“填词、设数量、点击”,但背后是 FOFA API 的search/all接口在按size和page做循环。page_max就是循环上限。工具在每页数据到达后追加渲染,直到达到上限或返回结果不足一页。这个行为在脚本侧可以直接复现,用 requests 请求 FOFA 接口做同样的事:
import requests import base64 import time email = "yourname@example.com" api_key = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx" query = 'protocol="redis" && country="CN"' fields = "host,ip,port,protocol" # 请求头按 FOFA API 要求对查询表达式做 base64 编码 fofa_enc = base64.b64encode(query.encode("utf-8")).decode("utf-8") for page in range(1, 6): params = { "email": email, "key": api_key, "qbase64": fofa_enc, "fields": fields, "size": 1000, "page": page, } resp = requests.get("https://fofa.info/api/v1/search/all", params=params, timeout=30) data = resp.json() if data.get("error") or not data.get("results"): break for row in data["results"]: print(row) time.sleep(2)这段代码的逻辑是每次带page参数请求 1000 条,qbase64是 FOFA 规定必须对查询表达式做 base64 编码的字段,fields控制返回列结构。time.sleep(2)用于降速,避免在短窗口内触发限流。注意data["results"]是二维数组,不是字典列表,因为它严格按fields的顺序输出。FofaViewer 的翻页策略和这段脚本本质相同,如果你需要抓超过 GUI 能处理的数量级,可以把page循环接上while,直到某次返回结果数量小于size才终止。
如果查询结果里包含大量看起来重复的 URL,建议先检查查询条件是否太单一。比如只写domain="example.com"而不带host,FOFA 可能把同一 IP 下多个虚拟主机条目全部返回,看起来像重复,其实每一行是不同的 Host 头。对安全测试来说这是有效数据,但要交给业务部门时就按ip维度去重。FofaViewer 不会在工具内部自动去重,默认把服务端原始数据原样展示,去重需求只能导出后处理。
3.3 组件指纹和业务搜索条件的设计:一次搜索两条路
设计查询之前先想清楚要的是精确还是召回。拿 Web 后台举例,title="管理后台"的召回范围很窄,很多后台标题不带这几个字;这时要换成body="admin" && body="login"做特征组合。反过来,想要降低误报率,就用body="微信登录" && body="admin"这种强业务特征组合。FOFA 的body用于匹配 HTTP 响应体,banner用于匹配端口服务的原始指纹,两者不能直接互换。
如果你的目标是给某个客户做资产面梳理,我一般会用一组锚点查询,至少包含domain、title、icon_hash其中之一,再配合country和status_code做过滤。.example.com的写法会命中所有子域及其关联 IP,这是做资产测绘时最高效的锚点。逐条把锚点查询在 FofaViewer 里执行,结果会自动追加到视图,配合去重逻辑就能拿到该目标的网络资产全貌。这里有一个值得留意的细节:FOFA 的host字段是带协议和端口的完整目标,domain字段是纯域名域,二者在导出结果里是独立列,后续处理别混用。
批量搜索的真正价值是拿同一套查询周期性地去跑,跑完比对差异。这要求你的查询条件写得像代码一样稳定,字段顺序固定,过滤条件不做临时调整。
4. 结果导出与排错:CSV/Excel 背后的数据治理和限速约束
4.1 导出格式选择与字段对齐:先把 CSV 的坑埋了
FofaViewer 的导出能力覆盖 CSV 和 Excel 两种格式。实践经验是,优先选 CSV。Java 导出的 CSV 编码在不同平台可能不一致,Windows 下常见 GBK,macOS 下是 UTF-8,Excel 直接打开可能乱码;而导出为 xlsx 时,某些版本会把长 IP 列自动转成文本,后续在离线数据里做端口合并反而要多一步处理。正确做法是在 GUI 选 CSV,再用 Python 做统一清洗:
import pandas as pd # 读取时统一按字符串解析,防止端口被转换为浮点数 df = pd.read_csv("fofa_export.csv", dtype=str) df = df.drop_duplicates(subset=["ip", "port"]) df["host"] = df["host"].str.replace(r"^https?://", "", regex=True) df = df[["host", "ip", "port", "protocol", "title"]] df.to_csv("fofa_clean.csv", index=False, encoding="utf-8-sig")用dtype=str读取所有列,是为了避免port被 pandas 解析成 int,导致端口21变成21.0。drop_duplicates以ip和port组合作为唯一键,过滤 FOFA 翻页时可能出现的重复记录。utf-8-sig输出编码带 BOM,让 Excel 打开就是正常中文。不要直接在 Excel 里对结果做筛选,几万行数据体量会卡死,交给 pandas 最合适。
导出后的表结构检查可以看下面这条验证路径:
| 检查项 | 命令 | 通过标准 |
|---|---|---|
| 行数 | wc -l fofa_clean.csv | 等于 GUI 显示条数 + 1(表头) |
| 列数 | awk -F',' '{print NF; exit}' | 等于result_fields列数 |
| 编码 | file fofa_clean.csv | Unicode text, UTF-8 |
| IP 是否规整 | cut -d',' -f2 | head | 无 http 前缀、无 CIDR 混合 |
4.2 那些让你以为工具坏了,其实是 API 限速的场景
在线服务使用过程中有三类现象很常见。第一类,搜索前几页正常,第三页起一直转圈,多是触发了 FOFA 的单 IP 限速,不是工具卡死。第二类,导出文件为空但查询有结果,多半是timeout_sec过短,请求在客户端超时,服务端已准备好数据却没传完。第三类,查询结果总比网页端少,这是账号等级在起作用,同样的size=1000,普通账号可能只返回 500。
处理这些问题的建议是:把thread_count降到 2,timeout_sec增大到 30,然后使用 FofaViewer 的手动加载下一页来翻页。不要依赖 GUI 的自动连续翻页,手动可以在每页之间留下人工节奏,反而比线程并发更稳妥。如果换成脚本实现,脚本必须加退避,比如遇到rate limited就 sleep 5 秒再重试前一页请求。
4.3 让导出的结果真正可用:Domain 合并与端口存活验证
结果拿到 CSV 里才是数据处理的开端。FOFA 返回的host经常是https://1.2.3.4:8443,而domain可能是同名站点,两者需要分场景使用。做资产备案时按domain分组,做暴露面评估时按host反解。如果做端口存活验证,不要试图用 icmp,直接对ip:port发起 TCP 连接测试,因为很多资产本身禁止 ping。
# 过滤掉纯 IP 格式,保留域名/CDN 类条目以便人工复核 cat fofa_clean.csv | awk -F',' '{print $1}' | grep -vE '^[0-9]+(\.[0-9]+){3}' | head -50这条命令取出host列,并筛掉纯 IPv4 格式,剩下的是包含域名或超链接格式的行,用于人工复核那些 FOFA 里没有 IP 化的资产条目。实际使用中,这类条目往往对应 CDN 节点,直接跳过即可。对端口存活做批量探测时,只验证 FOFA 结果里的服务端口,不要扫全端口,这是成本最低的存活语义。
另外,很多人在 FOFA 结果里把protocol和port混为一谈,protocol是 FOFA 识别到的应用层服务,可能是 ssh、http、rdp,而port是实际数字端口。两者不一致时,优先相信protocol,因为 FOFA 对协议识别是通过 banner 探测得到的,端口可能被动过映射。比如内网某台设备公网映射到 2222,FOFA 识别为 ssh,端口列是 2222。导出后做端口存活验证时,要用 2222 而不是 22。
5. 复用导出结果做资产变更监控:一次搜索与两次差分脚本
FofaViewer 是 GUI 工具,不适合做成常驻服务。但它的导出文件很适合做周期性快照。我拿它做资产变更监控的思路非常简单:固定一条查询,每周在同一个时间执行一次搜索并导出 CSV,然后用脚本对比相邻两次导出的差异,新增和下线资产一目了然。这么做比依赖平台订阅更贴合内部对历史数据的留痕要求。
具体操作分三步。第一步,在 FofaViewer 里固定result_fields和查询条件,导出为week1.csv和week2.csv。第二步,写一个差分脚本,把每行数据转换成一个集合元组,用集合运算拿到新增和消失的条目。代码示例如下:
import csv def parse_csv(path): with open(path, newline="", encoding="utf-8-sig") as f: reader = csv.reader(f) # 先跳过表头,避免首行参与集合运算 headers = next(reader) return headers, {tuple(row) for row in reader} h1, week1 = parse_csv("week1.csv") h2, week2 = parse_csv("week2.csv") assert h1 == h2, "列头必须一致,否则结果无意义" new_assets = week2 - week1 gone_assets = week1 - week2 print("新增资产:", len(new_assets)) print("下线资产:", len(gone_assets))这段脚本的关键点是next(reader),它把表头行先读掉,避免表头参与集合运算。导出文件用utf-8-sig编码后直接按行比较,host、ip、port等相关字段在这一时间段内没有变化时,集合长度会更稳定。列头不相同时,脚本直接assert失败,避免拿不同字段顺序的数据强行比较。
第三步,把脚本挂进定时任务。如果希望退出码能暴露差异,就在文件末尾加一行sys.exit(1 if new_assets else 0),然后用系统 cron 或写批处理文件,让监控端感知到结果集变化。因为 FofaViewer 手动执行是主线,脚本只做后处理,所以工具侧改参数不影响这个数据链路。
最后一个经验是:在二次检查时,优先关注new_assets里那些status_code=200且协议是 http/https 的条目,它们才是真正暴露的 Web 资产,其他诸如开放端口出去的服务往往由防火墙策略动态调整,不能直接等同于业务变化。
本文还有配套的精品资源,点击获取