jay-tank/jotwatch
GitHub: jay-tank/jotwatch
一个零配置的静态 JWT 安全扫描器,通过 CI 退出码在代码层面拦截 JWT 配置错误,防止攻击者伪造令牌。
Stars: 0 | Forks: 0
# jotwatch
**在攻击者伪造 token 之前,静态捕获 JWT 配置错误。**
一个错误的标志就能将 JSON Web Token 认证变成一扇敞开的大门:如果接受
`"none"` 算法,任何人都可以发送带有 `"role":"admin"` 的未签名 token;
如果调用 `jwt.decode()` 而不是 `jwt.verify()`,你的应用所信任的声明
将受到攻击者的控制。这些错误经常被发布,它们绝不会在正常路径测试中失败,
而这正是 [RFC 8725 (JWT Best Current Practices)](https://www.rfc-editor.org/rfc/rfc8725) 和
[OWASP JWT Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/JSON_Web_Token_for_Java_Cheat_Sheet.html) 所警告的内容。
`jotwatch` 是一个零配置、与语言无关的闸门,它会扫描你的源代码,并通过
干净的 CI 退出代码标记出这些模式。
```
$ jotwatch .
● 2 JWT blocker(s):
src/auth.js:15 JWT verification accepts the "none" algorithm.
↳ The "none" alg means an unsigned token. An attacker can drop the signature
and forge any claim (RFC 8725 §3.1). Pin an explicit allow-list of signing
algorithms and never include "none".
[JW001]
src/auth.js:21 jwt.decode() reads claims without verifying the signature.
↳ In jsonwebtoken, decode() does NOT check the signature — its claims are
attacker-controlled. Use jwt.verify(token, key, {algorithms:[...]}) ...
[JW002]
2 blockers · 1 warning
```
遇到 blocker 会返回退出代码 `1`,因此可以直接将其接入 pre-commit 或 CI。
## 为什么它与众不同
大多数 JWT 工具都是用于*运行时*的攻击套件(如 jwt_tool、jwt-scanner),它们会猛烈攻击实时的 endpoint,或者是深埋在大型 SAST 平台中的 JWT 规则。`jotwatch` 则恰恰相反:它是一个独立、零依赖的静态闸门,只专注于一件事——JWT 安全——它能在几毫秒内在你的源代码上运行,无需服务器,无需配置,也无需语言插件。
## 工作原理
`jotwatch` 逐行读取每个源文件,并匹配少量基于 RFC 8725 / OWASP 的不区分大小写的模式。它是一个启发式扫描器,而不是类型检查器:不执行任何代码,不进行任何网络调用,也没有任何需要配置的东西。它是一个单一的静态 Go 二进制文件。
唯一与语言相关的细节是:仅在 JavaScript/TypeScript 中,单纯的 `jwt.decode()` 会被标记,因为在 Node 的 `jsonwebtoken` 中,`decode()` 会跳过验证——而在 Python 的 PyJWT 中,`jwt.decode()` *正是*用于验证的调用。
## 规则
| 规则 | 严重程度 | 标记内容 | 依据 |
| :--- | :--- | :--- | :--- |
| JW001 | blocker | 接受 `"none"` 算法(`"alg":"none"`、`algorithms:["none"]` 等) | RFC 8725 §3.1 |
| JW002 | blocker | 禁用签名验证(`verify=false`、`verify_signature:false`、JS 中的 `jwt.decode()`) | RFC 8725 §2.2 |
| JW003 | warning | 不强制检查过期时间(`verify_exp:false`、`ignoreExpiration:true`) | RFC 8725 §3.9 |
| JW004 | warning | 传递给 JWT 调用的签名密钥是硬编码的字符串字面量 | OWASP JWT Cheat Sheet |
规则倾向于较低的误报率:JW004 仅在字符串字面量作为 key 参数直接传递给 `jwt.sign/encode/verify` 调用时触发,因此从环境中加载的 key 永远不会被标记。
## 安装
```
go install github.com/jay-tank/jotwatch@latest
```
或者从源码构建:`go build -o jotwatch .`
## 用法
```
jotwatch # scan the current directory
jotwatch ./src # scan a path
jotwatch --strict # treat warnings (JW003/JW004) as failures too
jotwatch --json # machine-readable output
```
### 在 CI 中
```
- run: go run github.com/jay-tank/jotwatch@latest ./ --strict
```
退出代码:`0` 表示干净 · `1` 表示存在 blocker(或在 `--strict` 下存在任何发现) · `2` 表示使用错误。
## 抑制误报
`jotwatch` 是一个启发式闸门。要屏蔽某一行,请在其上添加 `jotwatch:ignore` 注释(`// jotwatch:ignore` 或 `# jotwatch:ignore`)。要跳过整个路径,请在 `.jotwatchignore` 文件中列出路径子字符串(每行一个,使用 `#` 进行注释)。
## 范围
`jotwatch` 能够很好地回答一个聚焦的问题——*“这段代码是否接受不安全的 JWT?”*——它跨越各种语言,作为一个快速的静态闸门。它不能证明你的验证在其他方面是完美的(正确的受众、正确的签发者、密钥轮换);它捕获的是少数几个最直接让攻击者能够伪造 token 的错误。
## 许可证
MIT © Jay Tank
标签:DevSecOps, EVTX分析, Go, JWT, Ruby工具, URL发现, 上游代理, 动态分析, 安全检测, 日志审计, 错误基检测, 静态代码分析