鸿蒙应用安全——签名、权限与数据保护

一、引言

在移动应用开发中,安全是一个不可回避的话题。从应用签名验证到权限管理,从用户隐私保护到数据传输安全,每一个环节都可能成为安全漏洞的突破口。DriverLicenseExam 项目作为一款集成华为账号登录、定位服务、推送通知等多项敏感功能的应用,在安全方面做了充分的考量。本文将从应用签名、权限管理、数据保护、网络安全四个维度,结合项目源码深入解析鸿蒙应用的安全实践。

二、应用签名——应用的身份证

2.1 签名的作用

鸿蒙应用的签名机制类似于 Android 的 APK 签名,主要目的包括:

  • 身份验证:确认应用的开发者身份
  • 完整性校验:确保应用在发布后未被篡改
  • 信任链建立:系统根据签名决定应用之间的信任关系

2.2 签名配置

在项目根目录的 build-profile.json5 中配置签名信息:

// build-profile.json5 签名配置
{
  "app": {
    "signingConfigs": [
      {
        "name": "default",
        "material": {
          "certPath": "***.cer",
          "keyStorePath": "***.p12",
          "keyStorePassword": "***",
          "keyAlias": "***",
          "keyPassword": "***"
        }
      }
    ],
    "products": [
      {
        "name": "default",
        "signingConfig": "default",
        "compatibleSdkVersion": "5.0.0(12)",
        "runtimeOS": "HarmonyOS"
      }
    ]
  }
}

2.3 签名流程

在 DevEco Studio 中配置签名后,构建 APK 时系统会自动完成签名。开发过程中需要注意:

  1. 调试签名 vs 发布签名:开发阶段使用自动生成的调试证书,发布前必须替换为正式的发布证书
  2. 证书指纹配置:在 AppGallery Connect 中配置签名证书指纹,用于华为服务的鉴权
  3. 密钥保护:p12 密钥库文件需要妥善保管,泄露会导致应用身份被盗用

三、权限管理——最小权限原则

3.1 权限声明

module.json5 中声明应用所需的权限,项目遵循了最小权限原则,只申请真正需要的权限:

{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET",
        "reason": "用于网络请求"
      },
      {
        "name": "ohos.permission.APPROXIMATELY_LOCATION",
        "reason": "用于获取当前位置"
      },
      {
        "name": "ohos.permission.LOCATION",
        "reason": "用于获取精确位置"
      },
      {
        "name": "ohos.permission.GET_NETWORK_INFO",
        "reason": "用于获取网络状态"
      },
      {
        "name": "ohos.permission.READ_CALENDAR",
        "reason": "用于日历提醒"
      },
      {
        "name": "ohos.permission.WRITE_CALENDAR",
        "reason": "用于日历提醒"
      },
      {
        "name": "ohos.permission.APP_TRACKING_CONSENT",
        "reason": "用于广告追踪"
      },
      {
        "name": "ohos.permission.VIBRATE",
        "reason": "用于答题振动反馈"
      }
    ]
  }
}

每个权限的 reason 字段说明了用途,这是鸿蒙系统的强制性要求,用户在授权时会看到这些说明。

3.2 动态权限申请

权限分为安装时授权和运行时授权两类。定位、日历等敏感权限需要在运行时动态申请:

// PermissionUtil.ts - 动态权限申请
export class PermissionUtil {
  private static _atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
  private static _context: Context;

  public static async requestPermissions(permissions: Permissions[]) {
    const needAuthorized: Permissions[] = [];

    // 检查每个权限的状态
    for (let permission of permissions) {
      const isGranted = await PermissionUtil._checkPermissions(permission);
      if (!isGranted) {
        needAuthorized.push(permission);
      }
    }

    if (!needAuthorized.length) return true;

    try {
      // 向用户请求授权
      const data = await PermissionUtil._atManager
        .requestPermissionsFromUser(PermissionUtil._context, needAuthorized);
      let grantStatus: number[] = data.authResults;
      const needAuthorizedAgain: Permissions[] = [];

      for (let i = 0; i < grantStatus.length; i++) {
        if (grantStatus[i] === 0) {
          // GRANTED - 用户已授权
        } else if (grantStatus[i] === -1) {
          // DENIED - 用户拒绝
          needAuthorizedAgain.push(needAuthorized[i]);
        }
      }

      // 对拒绝的权限进行二次授权引导
      PermissionUtil._requestPermissionsOnSetting(needAuthorizedAgain);
    } catch (error) {
      return false;
    }
  }
}

3.3 二次授权引导

鸿蒙系统提供了一种特殊的二次授权机制——引导用户到系统设置页面开启权限:

private static async _requestPermissionsOnSetting(permissions: Permissions[]) {
  // 跳转到系统设置页,引导用户手动授权
  PermissionUtil._atManager.requestPermissionOnSetting(
    PermissionUtil._context, permissions
  ).then((data: abilityAccessCtrl.GrantStatus[]) => {
    Logger.info(TAG, 'request permission on setting success: ' + JSON.stringify(data));
  }).catch((err: BusinessError) => {
    Logger.error(TAG, 'Failed to request permissions on setting: ' + err.message);
  });
}

这种设计相比简单提示用户"去设置页面开启权限",提供了更流畅的用户体验。

四、数据保护——用户隐私的盾牌

4.1 日志脱敏

在开发过程中,日志是定位问题的重要工具。但日志中可能包含用户的敏感信息,必须进行脱敏处理:

// 使用 %{public}s 标记可公开的信息
Logger.info(TAG, 'Succeeded in loading the content.');
Logger.info(TAG, 'Current index: %{public}d', this.currentIndex);

// 使用 %{private}s 标记敏感信息(系统会自动脱敏)
hilog.info(0x0000, TAG, 'User phone: %{private}s', userPhone);

鸿蒙的 hilog 系统提供了内置的脱敏支持:

  • %{public}s:公开信息,正常输出
  • %{private}s:私有信息,在 release 版本中会被自动替换为 {private}

4.2 用户信息保护

// 手机号脱敏显示
AccountUtil.getUserInfo().phone = 'a000******00';

// 不保存密码等敏感信息
public static loginOut() {
  AccountUtil._accountInfo.idToken = '';
  AccountUtil._userInfo.avatar = $r('app.media.user_avatar');
  AccountUtil._userInfo.nickname = $r('app.string.user_name');
  AccountUtil._userInfo.phone = '';
}

退出登录时,除了清理登录凭证,还需要清理本地缓存的用户信息。

4.3 隐私协议的约束力

// 隐私协议勾选控制授权
Checkbox({ name: 'privacyCheckbox' })
  .select(this.isSelected)
  .onChange((value: boolean) => {
    if (value) {
      this.isSelected = true;
      this.controller.setAgreementStatus(loginComponentManager.AgreementStatus.ACCEPTED);
    } else {
      this.isSelected = false;
      this.controller.setAgreementStatus(loginComponentManager.AgreementStatus.NOT_ACCEPTED);
    }
  });

用户必须勾选并同意隐私协议后才能进行华为账号登录,这是一种合规的最佳实践。

五、网络安全——数据传输的保护

5.1 HTTPS 加密传输

项目中的网络请求使用 HTTPS 协议,确保数据传输过程中的安全性:

// AxiosHttpModel 使用 HTTPS
constructor(config: HttpRequestConfig, openMock: boolean = false) {
  this._config = config;
  this._instance = axios.create({
    ...config,
    baseURL: 'https://api.example.com',  // 使用 HTTPS
  });
}

5.2 Mock 数据的用途

// Mock 数据仅用于开发阶段,不上线到生产环境
class HttpApiMock {
  public getUserInfo(): Promise<BaseResponse<GetUserInfoResp>> {
    const resp: BaseResponse = { 
      code: 0, 
      data: { 
        avatar: 'mockImage://ic_default_avatar',
        nickname: '华为用户',
        phone: '1XXXXXX',
      } 
    };
    return Promise.resolve(resp);
  }
}

Mock 数据可以帮助开发者在没有后端服务时进行开发调试,但必须确保 Mock 代码不会出现在生产版本中

六、应用安全最佳实践总结

6.1 安全清单

安全领域 最佳实践 项目中实现
应用签名 使用正式证书签名的 APK 发布 build-profile.json5 配置
权限管理 最小权限原则 + 动态申请 PermissionUtil 二次授权
日志安全 敏感信息脱敏 %{public}s / %{private}s
用户数据 不缓存敏感信息 loginOut 清理数据
隐私合规 用户同意后方可登录 Checkbox + AgreementStatus
网络安全 HTTPS + Mock 隔离 Axios 请求封装

6.2 常见安全风险防范

  1. 逆向工程:使用混淆配置(obfuscation-rules.txt)保护代码
  2. 权限滥用:不在 manifest 中声明不需要的权限
  3. 数据泄露:日志中不输出用户密码、token 等信息
  4. 网络攻击:校验服务器证书,防止中间人攻击

七、总结

应用安全不是某个功能点,而是一个贯穿整个开发流程的工程实践。DriverLicenseExam 项目展示了从编译阶段的签名保护、到运行时的权限管理、再到用户数据的隐私保护,形成了一个完整的安全防护链。开发者在构建鸿蒙应用时,应该从项目初始化阶段就将安全纳入考量,而不是作为上线前的"补丁"。


关键源码文件:

  • build-profile.json5 — 签名配置
  • products/entry/src/main/module.json5 — 权限声明
  • commons/commonLib/src/main/ets/utils/PermissionUtil.ets — 权限工具
  • commons/commonLib/src/main/ets/utils/Logger.ets — 日志工具
  • commons/network/src/main/ets/mocks/HttpApisMock.ets — Mock 数据
Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐