- 示例工程
- 前端
- 移动开发
- 跨平台
【免费下载链接】uni-app
A cross-platform framework using Vue.js
本篇技术指南围绕 uni-app 与 uni-app x 提供的客户端安全 API展开,系统讲解如何通过plus.navigator.getSignature、plus.navigator.isSimulator、plus.networkinfo.isSetProxy、plus.navigator.isRoot等接口识别应用是否被重签名、运行在模拟器/越狱环境、网络是否被代理劫持,以及设备是否开启 USB 调试。读完本文,你将掌握一套可直接落地的客户端风控方案:既能在 App 启动时拦截异常运行环境,也能把安全状态上报业务服务器进行登录风控,并配合原生混淆加密与加固防止校验代码被篡改。
一、为什么客户端需要安全 API
uni-app 与 5+ App 提供了一批用于获取客户端安全相关信息的 API。这些信息的价值在于:App 的安装包可以被解压、被反编译、被二次打包,运行环境也不受开发者控制——模拟器、越狱/root 设备、代理网络都可能成为黑产攻击的入口。
| API | 能力 | uni-app 支持 | uni-app x 支持 | | :- | :- | :-: | :-: | |plus.navigator.getSignature| 获取应用签名标识,判断是否被重新签名 | ✔ | 通过uni.getAppBaseInfo().signature| |plus.navigator.isSimulator| 判断是否运行在模拟器 | ✔ | 通过uni.getDeviceInfo().isSimulator/uni.isSimulator()| |plus.networkinfo.isSetProxy| 判断网络环境是否设置代理 | ✔ | 暂不支持 | |plus.navigator.isRoot| 判断设备是否被 root/越狱(仅 iOS) | ✔ | 通过uni.getDeviceInfo().isRoot| |plus.navigator.isUSBDebugging| 判断是否开启 USB 调试(仅 Android) | 不支持 | 通过uni.getDeviceInfo().isUSBDebugging|
注意:官方更推荐使用 uni 云端一体安全网络方案,接入安全网络后将无需再使用本章节提供的 API。对于未接入安全网络的项目,本文的 API 校验仍是有效的兜底手段。
二、应用签名标识校验:拦截二次打包仿冒
2.1 为什么签名校验如此关键
签名证书是对 App 开发者身份的唯一标识。如果程序未对签名证书进行校验,App 被反编译后可能被二次打包,使用其它签名证书重新签名。若重新签名的 App 可以正常启动,则可能导致:
- App 被仿冒盗版,影响合法收入;
- 被添加钓鱼代码、病毒代码、恶意代码;
- 用户敏感信息泄露或遭受恶意攻击。
因此,在应用启动时校验签名,能第一时间识别出"非官方签名"的安装包。
2.2 uni-app 项目:使用 plus.navigator.getSignature
plus.navigator.getSignature用于获取应用签名标识。在 uni-app 项目中,建议在App.vue的应用生命周期onLaunch中进行校验,示例代码如下:
onLaunch: function(inf) { console.log('App Launch'); // #ifdef APP-PLUS // 签名证书检验 var platform = uni.getSystemInfoSync().platform; var sign = plus.navigator.getSignature(); if('android'==platform){ //Android平台 var sha1 = 'baad093a82829fb432a7b28cb4ccf0e9f37dae58'; //修改为自己应用签名证书SHA-1值,是全小写并且中间不包含":"符号 if(sha1!=sign){ //证书不对时退出应用 plus.runtime.quit(); } }else{ //iOS平台 var md5 = 'a2e629f0ea915b4ed11e296a059c9a12'; //修改为自己应用Apple Bunld ID(AppID)的md5值 if(md5!=sign){ //不进入应用或循环弹出提示框 console.log('应用被破坏,无法正常运行!'); uni.showModal({ title:'错误', content: '应用被破坏,无法正常运行!', }); } } // #endif }关键参数说明:
- Android 平台:
sign返回的是应用签名证书的 SHA-1 值,必须全小写且中间不包含:符号。可通过keytool -list -v -keystore 你的签名文件.keystore查看证书指纹后转小写并去掉冒号填入。 - iOS 平台:
sign返回的是应用签名证书中绑定的 Bundle ID(Apple ID)的md5 值,同样为全小写。可在打包前用echo -n "你的BundleID" | md5之类的方式计算后填入。 - 退出策略:Android 上签名不符时直接
plus.runtime.quit()退出;iOS 上可弹窗提示后退出或持续拦截,开发者可根据产品策略调整。
2.3 uni-app x 项目:使用 uni.getAppBaseInfo().signature
uni-app x 项目不支持 plus API,需使用 uni.getAppBaseInfo 返回值中的signature属性。其取值规则与 plus 版本一致:
- Android:应用签名证书的 SHA1 值(全部为小写,中间不包含
:); - iOS:应用签名证书中绑定的 Bundle ID(AppleID)的 md5 值(全部为小写)。
从仓库源码看,Android 侧的实现位于 AppBaseInfoDeviceUtil.uts:通过PackageManager.GET_SIGNATURES读取安装包签名,用MessageDigest.getInstance("SHA1")计算摘要,再将每个字节转换为不带冒号的十六进制小写字符串,与文档规定的取值格式完全对应。这意味着你预埋的 SHA-1 白名单必须与框架输出格式一致(全小写、无冒号)。
2.4 防止校验代码被篡改
签名校验代码本身位于 js 中,存在被反编译篡改的风险。官方建议:
- 将签名校验代码放到独立 js 文件中;
- 配置 js/nvue 文件原生混淆加密;
- 或使用 apk 加固处理。
三、模拟器检测:isSimulator
3.1 模拟器环境的危害
plus.navigator.isSimulator用于判断当前应用是否运行在模拟器中。iOS 系统由于苹果限制正式打包后不能在模拟器上运行,一般不存在此问题;但 Android 系统是开源的、底层代码全部公开,市面上存在大量 Android 模拟器,问题比较严重:
- 模拟器通常运行在 PC 上,可利用自动化工具自动操作 App(批量注册、刷单、薅羊毛);
- 模拟器是虚拟操作系统,可能破坏原生系统的安全性,导致用户敏感信息泄露。
3.2 uni-app 项目:在 onLaunch 中拦截
onLaunch: function(inf) { console.log('App Launch'); // #ifdef APP-PLUS // 模拟器检验 if(plus.navigator.isSimulator()){ //弹出提示框 uni.showModal({ title:'错误', content: '应用被不能运行到模拟器!', complete: ()=>{ plus.runtime.quit(); } }); } // #endif }3.3 uni-app x 项目与模拟器检测的底层原理
uni-app x 项目可通过 uni.getDeviceInfo 返回值中的isSimulator属性获取。需要注意:受隐私合规政策影响,从 4.51 版本起uni.getDeviceInfo中的isSimulator不再采集传感器信息,准确度会下降;如需更准确的判断,应使用独立的 uni.isSimulator API(Android 4.51+、iOS 4.51+),它会采集传感器信息以更准确地识别模拟器,但在 Android 应用商店上线时必须在隐私协议中声明,并确保在用户同意隐私协议后再调用。iOS 平台暂请继续使用uni.getDeviceInfo。
从源码看,Android 侧的模拟器检测逻辑集中在 EmulatorCheckUtil.uts,采用"多维特征 + 嫌疑值累计"的机制:
- 硬件名称(
ro.hardware):命中ttvm(天天模拟器)、nox(夜神)、cancro(网易 MUMU)、intel(逍遥)、vbox/vbox86(腾讯手游助手)、android_x86(雷电)等即判定为模拟器; - 已知包名:检测
com.bluestacks.home(蓝叠)、com.microvirt.guide(逍遥)等模拟器残留文件; - 构建渠道(
ro.build.flavor):包含vbox、sdk_gphone判定为模拟器; - 设备型号 / 制造商 / 主板 / 平台 / 基带:如
google_sdk、emulator、genymotion、netease、goldfish等特征值; - 硬件能力:检测是否支持闪光灯、蓝牙;
- 传感器数量与光线传感器:传感器数量过少或缺少光线传感器会增加嫌疑值;
- 最终嫌疑值大于 3 即判定为模拟器,同时每个维度都有"可能是模拟器/可能是真机"的中间状态,避免误杀。
3.4 业务侧的风控应用
实际项目中不必直接禁止运行,更推荐将模拟器状态提交业务服务器进行判断,例如:
- 模拟器环境不允许登录;
- 多次尝试在模拟器登录可临时封号;
- 由人工客服联系用户核实。
具体策略可根据业务情况进行调整。
四、网络代理检测:isSetProxy
4.1 代理网络的威胁
plus.networkinfo.isSetProxy用于判断当前应用网络环境是否设置代理。设置代理后,所有网络传输的数据都会经过代理服务器,意味着:
- 代理服务器可能看到所有传入内容,导致用户敏感信息泄露;
- 业务服务器可能被恶意攻击(抓包分析接口、重放攻击等)。
当然也存在例外:某些企业内部网络为保证安全,访问公网时可能必须设置代理。开发者需要权衡这种需求,决定是否限制应用正常运行。
4.2 在登录时上报代理状态
可以在用户登录时提交当前网络环境是否使用代理,示例代码如下:
function login() { //... //获取网络代理状态 var isProxy = plus.networkinfo.isSetProxy(); if(isProxy){ console.log("当前网络环境设置了代理!"); } //将使用代理状态提交给业务服务器 //... }4.3 平台支持情况
- uni-app 项目可通过
plus.networkinfo.isSetProxy获取; - uni-app x 项目暂不支持该能力,开发者需通过服务端风控等手段做替代判断。
五、Root/越狱检测:isRoot
5.1 越狱环境的危害
plus.navigator.isRoot用于判断当前应用运行的设备是否被 root 破解。注意:仅 iOS 平台支持,Android 平台暂不支持(Android 侧可参考下一节的 uni-app x 方案)。
root 破解(也叫越狱)是利用 iOS 系统漏洞破解系统安全机制,让任何应用获得系统级权限,从而对系统进行更灵活的自定义修改(修改字体、修改主题、使用插件等)。iOS 设计了沙盒机制,限制应用只能使用自身的数据;root 破解后,应用可以读取、修改其它任何应用的数据,可能导致:
- 用户敏感信息泄露;
- 其它应用的数据或代码被篡改,正常 App 遭到恶意攻击。
5.2 在应用启动时检测
建议在应用启动时判断,若运行在 root 破解环境则提示用户无法正常使用。uni-app 项目可以在App.vue的应用生命周期onLaunch中进行校验:
onLaunch: function(inf) { console.log('App Launch'); // #ifdef APP-PLUS //ROOT检验 if(plus.navigator.isRoot()){ //弹出提示框 uni.showModal({ title:'错误', content: '应用被不能运行到越狱或ROOT环境!', complete: ()=>{ //循环弹出提示框或死循环引起应用退出 } }); } // #endif }5.3 uni-app x 项目
uni-app x 项目可通过 uni.getDeviceInfo 返回值中的isRoot属性获取,其兼容性为 Android 3.9+、iOS 4.11+。其中 iOS 表示是否越狱。从源码看,iOS 侧实现直接封装了UTSiOS.isRoot()(见 DeviceUtil.uts),Android 侧则通过DeviceUtil.hasRootPrivilege()判断(见 index.uts)。
六、USB 调试检测:isUSBDebugging
该能力用于判断当前应用运行的设备是否开启 USB 调试,仅 Android 平台支持:
- uni-app 项目不支持获取此状态;
- uni-app x 项目可通过 uni.getDeviceInfo 返回值中的
isUSBDebugging属性获取,其实现通过检测 ADB 监听状态完成(DeviceUtil.listeningForADB(),见 index.uts)。
开启 USB 调试的设备更容易被 ADB 等调试工具接管,可结合代理检测、模拟器检测一起构成登录风控的多维信号。
七、防止校验代码被篡改:混淆加密与加固
上述安全校验都依赖前端 js 代码,而 App 安装包都可以解压,前端资源通常明文存放在安装包中。为防止校验代码被解包后篡改绕过,DCloud 提供了 App 端 js/nvue 文件的原生混淆能力,详见 js/nvue 文件原生混淆加密,要点如下:
- uni-app 项目:在
manifest.json的app-plus.confusion.resources节点下配置要混淆的 nvue/js 文件列表,键名为文件相对路径,值为空对象:
"app-plus": { "confusion": { "description": "原生混淆", "resources": { "common/test.js" : {} } }, // ... }- 在 vue 文件中通过
import引用被混淆的 js;HBuilderX2.6.3+ 版本 v3 编译器支持 vue 页面引用的独立 js 文件原生混淆,HBuilderX2.3.4+ 版本支持 nvue 文件原生混淆。 - 提交云端打包时,必须在 App 云端打包对话框中勾选"对配置的 js 文件进行原生混淆"。由于加密算法和 key 不对外公开,离线打包无法支持原生混淆加密,标准基座/自定义基座真机运行也不支持(仅正式云打包支持);制作 wgt 包同样不支持(HBuilderX3.1.0+ 版本后支持)。
- 没有绝对的安全:非常重要的信息应保存在服务器而非前端;运行期解密会影响性能,不建议全包混淆,只挑选需要保护的个别文件即可。
- 对安全性要求较高的开发者,除前端 js 加密外,还应对整个 apk 再做一次加固,可参考 uni 安全加固,能有效提升应用整体安全性。
八、综合实践与注意事项
8.1 组合使用建议
将本文的 API 按场景组合,可形成一套完整的客户端风控链路:
- 启动阶段(
onLaunch):校验应用签名(防二次打包)+ 检测模拟器 + 检测越狱/root(iOS); - 登录阶段:上报网络代理状态 + 模拟器状态 + USB 调试状态到业务服务器;
- 服务端策略:根据上报的安全信号决定是否放行、二次验证、临时封号或人工核实。
8.2 关键注意事项
- uni-app x 与 plus API 的差异:uni-app x 项目不支持
plus.xxx系列 API,统一改用uni.getAppBaseInfo().signature、uni.getDeviceInfo().isRoot / isSimulator / isUSBDebugging,代理检测(isSetProxy)在 uni-app x 中暂不可用。 - 平台限制:
plus.navigator.isRoot仅 iOS;USB 调试检测仅 Android;这些限制在使用前必须明确。 - 隐私合规:涉及传感器信息采集的
uni.isSimulator必须在隐私协议中声明,并在用户同意后再调用;uni.getDeviceInfo支持filter参数按需获取字段,例如只取品牌时不传isSimulator可避免触发传感器采集(uni.getDeviceInfo({filter:["brand"]}))。 - 代码保护:所有安全校验代码都应放入独立 js 并配置原生混淆加密,配合 apk 加固,避免被反编译篡改绕过。
- 平台能力边界:原生混淆仅限正式云打包生效,且部分老版本编译器存在限制,接入前请核对 HBuilderX 版本要求。
以上所有 API 的返回值与行为,均可结合 uni.getAppBaseInfo、uni.getDeviceInfo、uni.isSimulator 文档以及仓库中的 uni-getAppBaseInfo、uni-getDeviceInfo 模块源码逐一验证。
- 示例工程
- 前端
- 移动开发
- 跨平台
【免费下载链接】uni-app
A cross-platform framework using Vue.js
相关推荐
uni安全加固(uni-app App 应用安全)完整指南:蚂蚁小程序云加壳、签名校验与风险治理实战
uni安全加固(uni app App 应用安全)完整指南:蚂蚁小程序云加壳、签名校验与风险治理实战 uni安全加固是 DCloud 联合业内主流安全厂商为 u
示例工程前端移动开发跨平台uni-app X 模拟器检测指南:uni.isSimulator() API 原理、合规用法与实战示例
uni app X 模拟器检测指南:uni.isSimulator API 原理、合规用法与实战示例 本篇技术指南以 uni app X 提供的 uni.isS
示例工程前端移动开发跨平台uni-app x 推送开发实战:uni-push 客户端 API 全解析与源码级验证
uni app x 推送开发实战:uni push 客户端 API 全解析与源码级验证 uni push 是 DCloud 与个推(GeTui)联合推出的统一推
示例工程前端移动开发跨平台
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考