itsjustshubh/Phia
GitHub: itsjustshubh/Phia
揭示生产环境中5个未认证API端点的安全风险与修复方案。
Stars: 0 | Forks: 0
# Phia API — 研究
**作者:** Shubh Thorat · **日期:** 2026年4月11日 · **目标:** `api.phia.com`
## 我发现了什么
**5 个端点在生产环境中无需身份验证即可接受请求。**
最关键的一个是 `CreateMerchantUrl`:它会为**任意**用户的账户生成关联追踪链接,且不需要令牌。我传入一个伪造的 UUID,却得到了一个实时的关联 URL:
```
curl -X POST https://api.phia.com/v2/graphql \
-H "Content-Type: application/json" \
-H "x-platform: EXTENSION" \
-d '{
"operationName": "CreateMerchantUrl",
"variables": { "input": {
"phiaId": "00000000-0000-0000-0000-000000000000",
"url": "https://poshmark.com/listing/any-item",
"platform": "EXTENSION", "searchId": "test",
"productId": "ENTITY_TYPE_PRODUCT-abc123",
"secondhandRetailerId": "POSHMARK"
}},
"query": "mutation CreateMerchantUrl($input: CreateMerchantUrlInput!) { createMerchantUrl(input: $input) { id url } }"
}'
```
**响应 —— `200 OK`,未进行身份验证检查:**
```
{
"data": {
"createMerchantUrl": {
"id": "1516e925-3864-43a1-ab2a-102a8ff0ebef",
"url": "https://www.anrdoezrs.net/click-101090659-15736479?url=..."
}
}
}
```
任何人都可以涌入关联点击,污染任意用户的分析数据,并无限调用 CJ 和 Impact.com —— 而这一切都不需要会话。
## 所有发现
| # | 端点 | 严重程度 | 问题描述 |
| --- | ------------------------- | -------- | ---------------------------------------------------------------------------------------------- |
| 1 | `CreateMerchantUrl` | **严重** | 无身份验证,无 `phiaId` 归属检查 —— 大规模的关联欺诈 |
| 2 | `CreateResaleInsightsUrl` | 高 | 无身份验证 —— `link` 参数原样反射到输出 URL |
| 3 | `GET /whitelist/validate` | 高 | `phiaId` 以 URL 查询参数形式暴露 —— 泄露到服务器日志、浏览器历史记录和 Referer 头部 |
| 4 | 分析令牌 | 中 | Mixpanel 令牌硬编码在公共的 `background.js` 中 —— 任何人都可注入伪造事件 |
| 5 | GraphQL 自省 | 低 | `__schema` 返回 `500`,而非干净的禁用错误 |
## 负载测试
编写了一个 [Locust](https://locust.io) 脚本(`load_test.py`),并发地对全部 5 个未认证端点发起请求,针对生产环境进行测试。
| 端点 | 请求数 | 失败数 | 中位延迟 | p95 延迟 | p99 延迟 | 最大延迟 | 平均大小 |
| ------------------------- | :----: | :----: | -----------: | ------------: | ---------------: | ---------------: | --------: |
| `CreateMerchantUrl` | 19 | **0** | 210ms | 660ms | 660ms | 657ms | 314 B |
| `CreateResaleInsightsUrl` | 25 | **0** | 44ms | 58ms | 130ms | 127ms | 361 B |
| `GET /whitelist/validate` | 4 | **0** | 150ms | 180ms | 180ms | 177ms | 206 B |
| `GenerateEntityId` | 7 | **0** | 44ms | 49ms | 49ms | 49ms | 69 B |
| `SiteConfig` | 10 | **0** | 570ms | 1100ms | 1100ms | 1148ms | 63 B |
| **汇总** | **65** | **0** | **130ms** | **590ms** | **1100ms** | **1148ms** | **260 B** |
**65 次请求,0 次失败。每次调用都返回真实数据,无需身份验证。**
平均大小这一列证实了这些并非空错误。`CreateMerchantUrl` 每次调用返回 **314 字节的实时关联数据**。`SiteConfig` 在少量流量下达到 **1148 毫秒** —— 每次调用都会链式调用两个 gRPC 向下游。在真实负载下,这将成为第一个瓶颈。
完整报告:[`Report/Locust.html`](Report/Locust.html)
## 如何修复
**1. 在 `CreateMerchantUrl` 上要求身份验证 + 归属检查**
```
@Authorized()
async createMerchantUrl(input, context) {
if (input.phiaId !== context.user.phiaId)
throw new ForbiddenError('phiaId mismatch');
}
```
**2. 将 `phiaId` 移出 URL 参数** — 使用 GraphQL 请求中已存在的 `x-phia-id` 头部。永远不要将永久用户标识符放在查询字符串中。
**3. 轮换并代理分析令牌** — `0707dbc4f9ae6bc96db7b740d1c4911a` 存在于公共扩展包中。轮换它,并在服务器端代理事件,使令牌永不触达客户端。
**4. 禁用生产环境中的自省功能**
```
new ApolloServer({ introspection: process.env.NODE_ENV !== "production" });
```
**5. 对未认证端点实施速率限制** — 每 IP 至少每分钟 30 次请求。
## 方法论
未使用专有工具 —— 仅依靠 DevTools、Python、curl 和 Locust。
```
Chrome DevTools → HAR export (597 entries captured)
Python → parsed every request, isolated unauthenticated endpoints
curl → manually confirmed each finding against production
Locust → load_test.py — concurrent load across all 5 endpoints
```
---
欢迎在 staging 环境逐步验证或进行更深入的测试。
| 端点 | 请求数 | 失败数 | 中位延迟 | p95 延迟 | p99 延迟 | 最大延迟 | 平均大小 |
| ------------------------- | :----: | :----: | -----------: | ------------: | ---------------: | ---------------: | --------: |
| `CreateMerchantUrl` | 19 | **0** | 210ms | 660ms | 660ms | 657ms | 314 B |
| `CreateResaleInsightsUrl` | 25 | **0** | 44ms | 58ms | 130ms | 127ms | 361 B |
| `GET /whitelist/validate` | 4 | **0** | 150ms | 180ms | 180ms | 177ms | 206 B |
| `GenerateEntityId` | 7 | **0** | 44ms | 49ms | 49ms | 49ms | 69 B |
| `SiteConfig` | 10 | **0** | 570ms | 1100ms | 1100ms | 1148ms | 63 B |
| **汇总** | **65** | **0** | **130ms** | **590ms** | **1100ms** | **1148ms** | **260 B** |
**65 次请求,0 次失败。每次调用都返回真实数据,无需身份验证。**
平均大小这一列证实了这些并非空错误。`CreateMerchantUrl` 每次调用返回 **314 字节的实时关联数据**。`SiteConfig` 在少量流量下达到 **1148 毫秒** —— 每次调用都会链式调用两个 gRPC 向下游。在真实负载下,这将成为第一个瓶颈。
完整报告:[`Report/Locust.html`](Report/Locust.html)
## 如何修复
**1. 在 `CreateMerchantUrl` 上要求身份验证 + 归属检查**
```
@Authorized()
async createMerchantUrl(input, context) {
if (input.phiaId !== context.user.phiaId)
throw new ForbiddenError('phiaId mismatch');
}
```
**2. 将 `phiaId` 移出 URL 参数** — 使用 GraphQL 请求中已存在的 `x-phia-id` 头部。永远不要将永久用户标识符放在查询字符串中。
**3. 轮换并代理分析令牌** — `0707dbc4f9ae6bc96db7b740d1c4911a` 存在于公共扩展包中。轮换它,并在服务器端代理事件,使令牌永不触达客户端。
**4. 禁用生产环境中的自省功能**
```
new ApolloServer({ introspection: process.env.NODE_ENV !== "production" });
```
**5. 对未认证端点实施速率限制** — 每 IP 至少每分钟 30 次请求。
## 方法论
未使用专有工具 —— 仅依靠 DevTools、Python、curl 和 Locust。
```
Chrome DevTools → HAR export (597 entries captured)
Python → parsed every request, isolated unauthenticated endpoints
curl → manually confirmed each finding against production
Locust → load_test.py — concurrent load across all 5 endpoints
```
---
欢迎在 staging 环境逐步验证或进行更深入的测试。标签:0失败请求, 65请求, API安全, API测试, CreateMerchantUrl, CURL, GraphQL, HAR分析, JSON输出, Locust压力测试, MITM代理, 分析污染, 后端开发, 无认证端点, 未授权访问, 漏洞披露, 生产环境, 用户账户劫持, 端点枚举, 补丁建议, 负载测试, 逆向工具, 链接反射, 附属欺诈