mirror of
https://github.com/This-weiyuqing/mi-fitness-health.git
synced 2026-10-18 03:03:49 +08:00
feat(security): privacy hardening + data export (v0.2.1)
- CORS: default localhost-only origins (was *, allowed any webpage to read local health data cross-site); env override MI_FITNESS_CORS_ORIGINS - Host header allowlist middleware on FastAPI and Flask (DNS-rebinding protection); auto-configured for non-loopback binds via CLI - Move API key passTokens from SQLite plaintext to system keyring, with automatic migration on startup and cleanup for revoked keys - Disable uvicorn access log by default (query params out of terminals) - New GET /api/export: JSON/CSV export of local data (offline, Excel BOM) - Add docs/PRIVACY.md: full data-flow audit, outbound endpoint inventory, threat model, self-verification steps - 6 new tests (host allowlist, keyring roundtrip, CSV)
This commit is contained in:
1 parent
42c42b2c9e
commit
a47a8470f8
10 files changed
+444
-13
No files matched your search
@@ -0,0 +1,92 @@
|
||||
# 隐私与数据安全审计
|
||||
|
||||
> 本文回答一个问题:**我的健康数据到底去了哪里?**
|
||||
> 结论先行:本项目是"拉取式"本地工具——数据只从小米云**下载**到你的电脑,除小米自己的服务器外**不上传到任何第三方**;无遥测、无统计、无崩溃上报;查询功能完全离线可用。
|
||||
|
||||
---
|
||||
|
||||
## 1. 数据流向总览
|
||||
|
||||
```
|
||||
小米手环/手表 ──蓝牙──> 小米运动健康 App ──> 小米健康云(小米官方,与本项目无关)
|
||||
│
|
||||
│ ① 拉取(登录 + 分页下载,RC4 加密通道)
|
||||
▼
|
||||
┌──────────── 本项目(你的电脑)────────────┐
|
||||
│ SQLite(健康数据) │
|
||||
│ 系统 keyring(passToken / API Key 密钥) │
|
||||
│ config.json(无敏感信息) │
|
||||
└───────────────────────────────────────────┘
|
||||
│ ② 读取(纯本地,断网可用)
|
||||
├──> MCP 客户端(stdio,本机进程)
|
||||
├──> REST API(默认 127.0.0.1)
|
||||
└──> Web 仪表盘(默认 127.0.0.1)
|
||||
```
|
||||
|
||||
**唯一出网的方向是 ①:从小米云拉你自己的数据。** 没有任何"上传健康数据"的代码路径。
|
||||
|
||||
## 2. 全部对外网络请求清单(代码审计)
|
||||
|
||||
对 `src/` 全量 grep `https://` 的结果,所有外联域名均属小米:
|
||||
|
||||
| 端点 | 用途 | 方向 |
|
||||
|---|---|---|
|
||||
| `https://account.xiaomi.com/pass/serviceLogin` | passToken 换服务会话 | 请求小米 |
|
||||
| `https://account.xiaomi.com/longPolling/loginUrl` | 扫码登录:生成二维码会话 | 请求小米 |
|
||||
| `https://hlth.io.mi.com` / `https://{region}.hlth.io.mi.com` | 健康数据分页拉取 | 请求小米 |
|
||||
| `https://sts.api.io.mi.com/sts` | 仅作为扫码接口的 callback **参数**(小米校验用),本项目不请求它 | — |
|
||||
| `https://127.0.0.1:8321` | Web 仪表盘反代到本机 API | 仅本机 |
|
||||
|
||||
扫码登录流程中由**小米动态下发**的跳转地址(`c3.account.xiaomi.com`、`c3.lp.account.xiaomi.com` 等)也全部位于 `*.xiaomi.com` 小米域名下。
|
||||
|
||||
**不存在**:遥测(telemetry)、统计(analytics)、Sentry/PostHog/Mixpanel 等上报 SDK、GitHub star 上报、版本检查回传、任何第三方域名。可用以下命令自行复核:
|
||||
|
||||
```bash
|
||||
grep -rn "https\?://" src/ --include="*.py" | grep -v 127.0.0.1 # 应只见 *.xiaomi.com / *.io.mi.com
|
||||
grep -rniE "telemetry|analytics|sentry|posthog|mixpanel" src/ # 应无结果
|
||||
```
|
||||
|
||||
## 3. 数据在本机的存放位置
|
||||
|
||||
| 数据 | 位置 | 保护 |
|
||||
|---|---|---|
|
||||
| 健康数据(8 类) | `<用户数据目录>/mi-fitness-mcp/mi_fitness.db`(SQLite) | 本机文件权限;不上传 |
|
||||
| 小米 passToken(setup 凭据) | 系统 keyring(Windows: DPAPI 加密 / macOS: Keychain / Linux: Secret Service) | OS 级加密 |
|
||||
| API Key 对应的 passToken | 系统 keyring(v0.2.1 起;旧版本曾存 SQLite 明文,启动时自动迁移并清空) | OS 级加密 |
|
||||
| `mif_sk_*` Key 本身、user_id、region、发放/使用时间 | SQLite `api_keys` 表 | 无秘密性要求(可吊销) |
|
||||
| 配置 | `<用户配置目录>/mi-fitness-mcp/config.json` | 不含凭据 |
|
||||
| 仓库内 | 无任何上述文件(`.gitignore` 排除 `.venv/ *.db .env config.json` 等) | — |
|
||||
|
||||
## 4. 本地暴露面与防护(v0.2.1 加固)
|
||||
|
||||
数据"不上传"之外,还要防**被动窃取**。本项目默认只绑 `127.0.0.1`,针对本机攻击面的防护:
|
||||
|
||||
| 威胁 | 场景 | 防护 |
|
||||
|---|---|---|
|
||||
| 恶意网页跨站读取 | 你浏览器里开着的任意网页用 JS 请求 `http://127.0.0.1:8321` 并读取响应 | CORS 默认只允许 `localhost/127.0.0.1/[::1]` 来源(正则限定,任意端口),其他域名一律无 CORS 头,浏览器阻止读取 |
|
||||
| DNS 重绑定 | 攻击者域名解析到 127.0.0.1 绕过同源策略 | **Host 头白名单中间件**(FastAPI 与 Flask 双侧):Host 非本机域名直接 403 |
|
||||
| 本机其他进程读取 | 同机运行的任何程序请求本地 API | 设置环境变量 `MI_FITNESS_API_KEY` 强制鉴权;管理端点默认仅本机且可设 `MI_FITNESS_ADMIN_KEY` |
|
||||
| 凭据落盘明文 | SQLite 被复制/同步盘上传 | passToken 全部走系统 keyring;`api_keys` 表启动时自动迁移历史明文并清空 |
|
||||
| 日志泄密 | 终端/日志文件留下查询参数(日期等) | `api` 命令默认关闭 uvicorn 访问日志 |
|
||||
| 扫码端点滥用 | 他人把他的账号"登录"到你的服务器 | 扫码/Key 管理端点要求 `X-Admin-Key`(未设则仅限 127.0.0.1) |
|
||||
|
||||
环境变量开关(见 `.env.example`):`MI_FITNESS_ALLOWED_HOSTS`(Host 白名单,非回环绑定时 CLI 自动设置)、`MI_FITNESS_CORS_ORIGINS`(显式放开跨域来源,慎用 `*`)。
|
||||
|
||||
## 5. 如何自行验证
|
||||
|
||||
1. **断网测试**:拔网线后所有查询端点、`/api/export`、Web 仪表盘图表照常工作(同步/扫码会失败,属预期)——证明查询链路纯本地。
|
||||
2. **抓包**:`netstat`/Wireshark 观察进程外联,目标应只有 `*.xiaomi.com` / `*.io.mi.com`。
|
||||
3. **代码审计**:第 2 节的两条 grep 命令;全部网络代码集中在 `adapters/mi_fitness_cloud.py`、`api.py`(扫码)、`web.py`(本机反代)。
|
||||
4. **仓库审计**:`git ls-files | grep -iE "\.db|\.sqlite|config\.json|\.env$"` 应无结果。
|
||||
|
||||
## 6. 明确不做的事
|
||||
|
||||
- ❌ 不上传、不转发、不备份健康数据到任何第三方服务
|
||||
- ❌ 不内置任何遥测、统计、崩溃上报
|
||||
- ❌ 不在代码仓库或文档中存放任何凭据
|
||||
- ❌ 不写入小米账号(全协议只读;唯一"写"是登录换 token,属认证必需)
|
||||
|
||||
## 7. 边界说明(与本项目无关的上传)
|
||||
|
||||
- 手环 → 小米云的上传由**小米官方 App**完成,这是设备生态本身的行为;本项目只读取。介意者请在小米 App 内管理同步策略。
|
||||
- 你若把 SQLite 文件自行放进网盘/同步盘,属于你自己的操作,请自行加密。
|
||||
Reference in new issue
Block a user