kadeemj/URL-Safety-Checker
GitHub: kadeemj/URL-Safety-Checker
一款混合型 URL 安全分析工具,结合 14 项本地启发式检查与 Google Safe Browsing 信誉查询,生成可解释的 0–100 风险评分并附带通俗的证据说明。
Stars: 0 | Forks: 0
# URL 安全检查器
[](https://github.com/kadeemj/URL-Safety-Checker/actions/workflows/ci.yml)
[](LICENSE)
粘贴链接,获取判定结果——以及背后的原因。
一款混合型 URL 分析工具:**14 项本地计算的安全启发式检查**,外加
**Google Safe Browsing** 信誉查询,综合生成可解释的 0–100
风险评分。最终评分中的每一分都可以追溯到一项具名的检查以及
通俗易懂的证据,因此报告会告诉你链接*为什么*有风险,而不是
直接给出一个不透明的判定。
**在线演示:** https://kadeemj.github.io/URL-Safety-Checker/
## 架构
```
┌─────────────────────┐ POST /api/v1/check ┌──────────────────────────┐
│ GitHub Pages │ ────────────────────► │ FastAPI on Render │
│ (static frontend, │ │ │
│ vanilla JS) │ ◄──────────────────── │ 1. validate + SSRF guard │
└─────────────────────┘ JSON report card │ 2. 12 local heuristics │
│ 3. concurrently: │
│ · Google Safe Browsing│
│ · RDAP domain age │
│ · redirect chain walk │
│ 4. score + verdict │
└──────────────────────────┘
```
- **Frontend** (`docs/`):纯 HTML/CSS/JS,无框架,无构建步骤。
由 GitHub Pages 托管。所有 API 返回的字符串均使用
`textContent` 渲染——被分析的 URL 属于攻击者可控的输入,绝不能
被直接内插到 HTML 中。
- **Backend** (`backend/`):FastAPI + httpx。Safe Browsing API key 仅存放在
服务器环境中。CORS 限制为 Pages 源,并且
基于 IP 的滑动窗口速率限制保护着公共 endpoint。
## 检查项
| 检查项 | 信号 | 最高扣分 |
|---|---|---|
| 欺骗性 URL 用户名 | `https://paypal.com@evil.example` 实际访问的是 evil.example | 25 |
| 仿冒域名 (typosquatting) | 与 55 个顶级品牌的 Levenshtein 距离;子域名中包含品牌名 | 25 |
| 仿冒字符 (homoglyphs) | Punycode/IDN 骨架匹配品牌名 (`pаypаl` ≠ `paypal`) | 25 |
| 原始 IP 地址主机 | 无域名,无问责 | 20 |
| 域名年龄 (RDAP) | 注册 <30 天 (+20) 或 <180 天 (+10) | 20 |
| 重定向链 | 长度、短链接跳转、重定向至私有地址空间 | 15 |
| 异常长 / 随机 URL | 长度分级 + 路径/查询参数的香农熵 | 12 |
| URL 缩短服务 | 目的地被隐藏——这是一种不透明性惩罚,而非指控 | 12 |
| 钓鱼诱饵关键词 | 在外部域名上出现 `login`/`verify`/`secure` + 品牌关键词 | 12 |
| 未加密 (http) | 凭证页面应使用 HTTPS | 10 |
| 高滥用率 TLD | `.tk`, `.zip`, `.top`, …… 权重较低:单独作为弱信号 | 10 |
| 非标准端口 | 临时搭建的基础设施 | 8 |
| 过多的子域名 | `login.secure.account.verify.example.com` | 8 |
| 机器生成的域名 | 连字符/数字密集的可注册域名 | 8 |
| **Google Safe Browsing** | 确认的恶意记录将**覆盖一切**(见下文) | — |
**评分标准:** `score = min(100, Σ triggered penalties)` →
0–24 **安全** · 25–59 **可疑** · 60–100 **危险**。
有两个不对称设计是有意为之的:
- Safe Browsing **命中** 会强制将判定结果设为危险,并将分数下限设为
95——Google 已确认存在主动滥用;启发式检查无法降低其分数。
- Safe Browsing **未命中不会扣分**。没有证据并不代表
证据不存在:全新的钓鱼页面可能尚未被收录。
无法运行的检查项(RDAP 超时、缺少 API key)将报告为
`unavailable`,扣除零分,并将响应的
`confidence` 字段降级为 `partial`——该工具绝不会因为
我们自身的宕机而惩罚某个 URL,也绝不会在未实际执行查询时
假装已进行过查询。
## 威胁模型说明
这里有趣的安全问题在于,该服务会**获取
攻击者提供的 URL**(重定向遍历),这是一个教科书式的 SSRF 攻击向量。
`backend/app/validation.py` 实现了以下防护:
- scheme 白名单 (http/https)、长度限制、被阻断的主机名后缀
(`.local`, `.internal`, `.onion`, localhost)
- 字面量 IP 筛查,包含整数编码形式 (`http://2130706433/`
即 `127.0.0.1`)
- DNS 解析检查:对主机名进行解析,如果发现**任何**
非全局地址则拒绝(阻断通过 DNS-rebinding 指向 `169.254.169.254` 等地址的行为)
- 该防护在**每次重定向跳转时都会重新运行**,并且在任何
外部请求之前都会剥离 URL 中的凭证
其他安全加固措施:源限制 CORS、基于 IP 的速率限制
(在 Render 的代理后感知 `X-Forwarded-For`)、非 root Docker 镜像、
不持久化存储提交的 URL。
## 本地运行
```
# backend — Python 3.12+
cd backend
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements-dev.txt
cp .env.example .env # optionally add your Safe Browsing key
uvicorn app.main:app --reload # http://localhost:8000
# frontend — 任意静态服务器
cd ../docs
python3 -m http.server 3000 # http://localhost:3000
```
无需任何 API key 即可运行——Safe Browsing 检查只会报告
`unavailable`,而 14 项启发式检查仍会正常运行。
```
# tests + lint
cd backend
pytest # 125 tests, all external calls mocked (respx)
ruff check .
```
## 部署
1. **Backend → Render:** "New +" → Blueprint → 指向此仓库
(`render.yaml` 会配置一切)。在仪表板中设置 `GOOGLE_SAFE_BROWSING_API_KEY`。
2. **Frontend → GitHub Pages:** 仓库 Settings → Pages → 从分支
`main`、文件夹 `/docs` 进行部署。
3. 将 `docs/js/config.js` 指向你的 Render URL。
## 局限性(已知且接受)
- 速率限制器是基于内存的——在 Render 的单实例免费
层级上是准确的,但在多实例部署中计数会偏低。
- 品牌列表包含约 55 个域名;未列入榜单的品牌的 typosquat 不会被匹配。
- Render 的免费层级在空闲时会休眠;每天首次扫描大约需要
30–60 秒(UI 在等待期间会对此进行说明)。
- “安全”判定意味着*未发现风险信号*,而不是*被证明安全*。它是一个咨询
工具,而非神谕。
## License
MIT
标签:请求拦截, 调试辅助, 运行时操纵, 逆向工具