RN与Metro
一、名词解释
UI = 用户界面,就是用户在屏幕上看到、能操作的那一层画面
UI 组件库 = 把界面拆成可复用零件(按钮、输入框、列表)的工具箱
UI 框架=帮你做 UI 的整套方案(React、Vue)
React = 一个用 JavaScript 写 UI 的库/框架,帮你高效地把组件拼成完整界面
React Native是基于 React 做移动 App 的"框架
React Native 是一个让你用 JavaScript 写手机 App 的框架——写一套代码,同时生成 iOS App 和 Android App,而且界面是真原生 UI,不是网页。
二、RN 的开发流程(为什么需要 Metro)
开发者在电脑写 JS 代码
↓
Metro Bundler 把 JS 打包成 index.android.bundle / index.ios.bundle
↓
手机/模拟器通过 http://开发者电脑IP:8087/index.bundle 拉取
↓
App 内的 JS 引擎(JSC / Hermes)执行这个 bundle
↓
JS 调用原生组件渲染界面 ←→ Bridge 双向通信
**bundle 指的是:把多个 JS/CSS/资源文件打包成少数几个文件**
- **开发时**:Metro 当"实时服务器",你改代码它重新打包,手机刷新就看到效果(热更新)
- **发布时**:Metro 把 bundle 预先打包塞进 App 包,App 离线也能跑
**本次告警的 8087 端口,就是 Metro 在"开发时服务器模式"下监听的端口**。
三、RN 的优缺点(对你理解安全测试的意义)
### ✅ 优点
- **一套代码,两端运行**:开发效率高
- **热更新**:改代码不用重装 App,绕过商店审核(这也是 Metro 的能力)
- **生态成熟**:npm 上海量 JS 包可用,前端工程师上手快
### ⚠️ 缺点(也是安全风险来源)
- **JS 代码可被反编译**:bundle 是 JS,比原生 so 难做代码保护,容易被逆向
- **依赖 JS 引擎**:启动时需要加载 bundle,有冷启动开销
- **Bridge 通信有开销**:JS 和原生之间频繁通信有性能损耗(新版有 JSI 优化)
- **开发调试工具默认不设防**:像 Metro 这种调试服务器,设计时假设"只有开发者自己用",所以无鉴权、监听 0.0.0.0 → **这就是本次目录遍历漏洞的根源**
Metro 是 React Native 的构建工具 / 模块打包器(bundler)
Metro 指纹识别:(看请求的响应)
HTTP/1.1 200 OK
X-Metro-Files-Changed-Count: 0
X-Metro-Delta-ID: 8ad1a086a901b93a
Content-Location: http://127.0.0.1:8087/index.bundle
Content-Type: application/javascript; charset=UTF-8
Last-Modified: Thu, 13 Aug 2026 06:19:32 GMT
Date: Thu, 13 Aug 2026 10:59:09 GMT
Connection: keep-alive
Keep-Alive: timeout=5
Content-Length: 21932
可以看到:(明显的Metro指纹)
X-Metro-Files-Changed-Count: 0
X-Metro-Delta-ID: 8ad1a086a901b93a
再叠加上:
Content-Type: application/javascript; charset=UTF-8
Connection: keep-alive
Keep-Alive: timeout=5
以及url:
/index.bundle
这是 100% Metro,置信度接近 100%。
四、Metro Dev Server 四层路由架构详解
第 1 层:Metro 核心打包路由
这是 Metro 的"本职工作"——把 JS 源码打包成 bundle,提供静态资源。
| 路由 | 功能 | 实测状态 |
|---|---|---|
| GET /index.bundle | 动态打包 JS,bundle 是 App 运行的核心代码 | ✅ 200,22KB |
| GET /index.bundle?platform=ios&dev=true | 按平台/模式打包 | ✅ 200 |
| GET /assets/{path} | 提供静态资源文件(图片等) | ✅ 500(空项目) |
| POST /symbolicate | 符号化崩溃堆栈(把地址翻译成代码行号) | ✅ 500(需 JSON) |
安全攻击面:
- 目录遍历:/assets/../../../../etc/passwd —— 就是本次告警的漏洞点!这层负责读文件,如果路径校验缺失,就能穿越读任意文件
- Bundle 投毒:如果攻击者能篡改 bundle 内容,App 加载后等于执行了攻击者的 JS 代码
- Symbolicate 任意文件读:/symbolicate 的 POST body 里 file 字段直接被 path.resolve(projectRoot, file) + readFileSync 拼接,如果 file 含 ../ 也可能穿越(我们之前在源码里看到过)
我们裸 metro 环境(场景A)只有这第 1 层,所以你看到 /health、/debugger-ui 都是 404 —— 因为第 2 层没装。
🏢 第 2 层:RN CLI 注入的 Dev Middleware
React Native CLI 启动 Metro 时,会往 Metro 的 HTTP 服务器里注入额外的调试路由。这些路由不在 metro 核心包里,而是 @react-native-community/cli-plugin 注册的。
| 路由 | 功能 | 我们裸 metro 上 |
|---|---|---|
| GET / | 返回 "React Native packager is running" 欢迎页 | ❌ 404 |
| GET /status | 返回打包状态(packaging / done) | ❌ 404 |
| GET /health?platform=ios | 返回 200 / 400,用于 CI 环境检查 | ❌ 404 |
| GET /debugger-ui | 远程调试器的网页界面 | ❌ 404 |
| GET /debugger-ui/index.html | 同上 | ❌ 404 |
| POST /symbolicate | 被这层包装过,加了更好的错误处理 | - |
这就是为什么告警那台 Metro 的 GET / 返回 32 字节 "React Native packager is running",而我们裸 metro 返回 404 "Cannot GET /" —— 差的就是这第 2 层。
安全攻击面:
- /debugger-ui 远程调试:这个页面允许任何能访问 8087 的人打开 Chrome DevTools 连到 App 调试 → 可以实时查看 App 状态、执行 JS 代码
- /health 信息泄露:返回框架信息、平台、版本等
- /status 状态泄露:暴露打包进度、文件列表
📦 第 3 层:HMR / WebSocket 通道
干什么:Hot Module Replacement(热模块替换),让你改代码后 App 自动刷新,不用手动重新加载。通信方式是 WebSocket。
| 通道 | 功能 |
|---|---|
| ws://IP:8087/ws | WebSocket 主通道,双向通信 |
| HMR 消息 | 服务端推送"哪些模块变了" → App 只替换变化的模块 |
| Log 消息 | App 内的 console.log 通过这个通道推到开发者的终端 |
安全攻击面:
- WebSocket 无鉴权:任何能连到 8087 的人都能开 WebSocket 连接,冒充 App 接收 HMR 推送
- 伪造 HMR 推送:攻击者连上 WebSocket 后,伪造一个"模块更新"消息 → App 加载攻击者的 JS → 等同于在用户手机上执行任意代码
- 劫持 console 输出:App 的日志(可能含 token、用户数据)通过 WebSocket 明文传输 → 可被嗅探
🔍 第 4 层:Inspector Proxy
干什么:让开发者用 Chrome DevTools / VS Code 远程调试 App 里的 JS 代码(设断点、看变量、单步执行)。
| 通道 | 功能 |
|---|---|
| ws://IP:8087/inspector | Inspector Proxy 的 WebSocket 通道 |
| Chrome DevTools Protocol | 通过 CDP 协议远程控制 JS 执行 |
安全攻击面:
- 远程代码执行:Inspector 允许 Runtime.evaluate 命令 → 在 App 的 JS 上下文里执行任意 JS 代码
- 读取 App 内存:通过 Inspector 可以读取所有 JS 变量 → token、密码、用户数据
- 无鉴权:任何能访问 Inspector 端口的人都能连 → 完全控制 App 的 JS 运行时
更多推荐


所有评论(0)