Kirill89/webcensus
GitHub: Kirill89/webcensus
一个漏斗式多阶段流水线,用于在单机上快速从数百万域名中精确搜索并验证特定 URL 路径文件的存在。
Stars: 0 | Forks: 0
# WebCensus
一个快速 pipeline,用于在单台机器上以合理的时间,在**数百万个域名**中搜寻特定的文件路径(例如 `/.well-known/security.txt`、
`/robots.txt`、`/ads.txt`、`/humans.txt`、`/sitemap.xml`)。
整个流程运行在一个可复现的 Docker 沙箱中。每个阶段都是一个
狭窄的漏斗,尽可能以极低的成本剔除非候选目标,因此
开销昂贵的阶段只会处理通过前一层过滤存活下来的主机。
```
domain list ─► DNS (A records) ─► HTTPS probe ─► bulk fetch ─► verify
massdns skim (Rust) curl --parallel bun
UDP-fast status + cert bounded retries shape gate
```
## 使用方法
```
make shell # build & enter the sandbox
./scripts/step1_download_domain_list.sh # or bring your own data/domains.txt
./scripts/step2_massdns.sh
./scripts/step3_probe_status.sh
./scripts/step4_curl_config.mjs
./scripts/step5_curl.sh
./scripts/step6_filter.mjs
```
所有产物都将落在宿主机的 `./data/` 目录中(该目录会挂载到容器内)。
## 流水线 (pipeline)
### 1. 域名列表 — `step1_download_domain_list.sh`
下载 [Chrome UX Report 排名前列的站点](https://github.com/zakird/crux-top-lists)
(公开的 Top-N 列表中最“真实”的一个,基于实际 Chrome 用户
访问量进行排名),剥离排名列和 `https://` 协议,并写入
`data/domains.txt` —— 每行一个顶级域名。
只要输出格式是每行一个域名,你可以替换为任何其他来源(参见[域名列表来源](#domain-list-sources))。
### 2. DNS 解析 — `step2_massdns.sh`
使用 [massdns](https://github.com/blechschmidt/massdns) 针对
`resolvers.txt`(一组精选的公共递归解析器 —— Cloudflare、
Quad9、Google、AdGuard、OpenDNS 等)运行,为每个域名获取 A 记录。
- 输出:`data/dns.ndjson`(massdns 的 JSON 格式)
- 为什么它很快:massdns 通过解析器池并行发送 UDP 查询;在单台机器上可达到每秒数万次查询。其 JSON 输出是 `skim` 直接消费的格式 —— 无需中间转换。
### 3. HTTPS 状态探测 — `step3_probe_status.sh`
针对每个已解析的主机运行 `skim`,这是一个专为异步探测构建的 Rust 工具(参见 `skim/`)。对于每台主机,它会:
1. 打开一个到 `:443` 的 TCP 连接(可配置)。
2. 使用**记录验证器**执行 TLS 握手 —— 证书链验证会运行完毕并记录结果,但无效证书不会中止握手。这使得单次扫描既能捕获 HTTP 状态,*又*能判断证书是否可信。
3. 发送一个原始的 HTTP/1.1 `GET ` 请求。
4. **仅读取状态行**并关闭 socket。
输出行(`data/status.ndjson`)如下所示:
```
{"url":"https://example.com/.well-known/security.txt","status":"success","code":200,"cert_ok":true}
```
为什么它很快:
- 仅读取状态行 —— 不读取 body,不消耗 body 的带宽。
- 通过 `tokio` 信号量限制并发上限(默认为 100,可通过 `--concurrency` 调整)。
- 严格的逐阶段超时设置(连接 / 握手 / 读取),因此无响应的主机会在几秒内判定失败。
- 可恢复:传递 `--start-line N` 可在崩溃后继续执行。
- 预扫描会扫描一次输入以计算可探测的行数,从而提供真实的预计剩余时间 (ETA);
`--skip-precount` 可在恢复执行时跳过此步骤。
### 4. 构建 curl 配置 — `step4_curl_config.mjs`
流式读取 `data/status.ndjson` 并写入 `data/urls.curl`,这是一个 curl `-K` 配置
文件,其中为每个满足 `cert_ok && status == "success" && code == 200` 条件的 URL 添加一条记录:
```
url = "https://example.com/.well-known/security.txt"
output = "example.com_.well-known_security.txt"
url = "https://github.com/.well-known/security.txt"
output = "github.com_.well-known_security.txt"
```
每个 `output =` 是将其 URL 中的非字母数字字符替换为 `_` 后的结果,因此文件
落地时具有稳定且抗冲突的名称。该脚本还会输出一份快速的
合理性检查:有多少行的 `code == 200`,以及总共有多少行是可探测的。
为什么作为单独的阶段:将配置生成保留在 JS 中,意味着 URL 形状的
过滤器和文件名规则与 pipeline 的其余逻辑放在一起,
而 curl 调用本身仍然只需一行命令。
### 5. 批量抓取 — `step5_curl.sh`
运行 `curl --parallel -K data/urls.curl` 将每个 URL 下载到
`data/unfiltered/`。这些参数专为“从许多不同的
主机下载许多小文件”进行了调优:
- `--parallel-max 50` —— 并发传输中的最大连接数。
- `--max-filesize 5M` —— 预检拒绝 `Content-Length > 5MB` 的响应。
- `--max-time 10` —— 每次尝试的总挂钟时间上限(防止服务器
在没有 `Content-Length` 的情况下永远滴答传输数据)。
- `--connect-timeout 3` —— TCP 连接超时预算。
- `--retry 2 --retry-delay 5 --retry-max-time 30` —— 有限的重试预算,使得不稳定
的主机无法长时间占用其处理槽。
为什么使用 curl `--parallel` 而不是 aria2c / wget2:curl 是唯一能够硬性限制响应**大小**(`--max-filesize`)的主流工具 —— 当服务器
针对你想要的文件返回 200 状态码及数兆字节的 HTML 时,这一点很重要。它还运行
在单个进程中,因此 100 万个 URL 不需要执行 100 万次 `fork()` 调用。
### 6. 内容形状验证 — `step6_filter.mjs`
离线遍历 `data/unfiltered/` 并将存活文件复制到 `data/filtered/`。
默认的 `text-file` 验证器会拒绝:
- 短于 16 个字符的 body(空内容 / “Not Found” / “OK”),
- 包含控制字符或无效 UTF-8 的 body(二进制噪声),
- 看起来像 HTML、PHP 源码或 JSON 的 body(某些服务器对每个路径
都会以 SPA 外壳或通用 JSON 错误返回 200-OK)。
对于应该是 JSON 的路径,还提供了 `json` 验证器;将其作为
第一个参数传入:
```
./scripts/step6_filter.mjs json
```
为什么作为单独的阶段:验证器是**纯离线的** —— 无网络请求,无
顺序约束,幂等,并且可以使用不同的规则重新运行而无需
重新抓取。在 `step6_filter.mjs` 中添加新的验证器并重新运行;
带宽已经消耗过了。
## 为什么端到端如此快速
该 pipeline 呈漏斗状。每个步骤的*单主机*处理开销大约比
下一步低一个数量级,因此开销昂贵的阶段永远只会处理
输入的一小部分:
| 阶段 | 单主机开销 | 剔除内容 |
|-------------------|----------------------------|------------------------------------------|
| massdns | 一次 UDP 往返 | 已停放 / 死亡 / NXDOMAIN 的域名 |
| skim | 一次 TLS + 状态行 | 无 443 端口、无效证书、非 200 状态码的主机 |
| curl --parallel | 一次完整的 HTTPS GET (有上限)| 过大 / 挂起 / 不可达的主机 |
| filter (离线) | 每个文件一次正则表达式匹配 | 200-OK 但实际并非目标文件的噪声数据 |
此外:massdns 的输出 → skim 的输入使用的是相同的 NDJSON;skim 的输出 → curl
配置只需一次流式处理。各阶段之间没有高开销的格式转换,
并且验证器阶段完全不碰网络。
## 域名列表来源
### 排名前约 100 万列表 (免费)
- **Tranco** — 研究级综合排名。稳定,每月更新。https://tranco-list.eu
- **Cisco Umbrella Top 1M** — 来自 Umbrella 解析器的 DNS 查询量。每日更新;变动较大。http://s3-us-west-1.amazonaws.com/umbrella-static/index.html
- **Majestic Million** — 按引用子网排序(链接图谱)。每日更新。https://majestic.com/reports/majestic-million
- **Cloudflare Radar Top 1M** — 来自 1.1.1.1 的查询量。https://radar.cloudflare.com/domains
- **Chrome UX Report (CrUX)** — 按真实 Chrome 用户访问量排名的顶级站点。所有这些列表中最“真实”的一个。每月通过 BigQuery 发布。https://github.com/zakird/crux-top-lists *(`step1` 中的默认选项)*
### 更大的列表 (1000 万+)
- **Open PageRank Top 10M** — 基于 Common Crawl 链接图谱。https://www.domcop.com/openpagerank/what-is-openpagerank
- **DomCop Top 10M** — 聚合 Open PageRank;完整版需付费,提供免费样本。
### 超大规模 (1 亿+)
- **DNS zone files** — 每个 TLD 的已注册域名完整列表。
- Verisign 提供的 `.com`/`.net`(仅 `.com` 就约有 1.6 亿个)。
- ICANN CZDS 提供数百个 TLD:https://czds.icann.org
- 免费但需要签署访问协议。
- **Common Crawl URL index** — 从数十亿抓取的 URL 中提取唯一域名。https://commoncrawl.org
### 实时 / 持续更新
- **Certificate Transparency logs** (crt.sh, certstream) — 签发的每个 TLS 证书的实时信息流。有用视角:新建站点通常存在配置错误。https://certstream.calidog.io
- **SecurityTrails / DNSlytics** — 提供免费额度的商业 DNS 聚合器。
## 仓库布局
```
Dockerfile, Makefile — sandbox image (Ubuntu + node + bun + rust + massdns + curl)
ai-sandbox/ — same sandbox + claude-code, for AI-assisted iteration
scripts/ — the six pipeline steps
skim/ — Rust HTTPS prober (status-line + cert verdict)
resolvers.txt — curated public DNS resolvers for massdns
domain-lists/ — published domain lists produced by this pipeline
data/ — pipeline outputs (gitignored, mounted from host)
```
## 数据集
该仓库还发布了通过运行此 pipeline 生成的域名列表。每个
列表都是一个快照 —— 网络是不断变化的,因此该列表仅在其生成日期
及该日期当天是准确的。
**命名规范:** `YYYY-MM-DD-.txt`,每行一个顶级域名,
无表头,按字母顺序排序。日期前缀是运行扫描的那一天。
**可用列表:**
- **`2026-04-26-with-security-txt.txt`**(14,611 个域名) — 在 2026-04-26 探测时,通过有效证书经 HTTPS
提供经过内容验证的 `/.well-known/security.txt` 的域名。方法论:pipeline 以 CrUX 顶级站点列表作为输入运行,
`step3` 目标为 `/.well-known/security.txt`,`step6` 使用默认的 `text-file` 验证器
(拒绝空 body、HTML/JSON/PHP 形状、二进制噪声以及无效 UTF-8)。排除了文件存在
但却是带有 200 状态码的 HTML 404 页面的域名。
- **`sample.txt`**(3 个域名) — 一个微型夹具列表(`example.com`、
`google.com`、`github.com`),端到端 CI 工作流使用它对针对 `/robots.txt` 运行的 pipeline 进行冒烟测试。不属于研究产物。
随着新扫描的运行(不同的路径、
不同的日期、不同的域名列表输入),将添加更多列表。要复现任何列表,
使用记录的日期作为 CrUX 快照自己运行 pipeline 即可 —— 给定固定的输入列表,pipeline 是确定性的。
**这些数据集的使用**:参见顶部的[免责声明](#webcensus)。
域名是公开信息;如果你基于这些列表构建衍生工具,请做一个
良好的网络公民(带有标识的 UA、低并发、尊重删除请求)。
## 引用
如果你在学术或研究工作中使用了本软件,请引用本
仓库。BibTeX 条目如下:
```
@software{webcensus,
title = {webcensus: a fast pipeline for path-specific web measurement at scale},
author = {Kirill},
year = {2026},
url = {https://github.com/Kirill89/webcensus}
}
```
## 许可证
[MIT](LICENSE)。按**原样**提供,不做任何保证。使用风险自负 — 参见本 README 顶部的免责声明。
## 配置选项
- `scripts/step3_probe_status.sh` — 更改 `--path` 以搜寻不同的文件,如果你的网络能够承受,可以提高 `--concurrency`。
- `scripts/step5_curl.sh` — 调整 `--parallel-max`、`--max-filesize`、`--max-time` 以及重试预算。默认值较为保守;如果你的机器和 `ulimit -n` 允许,可以将 `--parallel-max` 提高到 200 以上。
- `scripts/step6_filter.mjs` — 第一个参数用于选择验证器(默认为 `text-file`,也内置了 `json`)。在 `verifiers` 对象中添加新的验证器作为键。
- `resolvers.txt` — 添加/删除解析器;massdns 会在列表中进行负载均衡。
标签:DNS枚举, DNS解析, Docker, GitHub, HTTP请求, Linux取证, massdns, NIDS, Rust, TLS探测, URL路径发现, 主动侦察, 内容验证, 可视化界面, 大规模域名扫描, 子域名发现, 安全文件探测, 安全防御评估, 实时处理, 容器化, 密码管理, 开源项目, 批量抓取, 搜索引擎优化(SEO), 数字取证, 数据可视化, 网站指纹识别, 网络安全工具, 网络流量审计, 网络资产普查, 自动化脚本, 请求拦截, 通知系统