HarmonyOS:应用数据安全
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
➤博客园地址:为敢技术(https://www.cnblogs.com/strengthen/ )
➤GitHub地址:https://github.com/strengthen
➤原文地址:https://www.cnblogs.com/strengthen/p/18519105
➤如果链接不是为敢技术的博客园地址,则可能是爬取作者的文章。
➤原文已修改更新!强烈建议点击原文地址阅读!支持作者!支持原创!
★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★
数据安全概述
应用的安全性是一个应用能否成功的关键。
HarmonyOS提供了系统安全、IDE/工具安全以及应用安全生态等三个层面的安全能力。
- 在系统安全层面,HarmonyOS通过完整性保护、漏洞防利用、安全可信环境等安全防护技术,从架构上支持了应用的安全运行,保证其自身业务的安全可靠(例如安全支付、安全登录、安全聊天等)。
- 在IDE/工具层面,生态开发者的应用来自不同的开发者和不同用途, 除了好的生态应用以外,也存在恶意利用生态开放进行牟利的黑产、诈骗应用、恶意营销广告推广(恶意弹框)等各种风险应用,为了生态的应用安全纯净可控, HarmonyOS将以端到端的安全可控的生态模式进行构建。
- 在应用安全生态层面HarmonyOS通过各种软硬件基础设施支持应用的安全性。具体来说通过下列方案实现对敏感数据存储和用户隐私保护:
- 敏感数据等级划分:通过设备等级划分和数据的敏感分级保护用户的数据安全,并使分布式设备间数据按分级正确流动。
- 文件分级保护:应用根据其自身需求,按照数据的安全等级,把数据保存到系统相应的加密目录,由系统保证数据的安全性。
- 关键资产数据加密保护:针对关键敏感数据,为用户提供基于底层TEE级别系统安全保护;提供关键敏感数据管理API,开发者无需关注底层具体安全实现。
下面我们讨论应用安全生态层面的敏感数据存储和用户隐私保护的实践。
风险等级划分
HarmonyOS安全能力是以分级安全为架构思想的基础安全底座,面向业务场景构建HarmonyOS安全的应用生态。其中的分级分为两个维度:一是设备的安全等级划分,二是数据的安全等级划分。
- 设备的安全等级划分的常用业务场景中分布式是一个非常重要的业务场景。在分布式里面最大问题是不同的设备的安全等级不一样,比如A设备为手机,手机的安全等级通常较高,B设备为手表,手表的安全等级较低,如果数据要在A设备和B设备之间互相流动,那么需要先对设备进行分级,以保证数据在跨设备之间流动的安全性。
- 另一方面,对流动的数据也需要进行分级,比如说数据能不能流动到普通的设备上,敏感程度不同的数据对流动的限制也不一样。这些需要系统在系统层面决定,而不是在应用层面去划分。如果在应用层面去划分数据等级且应用开发者对数据的处理如果没有等级的概念,那会导致整个系统的数据的泄露非常严重。
设备等级划分
根据设备安全能力,比如是否有TEE、是否有安全存储芯片等,将设备安全等级分为SL1、SL2、SL3、SL4、SL5五个等级。例如,智能穿戴通常为低安全的SL1设备,手机、平板通常为高安全的设备。
设备从SL1到SL5分级,在完整性保护、加密及数据保护、权限及访问控制、可信执行环境和漏洞防利用这几个维度对应的安全能力要求逐渐提高。
数据等级划分
敏感数据分类可帮助应用开发者根据数据的敏感性、价值以及数据泄露时的潜在影响来识别和分类数据。开发者需要了解不同类型的数据及其使用方式,并对数据进行分类,针对分类制定适当的安全措施和控制措施来保护数据并确保遵守相关法规和标准。下表举例说明了个人数据的分类:
个人数据分类 |
数据分级 |
举例 | |
---|---|---|---|
个人/敏感数据 |
关键资产数据 |
严重 |
口令、认证TOKEN、密保问题答案等 |
个人种族数据 |
种族血统 |
||
权威社会识别标识 |
高 |
身份证号码、军官证、社会/保险保障号码(社会号码)、驾照、护照号码、签证授权编号 |
|
负向名誉数据 |
犯罪记录(刑事、民事犯罪和诉讼记录),入狱记录、纪律处分 |
||
健康数据 |
健康数据(身高、体重、体脂、血压、血糖、心率等)、医疗记录、性生活 |
||
家居控制数据 |
家居控制、HICar反向控制、手机投屏后反向控制等 |
||
年龄生辰数据 |
中 |
年龄,出生日期 |
|
虚拟网络身份标识 |
华为帐号、宽带帐号、其他网络帐号(电话号码、邮箱) |
||
一般社会识别标识 |
姓名 |
||
个人多媒体数据 |
照片、视频、录音、文字、日历日程、备忘录文本等 |
||
一般注册信息 |
低 |
昵称、头像、性别、国籍、出生地、教育程度、专业背景等 |
|
正向名誉数据 |
专业成就 |
||
非个人数据 |
匿名化数据 |
公开 |
匿名化处理后的个人数据 |
非个人数据 |
系统、设备信息中公开发布的数据,如:TCB模块的版本信息、完整性度量值、访问控制策略数据的完整性度量值、策略数据本身等 |
除了基于内容对数据分类,开发者还需遵守相关的法律法规,如通用数据保护条例(GDPR)和个人信息保护法等,以保护用户的隐私权和数据安全。下图是基于法律法规的数据分类图:
按照数据分类分级规范要求,可将数据分为S1、S2、S3、S4四个安全等级。
风险等级 |
风险标准 |
定义 |
样例 |
---|---|---|---|
严重 |
S4 |
业界法律法规定义的特殊数据类型,涉及个人的最私密领域的信息或一旦泄露、篡改、破坏、销毁可能会给个人或组织造成重大的不利影响的数据。 |
政治观点、宗教和哲学信仰、工会成员资格、基因数据、生物信息、健康和性生活状况,性取向等或设备认证鉴权、个人信用卡等财物信息等。 |
高 |
S3 |
数据的泄露、篡改、破坏、销毁可能会给个人或组织导致严峻的不利影响 |
个人实时精确定位信息、运动轨迹等。 |
中 |
S2 |
数据的泄露、篡改、破坏、销毁可能会给个人或组织导致严重的不利影响 |
个人的详细通信地址、姓名昵称等。 |
低 |
S1 |
数据的泄露、篡改、破坏、销毁可能会给个人或组织导致有限的不利影响 |
性别、国籍、用户申请记录等。 |
数据安全标签和设备安全等级越高,加密措施和访问控制措施越严格,数据安全性越高。
数据跨设备同步时,数据管理基于数据安全标签和设备安全等级进行访问控制。规则为,在本设备的数据安全标签不高于对端设备的设备安全等级时,数据才能从本设备同步到对端设备,否则不能同步。具体访问控制矩阵如下:
设备安全级别 |
可同步的数据安全标签 |
---|---|
SL1 |
S1 |
SL2 |
S1~S2 |
SL3 |
S1~S3 |
SL4 |
S1~S4 |
SL5 |
S1~S4 |
例如,手表通常为低安全的SL1设备。若创建数据安全标签为S1的数据库,则此数据库数据可以在这些设备间同步;若创建的数据库标签为S2-S4,则不能在这些设备间同步。
分级数据加密
分级数据保护
不同的文件路径具备不同的属性和特征。分级数据使用应用沙箱作为保护机制,避免数据受到恶意路径穿越访问。在应用文件目录中,根据不同的文件加密类型,区分了不同的目录。
- el1,设备级加密区:设备开机后即可访问的数据区。
- el2,用户级加密区:设备开机后,需要至少一次解锁对应用户的锁屏界面(密码、指纹、人脸等方式或无密码状态)后,才能够访问的加密数据区。
应用如无特殊需要,应将数据存放在el2加密目录下,以尽可能保证数据安全。但是对于某些场景,一些应用文件需要在用户解锁前就可被访问,例如时钟、闹铃、壁纸等,此时应用需要将这些文件存放到设备级加密区(el1)。切换应用文件加密类型目录的方法请参见获取和修改加密分区。
应用文件目录的详细介绍请参考应用文件目录。
分级数据文件路径使用应用通用文件路径,获取路径代码如下:
getEL2Path() { let context = getContext(this) as common.UIAbilityContext; context.area = contextConstant.AreaMode.EL2; let file_path = context.filesDir + '/health_data.txt'; this.message = file_path; }
如果需要获取el1的路径者需要修改AreaMode,代码如下:
getEl1Path() { let context = getContext(this) as common.UIAbilityContext; context.area = contextConstant.AreaMode.EL1; let file_path = context.filesDir + '/health_data.txt'; this.message = file_path; }
应用如无特殊需要,应将数据存放在el2加密目录下。
系统的文件级加密提供了4种加密类型实现文件保护,应用可以根据自己的述求,把文件保存到相应的数据目录。下表是各个分级加密区所对应的策略:
分级加密 |
策略 |
---|---|
el4 |
用户锁定设备后不久(一般为 10 秒钟),解密的数据保护类密钥会被从内存丢弃,此类的所有数据都无法访问,除非用户再次输入密码或使用指纹或面容解锁设备。 |
el3 |
用户锁定设备后,如果文件已经被打开,则文件始终可以被继续访问,一旦文件关闭(锁屏),文件将不能被再次访问,除非用户再次输入密码或使用指纹或面容解锁设备 |
el2 |
用户开机后首次解锁设备后,即可对文件进行访问。这是未分配给数据保护类的所有第三方应用数据的默认数据保护类 |
el1 |
设备在直接启动模式下和用户解锁设备后均可对文件进行访问 |
数据加密算法
通用密钥库系统(OpenHarmony Universal KeyStore,简称HUKS),向业务提供了平台级的密钥管理能力,包括密钥的生成、导入、销毁、证明、协商、派生、加密/解密、签名/验签及访问控制等功能。HUKS是系统的底座安全能力,业务无需自己实现复杂的密码学算法、密钥访问控制模型,即可安全、便捷地使用HUKS提供的密钥全生命周期管理能力,进而保证业务敏感数据的安全性。
使用数据加密算法实现数据加密请参考通用加密库。
数据安全案例
健康数据是日常生活中常见的敏感数据,并且在HarmonyOS设备中广泛存在,如手机,手表等。下面介绍体检报表的存储和保护的案例。
场景设计
根据数据分类规则,体检数据被归类为高风险级别的数据。因此,在程序设计过程中,除了采用分级数据加密的保护措施外,还需要进行二次加密以加强数据的安全性。具体而言,可以先对数据进行一次加密,然后再使用分级数据加密的方法对加密后的数据进行处理。这样做的目的是为了提高数据的保密性和完整性,有效地防止潜在的数据泄露和非法访问。通过这样多层次的加密措施,体检数据将更加安全可靠地存储和传输。
程序的场景主要由三个页面构成,分别为体检列表页、数据录入页和数据详情页,页面如下:
场景开发
在实际开发中,数据的加密与解密经常是同时存在的,本场景中也是一样,由两个子场景组成,数据录入和数据详情。下面分别介绍这两个子场景的具体实现方式:
- 数据录入的实现方式
- 在列表页中点击录入按钮,进入数据录入页。
- 在数据录入页中录入信息。录入的信息以标签提示字符为key,把数据组装成json对象。
- 对录入的信息进行加密。然后把上一步骤中把组装好的json对象系列化成字符串,然后把字符串转换为字节数组,使用huks接口对字节数组进行加密,得到加密后的字节数组。
- 对加密后的数据进行分级加密。在上一步中得到的加密后字节数组使用fileio接口写入分级加密文件中。
- 数据详情的实现方式
- 在列表页点击相应的数据项,进入数据详情页,通过pushUrl接口的params参数把分级加密文件的文件名传给数据详情页。
- 对加密数据进行解密。根据params传入的分级加密文件名调用fileio接口读取加密数据,然后调用huks接口解密读取的字节数组,得到解密后的字节数组。
- 展示数据详情。把解密后的字节数组转换为字符串,然后把字符串转换为json对象,最后把json对象展示到页面。
代码实现
- 分级加密数据读写
参考分级数据保护中代码获取到数据路径后按正常的文件读写即可,以下是文件读写的代码:
function writeFile(file_path: string, data: string) { let file = fileIo.openSync(file_path, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE); let writeLen = fileIo.writeSync(file.fd, data); hilog.info(0x0000, 'AppDataSecurity', 'The length of str is: ' + writeLen); fileIo.closeSync(file); } function readFile(file_path: string): object { let file = fileIo.openSync(file_path, fileIo.OpenMode.READ_WRITE | fileIo.OpenMode.CREATE); let arrayBuffer = new ArrayBuffer(1024); class Option { public offset: number = 0; public length: number = 0; } let option = new Option(); option.length = arrayBuffer.byteLength; let readLen = fileIo.readSync(file.fd, arrayBuffer, option); let buf = buffer.from(arrayBuffer, 0, readLen); hilog.info(0x0000, 'AppDataSecurity', `The length of of file: ${readLen}`); fileIo.closeSync(file); return [readLen, buf.toString()] }
- 数据加解密
在使用huks之前要先配置加密算法的规格,本案例中选择的加解密算法规格为密钥长度为128的AES算法。在使用加解密算法计算前要先生成算法key,以下是算法配置和生成算法key代码:
在生成算法key的配置函数中需要指出算法类型HUKS_ALG_AES,算法key的大小以及密钥用途:
function GetAesGenerateProperties() { let properties: Array<huks.HuksParam> = [{ tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_AES }, { tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, value: huks.HuksKeySize.HUKS_AES_KEY_SIZE_128 }, { tag: huks.HuksTag.HUKS_TAG_PURPOSE, value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT | huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT }]; return properties; }
加密函数配置的算法规格需要跟生成算法key的配置保持一致。
HUKS_TAG_PADDING填充模式有三种不同填充模式。
- NoPadding:不带填充;
- PKCS5:填充字符由一个字节序列组成,每个字节填充该填充字节序列的长度,规定8字节填充;
- PKCS7:填充字符和PKCS5填充方法一样,但是可以在1-255字节之间任意填充。
HUKS_TAG_BLOCK_MODE有7种分组模式,对于CBC、CTR、OFB、CFB模式,仅使用HUKS_TAG_IV(密钥初始化的向量)加解密参数;对于GCM、CCM需要设置HUKS_TAG_NONCE(密钥加解密的字段)、HUKS_TAG_ASSOCIATED_DATA(附加身份验证数据的Tag )。并且需要配置HUKS_TAG_IV密钥初始化向量选项。
function GetAesEncryptProperties() { let properties: Array<huks.HuksParam> = [{ tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_AES }, { tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, value: huks.HuksKeySize.HUKS_AES_KEY_SIZE_128 }, { tag: huks.HuksTag.HUKS_TAG_PURPOSE, value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_ENCRYPT }, { tag: huks.HuksTag.HUKS_TAG_PADDING, value: huks.HuksKeyPadding.HUKS_PADDING_PKCS7 }, { tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE, value: huks.HuksCipherMode.HUKS_MODE_CBC }, { tag: huks.HuksTag.HUKS_TAG_IV, value: StringToUint8Array(IV) }]; return properties; }
解密函数配置的算法规格需要跟加密算法的配置保持一致,不同点是需要设置密钥用途为解密。
function GetAesDecryptProperties() { let properties: Array<huks.HuksParam> = [{ tag: huks.HuksTag.HUKS_TAG_ALGORITHM, value: huks.HuksKeyAlg.HUKS_ALG_AES }, { tag: huks.HuksTag.HUKS_TAG_KEY_SIZE, value: huks.HuksKeySize.HUKS_AES_KEY_SIZE_128 }, { tag: huks.HuksTag.HUKS_TAG_PURPOSE, value: huks.HuksKeyPurpose.HUKS_KEY_PURPOSE_DECRYPT }, { tag: huks.HuksTag.HUKS_TAG_PADDING, value: huks.HuksKeyPadding.HUKS_PADDING_PKCS7 }, { tag: huks.HuksTag.HUKS_TAG_BLOCK_MODE, value: huks.HuksCipherMode.HUKS_MODE_CBC }, { tag: huks.HuksTag.HUKS_TAG_IV, value: StringToUint8Array(IV) }]; return properties; }
完成上述配置后,调用生成算法key函数。
async function GenerateAesKey() { let genProperties = GetAesGenerateProperties(); let options: huks.HuksOptions = { properties: genProperties }; await huks.generateKeyItem(aesKeyAlias, options) .then((data) => { hilog.info(0x0000, 'AppDataSecurity', `promise: generate AES Key success, data = ${JSON.stringify(data)}`); }).catch((error: Error) => { hilog.error(0x0000, 'AppDataSecurity', `promise: generate AES Key failed, ${JSON.stringify(error)}`); }) }
生成算法key后即可进行加解密,在加解密中采用同样的步骤,先获取相应的配置,然后调用initSession和finishSession实现加解密操作。
async function EncryptData() { let encryptProperties = GetAesEncryptProperties(); let options: huks.HuksOptions = { properties: encryptProperties, inData: StringToUint8Array(plainText) }; await huks.initSession(aesKeyAlias, options) .then((data) => { handle = data.handle; }).catch((error: Error) => { hilog.error(0x0000, 'AppDataSecurity', `promise: init EncryptData failed, ${JSON.stringify(error)}`); }) await huks.finishSession(handle, options) .then((data) => { hilog.info(0x0000, 'AppDataSecurity', `promise: encrypt data success, data is ` + Uint8ArrayToString(data.outData as Uint8Array)); cipherData = data.outData as Uint8Array; }).catch((error: Error) => { hilog.error(0x0000, 'AppDataSecurity', `promise: encrypt data failed, ${JSON.stringify(error)}`); }) } async function DecryptData() { let decryptOptions = GetAesDecryptProperties() let options: huks.HuksOptions = { properties: decryptOptions, inData: cipherData }; await huks.initSession(aesKeyAlias, options) .then((data) => { handle = data.handle; }).catch((error: Error) => { hilog.error(0x0000, 'AppDataSecurity', `promise: init DecryptData failed, ${JSON.stringify(error)}`); }) await huks.finishSession(handle, options) .then((data) => { hilog.info(0x0000, 'AppDataSecurity', `promise: decrypt data success, data is ` + Uint8ArrayToString(data.outData as Uint8Array)); }).catch((error: Error) => { hilog.error(0x0000, 'AppDataSecurity', `promise: decrypt data failed, ${JSON.stringify(error)}`); }) }
在体检报表中有敏感数据,因此需要在对数据进行二次加密再进行分级加密,解密得过程顺序与加密相反,先读取分级加密的数据,再进行解密。在涉及的敏感的数据密级不同的时候,可以对不同的字符按密级分别封装序列化,避免在数据流动的时候可能把高密级的数据流动到低密级的设备中,造成数据泄露。
这里为了方便日志打印输出,对加密后的数据进行base64转码,在应用开发中此步骤可以省略。
总结与回顾
随着信息技术的不断发展,数据的价值和重要性日益凸显,因此保护数据安全已成为应用开发中的重要课题。数据安全保护需要综合考虑分析评估和设计开发两个阶段的工作:
- 对应用中涉及的各类数据进行全面的风险评估和分类分析,根据数据的敏感程度和重要性确定相应的安全保护策略。
- 采用分级数据保护和合适的加密算法规格 ,避免敏感数据泄露。
本文中,针对体检报表中的数据将其进行安全等级划分后,根据定义的的等级采取了对应的加密。
- 数据安全分级是一种基于数据重要性和敏感程度的分类保护方法。通过对数据进行分类,根据其重要性和敏感程度采取相应的安全措施,以确保数据的安全性。在实际应用中,开发人员应根据实际业务场景,选择合适的数据分级保护策略,从而降低数据管理的复杂度,并提高数据的安全性。例如,对于一些敏感的个人隐私数据,可以采取严格的访问控制策略,以防止未经授权的访问和泄露。
- 数据加密是一种常用的数据安全技术,通过对数据进行加密处理,使得未经授权的用户无法直接获取和理解数据内容,从而保障数据的机密性。在应用开发中,合理选择加密算法和规格对数据进行加密处理非常关键。开发人员需要平衡加密数据的安全性和计算复杂度,以确保在保障数据安全的同时不影响用户的正常使用体验。此外,还需要考虑数据的传输加密和存储加密,以全面保护数据的安全。
在实际应用开发中,开发者要重视数据安全工作,不断完善数据安全保护措施,为用户的数据安全和隐私保护提供更加可靠的保障。
示例代码