release: harden Android network security

This commit is contained in:
key
2026-07-29 01:13:18 +08:00
parent 9ea5d79a62
commit ba68fc8326
16 changed files with 198 additions and 29 deletions
+3 -1
View File
@@ -24,6 +24,8 @@
同日,本机 Android Studio `Medium_Phone` 虚拟机完成 Android 15/API 35 启动,物理分辨率 `1080×2400`、密度 `420`。在虚拟机此前未安装本应用的前提下,`0.1.6 (106)` 首次安装成功并从系统 Launcher 冷启动,`am start -W` 记录总耗时约 `6.4s`;最新包真实显示原生《服务协议和隐私政策》弹窗,四项相机、录音、图片和视频权限均为 `granted=false`,crash 缓冲区为空。截图和 UI 层级保存在忽略目录 `output/aug1-rc/android-emulator/`;验收未代替用户点击同意或拒绝。虚拟机证据用于补充干净安装和 API 35 权限基线,不能替代真实设备的媒体、WebRTC、弱网和系统浏览器交互。
同日,候选包继续升级为 `0.1.8 (108)`,大小 `33,921,705` 字节,SHA-256 为 `E252140645F3AAD14B3594FD78E76BDF1A2122F7DC70EBD21CC5DF53223C7878`。新增的项目根原生清单与 `network_security_config.xml` 已进入实际云打包输入,193 项移动端测试、类型检查、App 构建及联网法务预检通过;APK 反编译确认包内资源与当前编译产物一致、`usesCleartextTraffic=false`、`networkSecurityConfig` 引用、系统证书信任配置、签名和敏感值扫描均通过。虚拟机卸载旧实例后全新安装,Launcher 冷启动总耗时 `2.904s`,最新原生隐私弹窗真实显示,四项敏感权限均为 `granted=false`,crash 缓冲区为空。仍未代替用户点击拒绝或同意。
## 已完成的 Android 基座证据
- [x] 自定义基座包名、版本、API 35、单签名、v2 签名和 SHA-256 可重复校验。
@@ -33,7 +35,7 @@
- [x] 独立 DCloud 云证书内测 APK 的包内法务配置、API 35、单签名、v2 签名和 SHA-256 可重复校验。
- [x] 清除本 App 测试数据后首次启动先显示原生隐私弹窗,服务协议与隐私政策链接均可在系统浏览器打开。
- [x] `0.1.6 (106)` 当前候选包完成同签名覆盖安装、冷启动和启动权限复核,包内资源与当前 App-Plus 编译产物一致。
- [x] `0.1.6 (106)` 在本机 Android 15/API 35 虚拟机完成干净安装和冷启动,最新包原生隐私弹窗真实显示,四项敏感权限保持未授权且未见崩溃。
- [x] `0.1.8 (108)` 在本机 Android 15/API 35 虚拟机完成卸载重装和冷启动,最新包原生隐私弹窗真实显示,四项敏感权限保持未授权、网络安全配置已进入 APK 且未见崩溃。
## Android(最低 Android 10 + 一台当前主流机型)
+6 -5
View File
@@ -42,13 +42,14 @@ npm --prefix mobile-uni run build:app
npm --prefix mobile-uni run verify:app-release
```
`configure:app-privacy` 会同时校验 HTTPS、公网域名、两个地址互不相同,并从模板生成被 Git 忽略的 `mobile-uni/src/androidPrivacy.json`;`build:app` 再将其复制到 `dist/build/app/androidPrivacy.json`。这两个位置都纳入门禁,避免 npm/Vite CLI 项目把配置误写到工程根目录而产生假绿。`verify:app-release` 还会确认:
`configure:app-privacy` 会同时校验 HTTPS、公网域名、两个地址互不相同,并从模板生成被 Git 忽略的 `mobile-uni/src/androidPrivacy.json`;`build:app` 再将其复制到 `dist/build/app/androidPrivacy.json`,并把项目根的 `AndroidManifest.xml` 与 `nativeResources/android/res/xml/network_security_config.xml` 同步到云打包输入目录。源码和编译产物都纳入门禁,避免 npm/Vite CLI 项目把原生配置留在源码但未进入安装包而产生假绿。`verify:app-release` 还会确认:
- 首次与二次隐私弹窗使用同一组地址;
- 原生弹窗地址与本次 App 编译使用的 `VITE_APP_TERMS_URL`、`VITE_APP_PRIVACY_URL` 完全一致;
- 两个页面可通过公网 HTTPS 访问并包含可识别的协议正文;
- `VITE_DEV_SMS_HINT_ENABLED` 与 `VITE_DEV_SMS_HINT_VALUE` 均未进入发布环境;
- 编译产物不含手机号、固定测试验证码、私钥或常见云端/API 令牌格式。
- Android 清单与网络安全配置均禁止明文 HTTP,并只信任系统证书。
登录页默认不勾选协议。协议地址缺失或不合规时,页面允许查看但禁止发送验证码和登录;测试验证码快捷提示只有开发环境同时显式设置 `VITE_DEV_SMS_HINT_ENABLED=true` 和本机 `VITE_DEV_SMS_HINT_VALUE` 才显示。源码不再内置固定验证码值。
@@ -64,23 +65,23 @@ HBuilderX 5.22 当前实测中,自定义调试基座不支持安心打包,
```powershell
& '.\mobile-uni\scripts\verify-android-apk.ps1' `
-ApkPath '.\mobile-uni\dist\debug\bangdao-0.1.6-dcloud-test.apk' `
-ApkPath '.\mobile-uni\dist\debug\bangdao-0.1.8-dcloud-test.apk' `
-BuildKind dcloud-test
```
APK 门禁还会比较包内与当前 `dist/build/app/app-service.js` 的 SHA-256,避免用同版本号的旧包冒充当前源码,并在反编译结果中复查敏感文件、手机号、固定测试验证码、私钥和常见令牌格式。
APK 门禁还会比较包内与当前 `dist/build/app/app-service.js` 的 SHA-256,避免用同版本号的旧包冒充当前源码,并在反编译结果中复查敏感文件、手机号、固定测试验证码、私钥和常见令牌格式;`dcloud-test` 与 `release` 模式都拒绝 `usesCleartextTraffic=true`。
完成代码评审并提交冻结后,从仓库根目录执行统一 RC 证据脚本。默认拒绝脏工作区;`-AllowDirtyRehearsal -SkipBuild` 只能用于验证脚本和 APK,生成的证据会明确标记为不可发布:
```powershell
.\scripts\prepare-aug1-rc.ps1 `
-ApkPath '.\mobile-uni\dist\debug\bangdao-0.1.6-dcloud-test.apk' `
-ApkPath '.\mobile-uni\dist\debug\bangdao-0.1.8-dcloud-test.apk' `
-BuildKind dcloud-test
```
脚本会复跑移动端测试/类型检查/H5/App 构建、管理端构建、后端定向测试和打包,验证 APK 后在被 Git 忽略的 `output/aug1-rc/` 生成标准文件名、哈希及 JSON/Markdown 证据。它不执行生产同步、数据库迁移、服务重启或用户隐私选择。
2026-07-28 最终自定义基座大小为 `21,158,193` 字节,SHA-256 为 `82D3352FF96A86F5DB4AC0C71DCB0ED6CEC98F2A785F6EAED2FE8D4FC0C5F704`。2026-07-29 当前独立内测候选包为 `bangdao-0.1.6-dcloud-test.apk`:包名 `com.yincheng.wygj`、版本 `0.1.6 (106)`、`targetSdkVersion=35`,大小 `33,921,279` 字节,SHA-256 为 `A5F026B70F11327355597B9FEA3625C4168700297E6D7847F10B079CEB860914`,签名证书 SHA-256 为 `AB06ECC0DE544DE73C385F3F0690B22966EBC12887FB8BEE1830CDBEFDEBEF5D`。APK 反编译门禁已确认包内业务资源等于当前 App-Plus 编译产物,法务配置和敏感值扫描通过;同签名覆盖安装和冷启动成功,启动后敏感权限保持未授权且未见 `FATAL EXCEPTION`。前一候选包的干净启动已显示原生 template 隐私弹窗,两个链接均通过系统浏览器打开线上正文。该包使用 DCloud 云端证书,且 DCloud 运行壳仍声明允许明文流量,只能作为 8 月 1 日受控内测包;企业正式 release 门禁仍会拒绝明文流量和非企业签名。
2026-07-28 最终自定义基座大小为 `21,158,193` 字节,SHA-256 为 `82D3352FF96A86F5DB4AC0C71DCB0ED6CEC98F2A785F6EAED2FE8D4FC0C5F704`。2026-07-29 当前独立内测候选包为 `bangdao-0.1.8-dcloud-test.apk`:包名 `com.yincheng.wygj`、版本 `0.1.8 (108)`、`targetSdkVersion=35`,大小 `33,921,705` 字节,SHA-256 为 `E252140645F3AAD14B3594FD78E76BDF1A2122F7DC70EBD21CC5DF53223C7878`,签名证书 SHA-256 为 `AB06ECC0DE544DE73C385F3F0690B22966EBC12887FB8BEE1830CDBEFDEBEF5D`。APK 反编译门禁已确认包内业务资源等于当前 App-Plus 编译产物,法务配置、敏感值扫描、`usesCleartextTraffic=false`、`networkSecurityConfig` 引用和系统证书信任配置均通过;本机 Android 15/API 35 虚拟机完成卸载重装、冷启动和权限基线复核,总启动耗时约 `2.9s`,四项敏感权限保持未授权且 crash 缓冲区为空。该包仍使用 DCloud 云端测试证书,只能作为 8 月 1 日受控内测包;企业正式 release 还必须使用签认后的企业证书摘要。
企业正式签名 APK 还必须用发布负责人保管的证书摘要执行 release 模式核验;该模式会额外拒绝 `debuggable=true`、明文 HTTP 和签名摘要不一致:
@@ -14,17 +14,17 @@
## 已通过
| 检查项 | 2026-07-28 结果 |
| 检查项 | 当前结果 |
|---|---|
| 仓库同步 | 本地 `main` 与 `origin/main` 均为 `9ae5f750`,前后差异 `0/0` |
| 移动端自动化 | 191 项单元测试通过;TypeScript 类型检查通过 |
| 移动端自动化 | 193 项单元测试通过;TypeScript 类型检查通过 |
| 构建 | `mobile-uni` H5、App-Plus、管理端生产构建通过;后端 `ruoyi-admin.jar` 重建通过 |
| P0 后端语义 | `AihrSopSeedServiceTest` 39 项通过;`ruoyi-aihr` 696 项全量测试通过,0 失败、0 错误 |
| App 资源门禁 | 正式法务 URL、原生隐私配置、API 35、图标和启动图检查通过 |
| 独立 APK | `com.yincheng.wygj`,`0.1.6 (106)`,`targetSdkVersion=35`,v2 签名;SHA-256 `A5F026B70F11327355597B9FEA3625C4168700297E6D7847F10B079CEB860914` |
| 独立 APK | `com.yincheng.wygj`,`0.1.8 (108)`,`targetSdkVersion=35`,v2 签名,明文 HTTP 关闭且网络安全资源已进入 APK;SHA-256 `E252140645F3AAD14B3594FD78E76BDF1A2122F7DC70EBD21CC5DF53223C7878` |
| APK 内容门禁 | 包内业务资源等于当前 App-Plus 编译产物;法务链接、手机号、固定测试验证码、私钥和常见令牌扫描通过 |
| 真机安装启动 | vivo V2443A(Android 15)完成 `0.1.6` 覆盖安装和冷启动;相机、录音、媒体权限保持未授权,未见 `FATAL EXCEPTION` |
| 虚拟机干净安装 | 本机 `Medium_Phone` Android 15/API 35 完成 `0.1.6` 首次安装和冷启动;最新包真实显示原生隐私弹窗,四项敏感权限未授权,crash 缓冲区为空 |
| 虚拟机干净安装 | 本机 `Medium_Phone` Android 15/API 35 完成 `0.1.8` 卸载重装和冷启动;最新包真实显示原生隐私弹窗,四项敏感权限未授权,crash 缓冲区为空 |
| 首次隐私弹窗 | 最新包已在虚拟机干净启动显示原生弹窗;前一候选包的两个 HTTPS 链接已在 vivo 系统浏览器打开,仍需用户亲自执行拒绝/同意反例 |
| 法务静态页 | `https://peilian.njzhmj.top/legal/terms.html` 与 `https://peilian.njzhmj.top/legal/privacy.html` 已部署并可访问;发布前站点备份为 `/opt/wygj/backups/www-20260728224710` |
@@ -35,7 +35,7 @@
3. **原生人工反例未完成。** 覆盖安装和冷启动已通过;仍缺用户亲自执行的同意/拒绝、拒绝后退出、同意后登录、短信失败与重新登录、麦克风允许/拒绝、媒体取消、前后台切换和弱网验证。
4. **核心业务真机闭环未完成。** 仍缺认证账号下完整“查/练/问”、五个直通渠道、五个正式对练场景、ASR/TTS、附件上传下载和越权反例。
5. **内容与业务验收不足。** 当前仅收到 1 份生活顾问访谈,距离计划中的 24 份访谈、30 道人工审核政策题、5 个正式场景及五渠道真实处理人配置均无完成证据。
6. **发布与回滚未签认。** 尚无四个发布单元的版本矩阵、负责人签字、完整预检结果和回滚演练记录。当前 APK 使用 DCloud 测试证书且运行壳允许明文流量,只能用于受控内测;企业签名和公司法务正式签认仍是正式发布门槛。
6. **发布与回滚未签认。** 本地已有 APK/后端最小版本矩阵、构建证据和回滚边界,但尚无负责人签字、完整预检通过和回滚演练记录。当前 APK 已关闭明文流量但仍使用 DCloud 测试证书,只能用于受控内测;企业签名和公司法务正式签认仍是正式发布门槛。
## 转为 Go 的最短路径
@@ -1,6 +1,6 @@
# 帮道 8 月 1 日 Android 安装包测试版任务计划
> 文档版本:v1.9
> 文档版本:v2.0
>
> 制定日期:2026-07-27
>
@@ -86,16 +86,16 @@
### 3.2 RC 自动化门禁快照(2026-07-29)
本节记录当前工作区自动化和测试签名自定义基座结果,不代表企业签名 RC APK 或完整真机验收完成。
本节记录当前工作区自动化、测试签名自定义基座和独立内测 APK 结果,不代表企业签名 RC APK 或完整真机验收完成。
| 门禁 | 结果 | 说明 |
|---|---|---|
| `mobile-uni` 单元测试 | 通过 | 191/191;测试过程有本机 WebSocket 端口占用告警,但无失败 |
| `mobile-uni` 单元测试 | 通过 | 193/193;测试过程有本机 WebSocket 端口占用告警,但无失败 |
| `mobile-uni` TypeScript 检查 | 通过 | `npm --prefix mobile-uni run typecheck` |
| H5 资源构建 | 通过 | 仅证明 H5 资源可编译 |
| App-Plus 资源构建 | 通过 | 仅生成 HBuilderX 出包资源,不等同于 APK |
| App 原生配置、图标与启动图校验 | 通过 | 固定包名、语义版本、API 35、双 ABI、启动权限策略、Camera/Record 模块、22 个图标和 14 个启动图 |
| 自定义基座 APK 结构与签名校验 | 通过 | `verify:android-apk` 已核对包名、版本、`targetSdkVersion`、单签名、v2 签名和 SHA-256 |
| App 原生配置、图标与启动图校验 | 通过 | 固定包名、语义版本、API 35、双 ABI、启动权限策略、Camera/Record 模块、禁止明文 HTTP、网络安全资源、22 个图标和 14 个启动图 |
| 独立内测 APK 结构与签名校验 | 通过 | `verify-android-apk.ps1 -BuildKind dcloud-test` 已核对包名、版本、`targetSdkVersion`、网络安全配置、包内业务资源、单签名、v2 签名和 SHA-256 |
| `390×844` 未登录首屏与入口 | 通过 | 今日主操作、练、问、问制度快捷项、大喇叭和直通车登录门禁均在首屏可见;练、问已实际点击可达,无控制台错误 |
| 登录后核心业务路径 | 未执行 | 当前没有可用的本地 APP 登录主体和后端会话;不得用伪造手机号或未认证预览冒充通过 |
| 管理端生产构建 | 通过 | 存在大 chunk 告警,不阻断本轮构建 |
@@ -122,7 +122,8 @@
- 2026-07-29 当前候选包升级为 `0.1.6 (106)`,位于 `mobile-uni/dist/debug/bangdao-0.1.6-dcloud-test.apk`,大小 `33,921,279` 字节,SHA-256 为 `A5F026B70F11327355597B9FEA3625C4168700297E6D7847F10B079CEB860914`。登录页法务校验已移除 App-Plus 不保证支持的浏览器 `URL` 构造器,测试验证码不再以源码常量进入包内;APK 反编译门禁已确认包内业务资源等于当前编译产物,并通过法务链接、签名和敏感值扫描。
- `0.1.6 (106)` 已在同一设备完成保留数据的覆盖安装和 Launcher 冷启动;相机、录音和媒体权限保持未授权,未见 `FATAL EXCEPTION`。覆盖安装保留了前一版本的隐私状态,因此最新包的拒绝/同意反例仍须由用户亲自执行。
- 本机 Android Studio `Medium_Phone` 虚拟机已启动为 Android 15/API 35(`1080×2400`、密度 `420`)。在此前未安装本应用的虚拟机上,`0.1.6 (106)` 首次安装和 Launcher 冷启动成功,最新包真实显示原生隐私弹窗,四项相机、录音、图片和视频权限均未授予,crash 缓冲区为空。虚拟机只补充干净安装和 API 35 权限基线,不替代真机媒体、WebRTC、弱网与系统浏览器验收;拒绝/同意仍由用户亲自执行。
- 自定义基座和独立内测 APK 均使用 DCloud 云端测试证书,不是企业正式签名 APK;独立内测包的 DCloud 运行壳仍声明允许明文流量,因此只可用于 8 月 1 日受控内测。公司法务签认、企业签名、用户实际同意/拒绝、登录后权限反例仍是正式发布阻断。
- `0.1.8 (108)` 进一步把 Android 原生清单和 `network_security_config.xml` 纳入 App 构建链及 APK 反编译门禁。独立内测 APK 大小 `33,921,705` 字节,SHA-256 为 `E252140645F3AAD14B3594FD78E76BDF1A2122F7DC70EBD21CC5DF53223C7878`;APK 反编译确认 `usesCleartextTraffic=false`、`networkSecurityConfig` 引用和系统证书信任配置均已进入安装包,本机虚拟机卸载旧实例后全新安装并在 `2.904s` 内冷启动,最新隐私弹窗真实显示,四项敏感权限未授予且 crash 缓冲区为空。
- 自定义基座和独立内测 APK 均使用 DCloud 云端测试证书,不是企业正式签名 APK;当前 `0.1.8` 已关闭明文流量并内置网络安全配置,但仍只可用于 8 月 1 日受控内测。公司法务签认、企业签名、用户实际同意/拒绝、登录后权限反例仍是正式发布阻断。
## 4. P0 工作分解