HarmonyOS ArkWeb 安全治理实战:白名单、JSBridge、Cookie 与文件边界
HarmonyOS ArkWeb 安全治理实战:白名单、JSBridge、Cookie 与文件边界
ArkWeb 能让 HarmonyOS 应用快速承载活动页、帮助中心、协议页和混合业务,但它也会把 Web 风险带进原生应用:加载了非可信域名,JSBridge 暴露过多能力,Cookie 没有隔离,文件上传下载没有边界,都会影响用户数据和审核解释。ArkWeb 安全治理的目标不是拒绝混合页面,而是让每一次加载、桥接、存储和文件操作都经过可控规则。

本文会拆四条主线:
- URL 加载前先做白名单和场景判断。
- JSBridge 按接口分级,不给 H5 任意调用原生能力。
- Cookie、缓存和登录态要有清理策略。
- 文件上传下载要限制类型、大小和来源。
1. ArkWeb 容器先定边界
一个安全的 Web 容器至少要明确三件事:哪些地址能加载,哪些桥接接口能调用,哪些数据能留在容器里。不要把活动页、协议页、支付页、客服页全放进同一个“万能 WebView”。

| 边界 | 处理内容 | 常见风险 |
|---|---|---|
| URL | 协议、域名、路径、跳转链 | 非可信站点打开 |
| Bridge | 接口名称、参数、调用场景 | H5 越权调用原生能力 |
| Cookie | 主站登录态、第三方 Cookie、退出清理 | 账号残留或串号 |
| 文件 | 上传类型、大小、下载目录 | 非法文件进入应用 |
先把边界画清楚,再写容器代码。否则后面发现风险时,只能在页面里不断补 if。
2. ArkWeb 资料边界和工程目录
ArkWeb 文档提供 Web 组件、页面加载、生命周期、JavaScript 交互、权限与隐私等能力说明。工程上要把组件使用和安全策略分开。
| 资料入口 | 工程落点 |
|---|---|
| ArkWeb 概述 | 明确 Web 组件能力范围 |
| 使用 Web 组件加载页面 | URL 加载、页面生命周期和错误处理 |
| Web 与应用侧交互 | JSBridge 能力边界和参数约束 |
| ArkWeb 安全与隐私 | 安全加载、隐私保护和权限控制 |
建议目录:
entry/src/main/ets/
common/web/UrlPolicy.ets
common/web/BridgeGuard.ets
common/web/CookieScope.ets
common/web/FileBoundary.ets
common/web/WebAuditLog.ets
pages/web/SafeWebPage.ets
SafeWebPage 负责渲染,策略模块负责判断。不要让页面直接决定能否调用某个桥接接口。
3. UrlPolicy 控制可加载地址
Web 容器加载前必须先判断协议、域名和路径。只校验域名还不够,某些路径可能是开放跳转或不适合在 App 内打开。
export interface UrlDecision {
allowed: boolean
reason?: string
normalizedUrl?: string
}
export class UrlPolicy {
private readonly hosts = ['secure.example.com', 'help.example.com']
private readonly pathPrefixes = ['/activity/', '/help/', '/agreement/']
decide(rawUrl: string): UrlDecision {
try {
const url = new URL(rawUrl)
if (url.protocol !== 'https:') {
return { allowed: false, reason: '仅允许 HTTPS 页面' }
}
if (!this.hosts.includes(url.hostname)) {
return { allowed: false, reason: '域名不在 Web 容器白名单内' }
}
if (!this.pathPrefixes.some(prefix => url.pathname.startsWith(prefix))) {
return { allowed: false, reason: '路径不在可加载范围内' }
}
return { allowed: true, normalizedUrl: url.toString() }
} catch (_) {
return { allowed: false, reason: 'URL 格式不正确' }
}
}
}
这个策略适合放在页面加载前。只要不允许,就进入错误页,不把风险地址交给 Web 组件。
4. BridgeGuard 给 JSBridge 做分级
JSBridge 最容易被滥用。建议把接口分为只读、用户确认、禁止三类,并对每个接口做参数约束。
export type BridgeLevel = 'READONLY' | 'CONFIRM_REQUIRED' | 'FORBIDDEN'
export interface BridgeCall {
name: string
payload: Record<string, Object>
pageHost: string
}
export class BridgeGuard {
private readonly policy: Record<string, BridgeLevel> = {
getAppVersion: 'READONLY',
openNativePage: 'CONFIRM_REQUIRED',
uploadFile: 'CONFIRM_REQUIRED',
execShell: 'FORBIDDEN'
}
canInvoke(call: BridgeCall): boolean {
const level = this.policy[call.name] ?? 'FORBIDDEN'
if (level === 'FORBIDDEN') {
return false
}
if (call.name === 'openNativePage') {
const target = call.payload['target']
return typeof target === 'string' && target.startsWith('pages/')
}
return true
}
}
接口默认禁止,显式放行。对于会跳转、上传、复制、打开原生页面的接口,建议再加用户确认或业务场景限制。
5. CookieScope 管理登录态和退出清理
混合页面经常需要登录态,但 Cookie 不能无限保留。退出登录、账号切换、隐私撤销时,Web 容器状态也要清理。

export interface CookieCleanPlan {
host: string
clearSession: boolean
clearPersistent: boolean
reason: 'logout' | 'account_switch' | 'privacy_revoke'
}
export class CookieScope {
buildCleanPlan(reason: CookieCleanPlan['reason']): CookieCleanPlan[] {
return [
{
host: 'secure.example.com',
clearSession: true,
clearPersistent: reason !== 'logout',
reason
},
{
host: 'help.example.com',
clearSession: true,
clearPersistent: false,
reason
}
]
}
}
真实项目中清理动作要接入 ArkWeb 对应 Cookie 能力。这里强调的是策略:不同域名、不同原因,清理范围可以不同,但必须可解释。
6. FileBoundary 限制文件上传下载
Web 页面触发文件能力时,要限制类型、大小和来源。尤其是上传证件、截图、日志文件这类场景,不能直接把任意路径交给 Web。
export interface WebFileRequest {
fileName: string
mimeType: string
sizeBytes: number
source: 'camera' | 'album' | 'document'
}
export class FileBoundary {
private readonly allowedTypes = ['image/png', 'image/jpeg', 'application/pdf']
private readonly maxSize = 10 * 1024 * 1024
accept(req: WebFileRequest): boolean {
if (!this.allowedTypes.includes(req.mimeType)) {
return false
}
if (req.sizeBytes <= 0 || req.sizeBytes > this.maxSize) {
return false
}
return !req.fileName.toLowerCase().endsWith('.exe')
}
}
文件边界不要只靠前端页面提示。原生层拿到文件后仍要再判断一次,避免 H5 绕过 UI 限制。
7. Web 容器错误页要可解释
加载失败、域名不可信、Bridge 被拒绝、文件不支持,都要给用户可理解提示,同时记录开发排查字段。
export interface WebErrorView {
title: string
description: string
action: string
}
export function buildWebError(reason: string): WebErrorView {
if (reason.includes('白名单') || reason.includes('域名')) {
return { title: '页面暂不可打开', description: '当前页面来源未通过应用安全规则。', action: '返回上一页' }
}
if (reason.includes('文件')) {
return { title: '文件不支持', description: '请上传图片或 PDF 文件,并确认文件大小符合要求。', action: '重新选择' }
}
return { title: '页面加载失败', description: '请稍后重试,或返回应用首页。', action: '重试' }
}
错误页不是掩盖问题,而是给用户一个明确出口。开发侧还要保留审计日志。
8. WebAuditLog 记录风险动作
Web 安全问题排查离不开日志。日志要记录阶段、域名、接口名、拒绝原因,但不要记录完整 Token 或敏感参数。
export interface WebAuditRecord {
stage: 'url' | 'bridge' | 'cookie' | 'file'
host?: string
action: string
result: 'allow' | 'deny'
reason?: string
at: number
}
export class WebAuditLog {
private readonly records: WebAuditRecord[] = []
append(record: Omit<WebAuditRecord, 'at'>): void {
this.records.push({ ...record, at: Date.now() })
}
recentDenied(): WebAuditRecord[] {
return this.records.filter(item => item.result === 'deny').slice(-20)
}
}
上线后如果运营说某个活动页打不开,先看审计日志就能知道是域名、路径、桥接还是文件问题。
9. 混合容器验收动作
| 场景 | 操作 | 预期结果 |
|---|---|---|
| 可信活动页 | 加载白名单 HTTPS 地址 | 正常展示 |
| 非可信域名 | 加载外部站点 | 被拦截并进入错误页 |
| Bridge 越权 | H5 调用未登记接口 | 调用被拒绝并记录日志 |
| 账号退出 | 退出登录后回到 Web 页 | 会话 Cookie 被清理 |
| 文件上传 | 上传超大文件或不支持类型 | 被拒绝并提示原因 |
可以加入一个安全入口断言:
export function assertWebContainerDecision(decision: UrlDecision): void {
if (decision.allowed && !decision.normalizedUrl?.startsWith('https://')) {
throw new Error('允许加载的 Web 地址必须是 HTTPS')
}
}
这个断言能防止后续修改白名单时误放行非安全协议。
10. ArkWeb 异常排查表
| 现象 | 优先查看 | 处理建议 |
|---|---|---|
| 页面打不开 | UrlPolicy 拒绝原因、域名配置 |
区分域名、路径、协议问题 |
| H5 功能无响应 | Bridge 接口名、参数、策略等级 | 不要默认放行未知接口 |
| 退出后仍显示账号 | Cookie 清理计划、账号切换事件 | 退登时同步清理 Web 状态 |
| 上传文件失败 | 文件类型、大小、来源 | 页面和原生层都要提示限制 |
| 审核问到隐私问题 | Cookie、Bridge、文件能力说明 | 准备清晰的权限和数据流解释 |
ArkWeb 边界复现场景:给读者一组可执行核验
ArkWeb 安全要同时看域名、JSBridge、Cookie 和文件访问。只配置白名单不足以覆盖桥接调用风险。
| 核验维度 | 读者需要准备的证据 |
|---|---|
| 输入 | 页面入口、用户动作、关键参数 |
| 过程 | 日志、状态变化、异常分支 |
| 输出 | UI 表现、回调结果、持久化结果 |
| 回归 | 同场景重复执行后的结果 |
interface ArkWebReplayCase {
url: any
bridgeName: any
cookieMode: any
fileAccess: any
}
const replay78: ArkWebReplayCase = {
url: 'sample',
bridgeName: 'sample',
cookieMode: 'sample',
fileAccess: 'sample',
}
function assertReplay78(item: ArkWebReplayCase): void {
if (item.bridgeName && item.url.indexOf('https://') !== 0) throw new Error('非安全页面不允许桥接')
}
这组核验关注 ArkWeb 的桥接边界,适合在新增 JSBridge 或开放新域名前使用。
11. 小结:混合页面要有原生安全壳
ArkWeb 的价值是快速承载 Web 能力,但安全边界必须由原生壳掌控。URL 白名单决定能不能加载,Bridge 策略决定能不能调用原生能力,CookieScope 决定登录态如何保留和清理,FileBoundary 决定文件能否进出。把这些策略集中起来,混合页面才能既灵活又可控。
更多推荐



所有评论(0)