把安全检查前移到配置阶段:frpguard 的 13 条规则
我这台 Windows 机器需要偶尔从外面连回来。搭 frp 的时候翻了一圈中文教程,发现一个规律:绝大多数教程给出的配置都是「能跑通、但不安全」的。
典型长这样:
bindPort = 7000
webServer.addr = "0.0.0.0"
[[proxies]]
type = "tcp"
localPort = 3389
remotePort = 13389
四行里埋了四个雷:没有 auth.token(谁都能把你的 frps 当免费跳板)、管理面板监听 0.0.0.0(面板直接挂公网)、没有 allowPorts(任意端口可被登记)、13389 这个端口号所有教程都在用(扫描器的字典第一条)。
这些问题有个共同点:它们都不影响连通性。配置跑得起来,端口能通,所以你根本不会意识到写错了。等意识到的时候,通常是因为日志里出现了陌生 IP。
于是我把「什么算错」写成规则,做成一个检查器。
frpguard 是什么
一个纯前端页面:把 frps.toml / frpc.toml 粘进去,立刻得到分级风险报告。
零依赖、单文件、无网络请求,双击 index.html 就能用。无网络请求这点很重要——意味着你可以放心粘贴包含真实 token 的配置(虽然我仍然建议先脱敏)。
13 条规则
规则分两个作用域(服务端 / 客户端),四个严重度。
服务端 frps.toml
| 规则 | 严重度 | 检查内容 |
|---|---|---|
FRPS_NO_AUTH |
严重 | 没配 auth.token,任何人可把你的 frps 当免费跳板 |
FRPS_DASHBOARD_PUBLIC |
严重 | webServer.addr 不是回环地址,管理面板暴露公网 |
FRPS_WEAK_TOKEN |
高危 | token 短于 24 字符,可被爆破 |
FRPS_DASHBOARD_NO_PASS |
高危 | 面板开了端口但没设密码 |
FRPS_ALLOWPORTS_WIDE |
高危 | allowPorts 某区间跨度 > 100,等于放行一大片端口 |
FRPS_ALLOWPORTS_NONE |
中危 | 完全没配 allowPorts,默认允许任意端口 |
FRPS_ALLOWPORTS_PRIVILEGED |
中危 | allowPorts 含 < 1024 的低号端口 |
客户端 frpc.toml
| 规则 | 严重度 | 检查内容 |
|---|---|---|
FRPC_NO_TOKEN |
严重 | 没配 token,你以为是连自己的 frps,其实可能连到别人的 |
FRPC_TUTORIAL_PORT |
高危 | remotePort 落在教程默认端口集合(13389 / 3389 / 7001 / 8080 / 8000 / 8888 / 9000) |
FRPC_RDP_PUBLIC |
高危 | 用 tcp 类型把 3389 映射到公网 |
FRPC_SECRETKEY_SHORT |
高危 | stcp / xtcp 的 secretKey 短于 24 字符 |
FRPC_TLS_OFF |
中危 | 显式写了 transport.tls.enable = false,隧道内容明文 |
FRPC_NO_STCP |
提示 | 全部代理都是 tcp 类型,没有 stcp / xtcp 备用路径 |
写规则时最难的不是判「有没有」,而是判「够不够」。比如 token 长度,我定在 24 字符——低于这个数的高熵爆破在常见带宽下是可行的。allowPorts 区间跨度定在 100,是因为真实的 frp 使用场景里,一个人同时需要一百多个连续远端端口的情况极少;跨度大往往意味着「懒得想,直接给一大段」。
评分模型
从 100 分开始扣:
| 严重度 | 扣分 |
|---|---|
| 严重 | −40 |
| 高危 | −20 |
| 中危 | −8 |
| 提示 | −3 |
结果截断在 0~100,三档配色:≥85 绿、60~84 黄、<60 红。
为什么用硬编码权重而不是加权模型? 因为可解释。用户看到「−40」能立刻知道哪几条最要命;某些评分模型给一个 73.6 分,你没法从分数反推该先改哪里。安全提示的价值在于让人立刻知道下一步做什么,不在于分数本身多精确。
为什么是静态分析
三个理由:
- 探测需要目标可达,而且会暴露「有人在扫你」——对一个刚暴露到公网的服务来说,这是额外风险。
- 配置文件本身就是风险的来源。上面那四行里没有一个需要「探测」才能发现的问题,全是读文本就能判定的。
- 无网络请求 = 可离线使用 = 可粘贴真实配置。这点决定了它到底会不会被真正用起来。一个要求你把配置上传到某台服务器的检查器,没人会粘真实 token 进去。
自己写 TOML 解析器的取舍
为了保持零依赖,我手写了一个 TOML 子集解析器,只支持 frp 配置用到的语法:[[table]]、[table]、key = value、点号嵌套键、多行数组、行内表。
不支持日期字面量、多行字符串、十六进制整数等 TOML 完整特性。这换来 git clone 后无需 npm install、无需构建、双击即用。
这是个明确的取舍:工具的使用成本必须低于它省下的成本。如果为了跑一个检查器要先装 node_modules,那不如不检查。
实测
用两份真实生产配置跑(内容已脱敏,只留结果):
| 配置 | 分数 | 命中 |
|---|---|---|
加固后的 frps.toml(本地面板 + 单端口放行 + 32 位 token) |
100 | 无 |
加固后的 frpc.toml(stcp 备用 + 高位端口 + 仍开一条 tcp RDP) |
80 | FRPC_RDP_PUBLIC |
第二条是我想强调的取舍:即使做了大部分加固,只要还对公网开着 RDP,就仍然扣分。
80 分不是「还凑合」,是「这里有一个必须知道自己承担了什么的选择」。工具的责任是把选择摆到明面上,而不是替用户做决定。
一条只提示、不重罚的规则
FRPC_NO_STCP 只是个「提示」,扣 3 分。
stcp(secret tcp)明显更安全——它不需要在服务端放行远端端口,访问端要用带 secretKey 的 visitor 才能连。但它要求每台访问设备都装 frpc visitor。对「随时随地用手机构一下」的场景,tcp 是理性选择。
工具不该把自己的洁癖强加给使用者。所以这里只提示、不重罚——这也是为什么它的严重度是「提示」而不是「中危」。
局限(说清楚)
- 只做静态分析,不验证配置能否跑通、不检测端口是否真在监听
- 规则覆盖的是常见错误,不是完整安全审计
- 不支持旧版
frpc.ini(只支持 TOML,frp ≥ 0.52) - 不检查
[include]引入的外部文件 - 不联网核对 frp 版本是否有已知漏洞
一个只查「常见错误」的工具确实不高级。但现实是:绝大多数出事的人不是被高级攻击打穿的,是配置里就写着漏洞。
怎么用
git clone <repo>
cd frpguard
# 直接打开
open index.html # macOS
start index.html # Windows
xdg-open index.html # Linux
页面上有四个样例按钮(加固前后的 frps / frpc 各一份),可以先点着看效果,再粘自己的配置。
如果愿意,也可以把 guard.js 单独拿去用——它是纯逻辑层,不碰 DOM,可以在 Node 里单测:
const { parseToml, run, score } = require("./guard.js");
const findings = run(parseToml(tomlText));
console.log(score(findings), findings.map(f => f.id));
把它挂进 CI,每次改配置自动跑一遍,也就十几行的事。
MIT 许可。
- 点赞
- 收藏
- 关注作者
评论(0)