收紧移动壳WebView安全开关
移动壳 WebView 显式禁用多窗口、文件访问、混合内容、第三方 Cookie 和远程调试 移动壳检查脚本拒绝 WebView 安全开关被放宽 移动壳导航测试补充协议降级、协议相对外域和危险协议拦截 宿主壳方案和共享决策记录移动 WebView 安全边界
This commit is contained in:
@@ -176,8 +176,16 @@ export default function App() {
|
|||||||
ref={webViewRef}
|
ref={webViewRef}
|
||||||
source={{ uri: webUrl }}
|
source={{ uri: webUrl }}
|
||||||
javaScriptEnabled
|
javaScriptEnabled
|
||||||
|
javaScriptCanOpenWindowsAutomatically={false}
|
||||||
domStorageEnabled
|
domStorageEnabled
|
||||||
|
mixedContentMode="never"
|
||||||
originWhitelist={[allowedWebOrigin]}
|
originWhitelist={[allowedWebOrigin]}
|
||||||
|
allowFileAccess={false}
|
||||||
|
allowFileAccessFromFileURLs={false}
|
||||||
|
allowUniversalAccessFromFileURLs={false}
|
||||||
|
thirdPartyCookiesEnabled={false}
|
||||||
|
sharedCookiesEnabled={false}
|
||||||
|
webviewDebuggingEnabled={false}
|
||||||
onMessage={handleMessage}
|
onMessage={handleMessage}
|
||||||
onShouldStartLoadWithRequest={handleShouldStartLoad}
|
onShouldStartLoadWithRequest={handleShouldStartLoad}
|
||||||
onNavigationStateChange={(event) => {
|
onNavigationStateChange={(event) => {
|
||||||
|
|||||||
@@ -295,6 +295,15 @@ for (const snippet of [
|
|||||||
'SafeAreaView',
|
'SafeAreaView',
|
||||||
'MOBILE_SHELL_SAFE_AREA_EDGES',
|
'MOBILE_SHELL_SAFE_AREA_EDGES',
|
||||||
'resolveMobileShellBaseWebUrl',
|
'resolveMobileShellBaseWebUrl',
|
||||||
|
'javaScriptCanOpenWindowsAutomatically={false}',
|
||||||
|
'mixedContentMode="never"',
|
||||||
|
'allowFileAccess={false}',
|
||||||
|
'allowFileAccessFromFileURLs={false}',
|
||||||
|
'allowUniversalAccessFromFileURLs={false}',
|
||||||
|
'thirdPartyCookiesEnabled={false}',
|
||||||
|
'sharedCookiesEnabled={false}',
|
||||||
|
'webviewDebuggingEnabled={false}',
|
||||||
|
'setSupportMultipleWindows={false}',
|
||||||
]) {
|
]) {
|
||||||
if (!appSource.includes(snippet)) {
|
if (!appSource.includes(snippet)) {
|
||||||
throw new Error(`mobile shell App missing ${snippet}`);
|
throw new Error(`mobile shell App missing ${snippet}`);
|
||||||
|
|||||||
@@ -19,6 +19,12 @@ describe('shouldOpenInMobileShellWebView', () => {
|
|||||||
expect(
|
expect(
|
||||||
shouldOpenInMobileShellWebView('/creation/puzzle', allowedOrigin),
|
shouldOpenInMobileShellWebView('/creation/puzzle', allowedOrigin),
|
||||||
).toBe(true);
|
).toBe(true);
|
||||||
|
expect(
|
||||||
|
shouldOpenInMobileShellWebView(
|
||||||
|
'http://app.genarrative.world/works/detail?work=PZ-1',
|
||||||
|
allowedOrigin,
|
||||||
|
),
|
||||||
|
).toBe(false);
|
||||||
expect(
|
expect(
|
||||||
shouldOpenInMobileShellWebView('about:blank', allowedOrigin),
|
shouldOpenInMobileShellWebView('about:blank', allowedOrigin),
|
||||||
).toBe(true);
|
).toBe(true);
|
||||||
@@ -33,6 +39,12 @@ describe('shouldOpenInMobileShellWebView', () => {
|
|||||||
expect(
|
expect(
|
||||||
shouldOpenInMobileShellWebView('mailto:hi@example.com', allowedOrigin),
|
shouldOpenInMobileShellWebView('mailto:hi@example.com', allowedOrigin),
|
||||||
).toBe(false);
|
).toBe(false);
|
||||||
|
expect(
|
||||||
|
shouldOpenInMobileShellWebView('//example.com/evil', allowedOrigin),
|
||||||
|
).toBe(false);
|
||||||
|
expect(
|
||||||
|
shouldOpenInMobileShellWebView('javascript:alert(1)', allowedOrigin),
|
||||||
|
).toBe(false);
|
||||||
expect(shouldOpenInMobileShellWebView('not a url', allowedOrigin)).toBe(
|
expect(shouldOpenInMobileShellWebView('not a url', allowedOrigin)).toBe(
|
||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -59,6 +59,7 @@
|
|||||||
- 2026-06-18 移动壳网络安全元数据:Expo 移动壳默认包配置显式禁用 Android 明文流量 `usesCleartextTraffic=false`,iOS ATS 禁用任意加载 `NSAllowsArbitraryLoads=false`,并设置 `ITSAppUsesNonExemptEncryption=false` 作为当前未接入自定义加密能力的出口合规声明;本地 Vite 联调只通过 development build 显式环境变量进入,不把任意明文流量开关带进默认包配置。
|
- 2026-06-18 移动壳网络安全元数据:Expo 移动壳默认包配置显式禁用 Android 明文流量 `usesCleartextTraffic=false`,iOS ATS 禁用任意加载 `NSAllowsArbitraryLoads=false`,并设置 `ITSAppUsesNonExemptEncryption=false` 作为当前未接入自定义加密能力的出口合规声明;本地 Vite 联调只通过 development build 显式环境变量进入,不把任意明文流量开关带进默认包配置。
|
||||||
- 2026-06-18 移动壳麦克风权限禁用:Expo 移动壳的 `expo-image-picker` 插件必须保持 `microphonePermission=false`,Android 包配置必须通过 `android.blockedPermissions` 显式移除 `android.permission.RECORD_AUDIO`,且不得在 `android.permissions` 中重新声明;移动壳现阶段没有录音、后台音频采集或远程语音 SDK,音频导入只走系统文档选择器。配置检查会拒绝麦克风权限拦截缺失或被反向加入。
|
- 2026-06-18 移动壳麦克风权限禁用:Expo 移动壳的 `expo-image-picker` 插件必须保持 `microphonePermission=false`,Android 包配置必须通过 `android.blockedPermissions` 显式移除 `android.permission.RECORD_AUDIO`,且不得在 `android.permissions` 中重新声明;移动壳现阶段没有录音、后台音频采集或远程语音 SDK,音频导入只走系统文档选择器。配置检查会拒绝麦克风权限拦截缺失或被反向加入。
|
||||||
- 2026-06-18 移动壳 Android 自动备份关闭:Expo 移动壳必须保持 `android.allowBackup=false`,避免 WebView cookie、localStorage、缓存文件和宿主文件导入导出中间态进入 Google Drive 自动备份 / 恢复链路;正式业务事实仍以后端账号、作品、钱包和草稿状态为准。配置检查会拒绝恢复 Android 默认允许备份的包配置。
|
- 2026-06-18 移动壳 Android 自动备份关闭:Expo 移动壳必须保持 `android.allowBackup=false`,避免 WebView cookie、localStorage、缓存文件和宿主文件导入导出中间态进入 Google Drive 自动备份 / 恢复链路;正式业务事实仍以后端账号、作品、钱包和草稿状态为准。配置检查会拒绝恢复 Android 默认允许备份的包配置。
|
||||||
|
- 2026-06-18 移动壳 WebView 安全开关:Expo 移动壳 WebView 必须显式禁用 JS 自动开窗、多窗口、文件访问、file URL 跨源访问、HTTPS 混合内容、第三方 Cookie、共享 Cookie 和 WebView 远程调试;同源主站页面才能留在带 HostBridge 的 WebView 内,外链只通过受控协议离开容器交给系统。配置检查和移动壳导航测试会拒绝这些边界被放宽。
|
||||||
- 影响范围:`src/services/host-bridge/`、未来 `apps/mobile-shell/`、未来 `apps/desktop-shell/`、移动端支付 / 分享 / 深链 / 推送、桌面端系统能力、AI H5 sandbox 的 GameBridge 边界。
|
- 影响范围:`src/services/host-bridge/`、未来 `apps/mobile-shell/`、未来 `apps/desktop-shell/`、移动端支付 / 分享 / 深链 / 推送、桌面端系统能力、AI H5 sandbox 的 GameBridge 边界。
|
||||||
- 验证方式:普通浏览器、小程序、Expo 壳、Tauri 壳都能返回正确 `getHostRuntime()`;未支持能力能回退 H5;固定玩法在各宿主中读取同一作品数据和运行态 snapshot;AI sandbox 无法直接调用 HostBridge;Tauri release 不允许任意远端页面调用桌面命令。
|
- 验证方式:普通浏览器、小程序、Expo 壳、Tauri 壳都能返回正确 `getHostRuntime()`;未支持能力能回退 H5;固定玩法在各宿主中读取同一作品数据和运行态 snapshot;AI sandbox 无法直接调用 HostBridge;Tauri release 不允许任意远端页面调用桌面命令。
|
||||||
- 关联文档:`docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md`、`docs/【前端架构】宿主壳能力统一协议-2026-06-17.md`。
|
- 关联文档:`docs/【前端架构】ExpoReactNative与Tauri宿主壳方案-2026-06-17.md`、`docs/【前端架构】宿主壳能力统一协议-2026-06-17.md`。
|
||||||
|
|||||||
@@ -321,6 +321,8 @@ GameBridge 禁止:
|
|||||||
|
|
||||||
2026-06-18 追加:移动壳 Android 自动备份显式关闭。Expo `android.allowBackup=false`,避免 WebView cookie、localStorage、缓存文件、系统剪贴板导入中间文件等本地宿主状态进入 Google Drive 自动备份 / 恢复链路;正式业务事实仍以后端账号、作品、钱包和草稿状态为准,不依赖设备备份恢复。`apps/mobile-shell/scripts/check-config.mjs` 会拒绝恢复默认允许备份的 Android 包配置。
|
2026-06-18 追加:移动壳 Android 自动备份显式关闭。Expo `android.allowBackup=false`,避免 WebView cookie、localStorage、缓存文件、系统剪贴板导入中间文件等本地宿主状态进入 Google Drive 自动备份 / 恢复链路;正式业务事实仍以后端账号、作品、钱包和草稿状态为准,不依赖设备备份恢复。`apps/mobile-shell/scripts/check-config.mjs` 会拒绝恢复默认允许备份的 Android 包配置。
|
||||||
|
|
||||||
|
2026-06-18 追加:移动壳 WebView 原生安全开关显式收紧。`react-native-webview` 只加载同源主站入口,保留 JS 和 DOM storage 以运行现有 H5,但禁用 JS 自动开窗、多窗口、文件访问、file URL 跨源访问、HTTPS 页面加载 HTTP 混合内容、第三方 Cookie、共享 Cookie 和 WebView 远程调试;外链继续只允许 `http:`、`https:`、`mailto:`、`tel:` 离开 WebView 交给系统。`apps/mobile-shell/scripts/check-config.mjs` 和 `mobileShellNavigation.test.ts` 会覆盖这些壳边界,避免后续为单个页面调试把完整 HostBridge 暴露给外域页面。
|
||||||
|
|
||||||
### Phase 4:宿主能力扩展
|
### Phase 4:宿主能力扩展
|
||||||
|
|
||||||
- 移动端接入系统分享、推送、原生登录和渠道支付。
|
- 移动端接入系统分享、推送、原生登录和渠道支付。
|
||||||
|
|||||||
Reference in New Issue
Block a user