鸿蒙WebView IPC防伪造请求方案
纵深防御:不要依赖单一安全措施。应组合使用来源校验动态令牌和接口权限控制,构建多层防御体系。最小权限原则:仅为 Web 页面暴露其完成功能所必需的最少 JSBridge 接口,并为每个接口分配最小必要权限。令牌生命周期管理:注入的令牌应设置较短的过期时间(如 30 分钟),并在用户退出登录或应用切换到后台时主动失效。敏感操作二次确认:对于特别敏感的操作(如支付、删除数据),即使请求通过了所有校验,
在鸿蒙 WebView 混合架构中,IPC(进程间通信)或 JSBridge 是 Web 页面与原生侧交互的核心通道。为了防止恶意页面(如被注入恶意脚本的页面或第三方加载的页面)伪造请求、窃取数据或执行未授权操作,必须建立一套严格的安全防御机制。核心思路是将原生侧视为安全边界,对来自 Web 的所有请求进行身份认证、来源校验和权限控制。
以下是具体的安全策略与实现方案。
一、 安全威胁模型与防护策略对比
恶意页面伪造请求的常见方式包括:篡改注入的 JS 脚本、利用跨站脚本(XSS)漏洞、加载不受控的第三方页面等。针对这些威胁,可采取多层次的安全防护。
| 安全维度 | 核心威胁 | 防护策略 | 实施要点 |
|---|---|---|---|
| 请求来源验证 | 非授权域名或本地文件加载的页面调用 JSBridge | 域名/协议白名单校验 | 在原生侧拦截并验证请求的 URL 来源 |
| 身份认证 | 已加载的合法页面内,恶意脚本冒用合法身份发起请求 | Token/签名动态注入与验证 | 原生侧在页面加载时注入一次性 Token,每次请求需携带并验证 |
| 参数安全 | 请求参数被篡改,导致越权操作(如访问他人数据) | 参数签名与完整性校验 | 对请求参数生成签名,原生侧验签确保数据未被篡改 |
| 权限隔离 | 恶意请求试图调用高敏感接口(如文件读写、通讯录) | 基于角色的接口访问控制 (RBAC) | 根据 Web 页面的业务角色动态分配可调用的 JSBridge 方法集 |
二、 核心安全实现方案
1. 严格校验 WebView 加载来源(第一道防线)
在创建 WebView 或处理请求前,必须验证当前加载页面的 URL 是否在可信白名单内。
// ArkTS 侧 WebViewController 示例
import web_webview from '@ohos.web.webview';
import { BusinessError } from '@ohos.base';
@Entry
@Component
struct SecureWebView {
controller: web_webview.WebviewController = new web_webview.WebviewController();
// 定义可信域名白名单
private trustedDomains: string[] = [
'https://your-legitimate-domain.com',
'file://resource/rawfile/', // 允许加载本地安全资源
];
aboutToAppear() {
// 监听页面加载开始,进行来源校验
this.controller.onLoadStart((event) => {
const url = event?.url;
if (!url || !this.isUrlTrusted(url)) {
console.error(`安全拦截:试图加载非授信源 ${url}`);
// 可选择停止加载或跳转到错误页
this.controller.stopLoading();
// 跳转到本地安全提示页
this.controller.loadUrl('file://resource/rawfile/error.html');
} else {
console.info(`允许加载授信源:${url}`);
}
});
}
// 校验URL是否可信
private isUrlTrusted(url: string): boolean {
return this.trustedDomains.some(domain => url.startsWith(domain));
}
build() {
Column() {
Web({ src: 'https://your-legitimate-domain.com/index.html', controller: this.controller })
.width('100%')
.height('100%')
.javaScriptAccess(true) // 开启JS交互
.onInterceptRequest((event) => {
// 可选:进一步拦截所有资源请求(如图片、API),进行更细粒度的校验
return this.handleInterceptRequest(event);
})
}
}
private handleInterceptRequest(event: any): boolean {
// 示例:拦截所有请求,确保其来源或目标为授信域
const requestUrl = event?.request?.url;
if (requestUrl && !this.isUrlTrusted(requestUrl)) {
console.warn(`拦截非授信资源请求:${requestUrl}`);
return true; // true 表示拦截该请求
}
return false; // false 表示允许请求通过
}
}
代码解析:此代码在页面加载开始(onLoadStart)时进行第一层域名校验,防止加载恶意首页。同时,通过 onInterceptRequest 可以拦截页面内的所有子资源请求(如 XHR/Fetch、图片),实现网络层面的访问控制 。
2. 动态令牌注入与验证(第二道防线)
对于授信页面,原生侧在页面加载完成后注入一个临时的、唯一的令牌(Token)。所有后续的 JSBridge 调用都必须携带此令牌,原生侧验证令牌有效性后才处理请求。
步骤一:原生侧注入令牌
// 在 ArkTS 组件中,监听页面加载完成
private setupTokenInjection() {
this.controller.onPageFinish((url: string) => {
if (this.isUrlTrusted(url)) {
// 生成一个临时令牌(实际应用中应更复杂,如JWT)
const token = this.generateSecureToken();
// 将令牌注入到 Web 页面的全局环境
const jsCode = `
(function() {
window.__NATIVE_TOKEN__ = '${token}';
console.log('安全令牌已注入');
})();
`;
this.controller.runJavaScript(jsCode, (err: BusinessError) => {
if (err) {
console.error('令牌注入失败:', JSON.stringify(err));
}
});
}
});
}
private generateSecureToken(): string {
// 实际开发中应使用加密库生成,此处为示例
return 'token_' + Date.now() + '_' + Math.random().toString(36).substr(2);
}
步骤二:JSBridge 接口统一验证令牌
假设我们通过 runJavaScript 或 onMessage 方式暴露一个 callNative 方法给 Web。
// Web 页面端 JS:封装安全的调用函数
window.callSecureNative = function(method: string, params: any): Promise<any> {
return new Promise((resolve, reject) => {
// 获取原生注入的令牌
const token = window.__NATIVE_TOKEN__;
if (!token) {
reject(new Error('安全令牌未就绪,请等待页面加载完成'));
return;
}
// 构造请求对象,包含令牌
const request = {
method: method,
params: params,
timestamp: Date.now(),
token: token
};
// 假设通过 postMessage 或特定函数与原生通信
// 这里以假设的全局桥接对象为例
if (window.NativeBridge && window.NativeBridge.postMessage) {
window.NativeBridge.postMessage(JSON.stringify(request), (response) => {
// 处理原生返回
const resp = JSON.parse(response);
if (resp.code === 200) {
resolve(resp.data);
} else {
reject(new Error(resp.message || '调用失败'));
}
});
} else {
reject(new Error('原生桥接不可用'));
}
});
};
// 使用示例:调用获取位置的接口
window.callSecureNative('getLocation', { highAccuracy: true })
.then(location => console.log('位置:', location))
.catch(err => console.error('获取失败:', err));
步骤三:原生侧验证请求
在原生侧接收消息的地方,验证令牌。
// ArkTS 侧:监听来自 Web 的消息
private setupMessageHandler() {
// 假设通过某种方式(如 onMessage)接收消息
this.controller.onMessage((event) => {
try {
const request = JSON.parse(event?.data);
// 1. 验证令牌是否存在且有效
if (!request.token || !this.isTokenValid(request.token)) {
this.sendErrorResponse(event, '非法令牌或令牌已失效', 403);
return;
}
// 2. 可选:验证时间戳,防止重放攻击
if (Date.now() - request.timestamp > 5 * 60 * 1000) { // 5分钟有效期
this.sendErrorResponse(event, '请求已过期', 408);
return;
}
// 3. 令牌有效,处理业务请求
this.handleBridgeMethod(request.method, request.params, event);
} catch (error) {
this.sendErrorResponse(event, '请求格式错误', 400);
}
});
}
private isTokenValid(token: string): boolean {
// 实现令牌验证逻辑,例如检查是否在已签发的令牌列表中,是否过期等
// 此处为示例,实际应结合缓存或数据库
return token.startsWith('token_'); // 简单示例
}
private sendErrorResponse(event: any, message: string, code: number) {
const response = JSON.stringify({ code: code, message: message });
// 通过相应方式将错误信息传回 Web
this.controller.postMessage(event?.origin, response);
}
3. 接口级权限控制(RBAC)
根据 Web 页面的业务角色,限制其可调用的 JSBridge 方法。例如,一个普通的资讯页面不应该能调用“发送短信”或“读取通讯录”的接口。
// ArkTS 侧:维护接口-角色映射
private methodPermissionMap: Map<string, string[]> = new Map([
['getLocation', ['user', 'admin']], // 用户和管理员可调用
['readContacts', ['admin']], // 仅管理员可调用
['getDeviceInfo', ['user', 'admin', 'guest']], // 所有角色可调用
]);
private handleBridgeMethod(method: string, params: any, event: any) {
// 1. 获取当前页面的角色(可从注入的令牌中解析,或根据URL判断)
const currentPageRole = this.getRoleFromTokenOrUrl(event?.url); // 假设实现此方法
// 2. 检查该角色是否有权限调用此方法
const allowedRoles = this.methodPermissionMap.get(method);
if (!allowedRoles || !allowedRoles.includes(currentPageRole)) {
this.sendErrorResponse(event, `角色 ${currentPageRole} 无权限调用方法 ${method}`, 403);
return;
}
// 3. 有权限,执行对应方法
switch (method) {
case 'getLocation':
this.handleGetLocation(params, event);
break;
// ... 其他方法
default:
this.sendErrorResponse(event, `未知方法: ${method}`, 404);
}
}
三、 总结与最佳实践
- 纵深防御:不要依赖单一安全措施。应组合使用来源校验、动态令牌和接口权限控制,构建多层防御体系。
- 最小权限原则:仅为 Web 页面暴露其完成功能所必需的最少 JSBridge 接口,并为每个接口分配最小必要权限。
- 令牌生命周期管理:注入的令牌应设置较短的过期时间(如 30 分钟),并在用户退出登录或应用切换到后台时主动失效。
- 敏感操作二次确认:对于特别敏感的操作(如支付、删除数据),即使请求通过了所有校验,也应在原生侧弹出系统级别的确认对话框,由用户最终授权。
- 定期安全审计:定期检查 JSBridge 的调用日志,监控异常调用模式,及时发现潜在的攻击行为。
通过上述方案,可以极大程度地防止鸿蒙 WebView 中恶意页面伪造 IPC/JSBridge 请求,确保混合应用的安全边界牢固可靠 。
更多推荐


所有评论(0)