clay-good/qiuth
GitHub: clay-good/qiuth
Qiuth 是一款为 API 密钥添加多因素认证的安全中间件,将 Bearer Token 转化为持有证明令牌,在密钥泄露时仍能阻止未授权访问。
Stars: 5 | Forks: 1
# Qiuth
**为 API 密钥提供多因素认证** - 别再把 API 密钥当成普通密码了。
发音为 **chew-auth**。灵感来源于 [Kevin Qiu](https://www.linkedin.com/in/kevinmqiu)
## 问题所在
**API 密钥是单点故障。** 如果你的 API 密钥泄露(被提交到 GitHub、在传输过程中被截获、从日志中失窃),攻击者将获得对你 API 的**无限访问权限**。
```
# 你的 .env 文件被意外提交到了 GitHub
API_KEY=sk_live_abc123def456
# 攻击者发现了它并获得了完全访问权限
curl -H "Authorization: Bearer sk_live_abc123def456" https://api.yourapp.com/data
# 成功 - 攻击者下载了你所有的数据
```
**这种情况比你想象的更为常见:**
- 每天都有数以千计的 API 密钥在 GitHub 上泄露
- `.env` 文件被意外提交到公开仓库
- API 密钥被记录在错误消息或监控工具中
- 密钥在传输过程中被截获或从被入侵的系统中失窃
- 即使使用密钥对,如果私钥泄露,一切也就完了
## 解决方案
**Qiuth 为你的 API 密钥增加了多因素认证**,将它们从 Bearer Token(持有密钥的任何人都可以使用)转变为**持有证明令牌**(你需要密钥加上额外的验证因素)。
### 多层防御
1. **IP 允许列表** - 第一道防线
- 验证请求是否来自授权位置
- 支持 IPv4/IPv6 CIDR 表示法
- 立即阻止未授权的网络
2. **TOTP MFA** - 基于时间的一次性密码
- 适用于服务账号(程序化运行)
- 令牌每 30 秒更改一次
- 即使 API 密钥泄露,攻击者也需要 TOTP 密钥
3. **请求签名** - 加密证明(选择其一):
**证书认证**(非对称)
- 需要 RSA 私钥对每个请求进行签名
- 服务器上仅存储公钥
- 最适用于外部客户端,或当你需要考虑密钥分发问题时
**HMAC 认证**(对称)
- 使用共享密钥进行请求签名
- 设置更简单,对内部服务提供相同的安全性
- 最适用于受信任的环境和服务间通信
4. **时间戳验证** - 防止重放攻击
- 签名请求包含时间戳
- 服务器拒绝过期请求(时间窗口可配置)
- 即使请求被截获也无法重放
### 实际影响
**使用 Qiuth 之后:**
```
# API key 在 GitHub 上泄露
API_KEY=sk_live_abc123def456
# 攻击者尝试使用它
curl -H "X-API-Key: sk_live_abc123def456" https://api.yourapp.com/data
# 失败:401 Unauthorized - IP 不在 allowlist 中
# 攻击者需要所有这些条件:
# 1. API key(已泄露)
# 2. TOTP secret(单独存储)
# 3. Signing key(private key 或 HMAC secret)
# = 几乎不可能被攻破
```
## 为什么选择 Qiuth?
### 非人类身份 (NHI) 的安全盲区
我们在人类账户的多因素认证上投入了大量精力。密码管理器、身份验证器应用、硬件密钥——安全行业已经让 MFA 成为人类用户的标配。
**但是服务账号呢?**
服务账号、API 客户端和机器间通信的集成通常使用以下方式进行身份验证:
- 永不轮换的静态 API 密钥
- 没有第二验证因素的 OAuth client_id/client_secret 对
- 没有额外验证的长期有效令牌
这造成了一个根本的安全盲区:**非人类身份的认证强度弱于人类身份**,尽管它们通常拥有对敏感系统更广泛的访问权限。
### OAuth Client Secret 的问题
许多开发者认为“只需使用 OAuth”就能解决 API 安全问题。但是 OAuth client secret 具有与 API 密钥相同的漏洞:
- **Client Secret 是静态的** - 除非手动轮换,否则它们不会改变
- **Client Secret 是可重用的** - 任何拥有该 secret 的人都可以请求令牌
- **Client Secret 可能被截获** - 通过 MITM 中间人攻击截获初始令牌请求,你就能获得 secret
- **Client Secret 本质上是密码** - 带有凭证失窃的所有相同风险
JP Morgan Chase 的 CISO [致第三方供应商的公开信](https://www.jpmorganchase.com/about/technology/blog/open-letter-to-our-suppliers) 强调了关于 OAuth 和当前机器间通信认证标准的确切隐患。
### 为什么 IP 允许列表应当成为标配
一个简单的问题:**为什么 SaaS 应用添加 IP 允许列表如此困难?**
考虑这个真实场景:一名工程师意外将包含根访问密钥的配置文件提交到了公开仓库。几分钟内,自动化扫描器就发现了它。该密钥宣告沦陷。
如果使用一个简单的 IAM 策略,这本来根本不是问题:
```
Deny all access with this key if source IP is not in [VPN_STATIC_IP]
```
即使是最简单的 IP 验证——在返回响应之前读取 `X-Forwarded-For` 头——也能极大地缩小泄露凭证的波及范围。然而,大多数 API 并不提供这种基本保护。
Qiuth 将 IP 允许列表作为一项核心功能,而不是事后补丁。
### Qiuth 的方法:为所有身份提供 MFA
Qiuth 将我们对人类账户所期望的同等严格的安全标准应用于服务账号:
| 人类账户 | 服务账号 (使用 Qiuth) |
|--------------|------------------------------|
| 密码 | API 密钥 |
| 基于 IP 的访问控制 | IP 允许列表 (第 1 层) |
| 身份验证器应用 (TOTP) | 面向服务的 TOTP (第 2 层) |
| 硬件安全密钥 | 证书签名 (第 3 层) |
结果是:即使你的 API 密钥泄露,攻击者还需要同时攻破你的 TOTP 密钥和你的私钥,并且必须从允许的 IP 地址发起请求。
**这就是针对机器身份的纵深防御。**
## 快速开始
### 安装
```
npm install qiuth
```
### 基本用法
```
import { QiuthConfigBuilder, QiuthAuthenticator, generateKeyPair } from 'qiuth';
// Generate certificate key pair for maximum security
const { publicKey, privateKey } = generateKeyPair({ modulusLength: 2048 });
// Configure all three security layers
const config = new QiuthConfigBuilder()
.withApiKey('your-api-key')
.withIpAllowlist(['192.168.1.0/24'])
.withTotp('your-totp-secret')
.withCertificate(publicKey) // Add certificate-based authentication
.build();
// Authenticate requests
const authenticator = new QiuthAuthenticator();
const result = await authenticator.authenticate({
apiKey: 'user-provided-key',
clientIp: '192.168.1.100',
totpToken: '123456',
signature: 'base64-signature', // Required when using withCertificate
timestamp: Date.now().toString(),
method: 'GET',
url: 'https://api.example.com/resource',
}, config);
if (result.success) {
console.log('Authentication successful!');
} else {
console.error('Authentication failed:', result.errors);
}
```
**注意:** 使用 `.withCertificate(publicKey)` 以获得最高安全性。这要求客户端使用其私钥对每个请求进行加密签名,提供持有证明,即使 API 密钥和 TOTP 密钥泄露也能防止未经授权的访问。
### 框架中间件
Qiuth 为流行的 Node.js 框架提供了中间件:
| 框架 | 函数 | 运行时 |
|-----------|----------|---------|
| Express | `createQiuthMiddleware` | Node.js |
| Fastify | `qiuthFastifyPlugin` | Node.js |
| Koa | `createQiuthKoaMiddleware` | Node.js |
| Hono | `createQiuthHonoMiddleware` | Node.js, Cloudflare Workers, Deno, Bun |
#### Express
```
import express from 'express';
import { createQiuthMiddleware, QiuthConfigBuilder } from 'qiuth';
const app = express();
const qiuthAuth = createQiuthMiddleware({
configLookup: async (apiKey) => {
return await db.getApiKeyConfig(apiKey);
},
});
app.get('/api/protected', qiuthAuth, (req, res) => {
res.json({ message: 'Access granted!' });
});
```
#### Fastify
```
import fastify from 'fastify';
import { qiuthFastifyPlugin } from 'qiuth';
const app = fastify();
app.register(qiuthFastifyPlugin, {
configLookup: async (apiKey) => await db.getApiKeyConfig(apiKey),
});
app.get('/protected', { preHandler: app.qiuthAuth }, async (request, reply) => {
return { authenticated: true };
});
```
#### Koa
```
import Koa from 'koa';
import { createQiuthKoaMiddleware } from 'qiuth';
const app = new Koa();
const qiuthMiddleware = createQiuthKoaMiddleware({
configLookup: async (apiKey) => await db.getApiKeyConfig(apiKey),
});
app.use(qiuthMiddleware);
```
#### Hono (支持边缘计算)
```
import { Hono } from 'hono';
import { createQiuthHonoMiddleware } from 'qiuth';
const app = new Hono();
app.use('/api/*', createQiuthHonoMiddleware({
configLookup: async (apiKey) => await db.getApiKeyConfig(apiKey),
}));
```
[完整的中间件文档](./docs/middleware-integrations.md)
## 交互式演示
**5 分钟即可体验 Qiuth 的实际运行!**
```
# Clone 仓库
git clone https://github.com/clay-good/qiuth.git
cd qiuth
# Install dependencies
npm install
# 启动 interactive demo
npm run demo
```
演示服务器将启动并显示测试凭证。打开一个新的终端并尝试测试命令,看看所有三个安全层的实际运作!
**你将体验到:**
- 级别 1:基本 API 密钥认证
- 级别 2:API 密钥 + IP 允许列表
- 级别 3:API 密钥 + IP + TOTP MFA
- 级别 4:最高安全级别(所有三层)
- 失败场景(凭证错误、令牌过期、签名无效)
[**完整演示指南**](./demo/README.md)
## 功能
### 安全性
- **三层认证** - 基于 IP、TOTP 和证书
- **快速失败验证** - 在第一次失败时即停止,以保证性能
- **防止重放攻击** - 时间戳验证
- **安全的凭证生成** - 加密安全的随机生成
- **API 密钥哈希** - 使用 SHA-256 进行安全存储
### 开发者体验
- **TypeScript 优先** - 包含完整的类型定义
- **流式 API** - 直观的配置构建器
- **Express 中间件** - 即插即用的认证
- **HTTP 客户端** - 自动请求签名
- **CLI 工具** - 轻松生成凭证
### 生产就绪
- **零停机凭证轮换** - 为更新提供过渡期
- **结构化日志** - 全面的可观测性
- **指标收集** - 跟踪认证性能
- **环境配置** - 从环境变量加载
- **全面的错误处理** - 详细的错误信息
### 构建与分发
- **双模块支持** - ESM 和 CommonJS
- **可摇树优化** - 按需导入
- **零依赖** - 仅使用 Node.js 内置模块
- **经过充分测试** - 318 个测试,90%+ 覆盖率
## 使用场景
### 1. 服务间认证
使用 MFA 保护微服务通信:
```
const config = new QiuthConfigBuilder()
.withApiKey(process.env.API_KEY)
.withIpAllowlist(['10.0.0.0/8']) // Internal network
.withTotp(process.env.TOTP_SECRET)
.build();
```
### 2. API 密钥管理
为你现有的 API 密钥系统添加 MFA:
```
app.use('/api', createQiuthMiddleware({ config }));
```
### 3. 合规性要求
满足 PCI DSS、SOC 2、HIPAA 安全要求:
```
const config = new QiuthConfigBuilder()
.withApiKey(apiKey)
.withIpAllowlist(allowedIps)
.withTotp(totpSecret)
.withCertificate(publicKey) // Maximum security
.build();
```
### 4. CI/CD 流水线安全
保护自动化部署安全:
```
// GitHub Actions, Jenkins, etc.
const client = new QiuthClient({
apiKey: process.env.API_KEY,
totpSecret: process.env.TOTP_SECRET,
privateKey: process.env.PRIVATE_KEY,
});
```
## 安全性
Qiuth 的设计以安全性为首要任务:
- **不记录敏感数据** - API 密钥和 secret 绝不会被记录
- **加密安全** - 使用 Node.js crypto 模块
- **兼容 RFC 标准** - TOTP 遵循 RFC 6238 标准
- **行业标准** - RSA-SHA256 签名
- **定期审计** - 自动化的安全扫描
## 性能
Qiuth 专为生产环境使用而设计,开销极低:
- **IP 验证**:< 1ms
- **TOTP 验证**:< 5ms
- **证书验证**:< 10ms
- **总计**:所有三层 < 20ms
**包体积:**
- ESM:~60 KB
- CommonJS:~61 KB
- TypeScript 声明文件:~48 KB
**别再把 API 密钥当成普通密码了。今天就加入多因素认证吧。**
[开始使用](./docs/getting-started.md) • [尝试演示](./demo/README.md) • [自述文件](https://github.com/clay-good/qiuth/readme.md)
标签:API安全, JSONLines, JSON输出, MFA, MITM代理, 网络访问控制, 自动化攻击, 零信任