编者按:

2019年至2021年初,先后曝出三起App被质疑自动读取、删除用户相册图片舆情事件,涉及相关事件的应用在舆情发酵的第一时间均做出了回应:造成系统报应用删除图片的原因在于App仅在相关场景中对缓存图片数据进行读写、删除,这一行为本身并未删除图片,而操作系统对删除缓存数据未做区分。

DPO社群的谷元坤和田申撰写了这篇文章。他们提出:用户对于手机存储空间,特别是公共存储空间目录下(手机相册)理论上具有“隐私期待利益”,但是由于手机存储空间具有不同的存储架构分层,且App为实现相关功能或优化产品性能表现,确需处理(访问、读取、擦除)存储空间中的数据,且处理的相关数据可能属于仅为应用运行过程中自动生成或缓存的信息,因此不能一概认为在用户“不知情”的情况下处理手机存储空间中的数据均为侵犯用户隐私,需要根据具体情况进行分析,并规范App对存储空间的访问与对数据的处理活动。

本文作者谷元坤和田申均就职于字节跳动。欢迎其他DPO社群成员赐稿、争鸣。公号君将做好服务工作。

一、Android操作系统存储结构与权限

1、Android操作系统数据存储结构

为了便于对手机存储空间的法律规制进行明晰,我们以Android操作系统为研究对象,将Android系统中的数据存储(手机存储空间)结构进行分析。Android 提供两类物理存储位置,包括内部存储空间和外部存储空间。在大多数设备上,内部存储空间小于外部存储空间。不过,所有设备上的内部存储空间都是始终可用的,因此在存储APP所依赖的数据时更为可靠。可移除卷(例如 SD 卡)在文件系统中属于外部存储空间,其文件并非一直可以访问,因为有些设备允许用户移除提供外部存储空间的实体设备。

  • Android系统数据存储结构如下图:

内部存储空间(data)目录:这些目录既包括用于存储持久性文件的专属位置,也包括用于存储缓存数据的其他位置。Android系统会阻止其他应用访问这些位置,并且在 Android 10(API 级别 29)及更高版本中,系统会对这些位置进行加密。这些特征使得这些位置非常适合存储只有App本身才能访问的敏感数据。

外部存储空间(storage)目录:这些目录既包括用于存储持久性文件的专属位置,也包括用于存储缓存数据的其他位置。虽然其他应用可以在具有适当权限的情况下访问这些目录,但存储在这些目录中的文件仅供应用本身使用。如果应用明确打算创建其他应用能够访问的文件,应改为将这些文件存储在外部存储空间的共享存储空间部分。

2、APP数据存储类型及访问所需权限

Android 使用的文件系统类似于其他平台上基于磁盘的文件系统。该系统提供了以下几种保存应用数据的选项。

  • 应用专属存储空间:存储仅供应用使用的文件,可以存储到内部存储卷中的专属目录或外部存储空间中的其他专属目录。使用内部存储空间中的目录保存其他应用不应访问的敏感信息。

  • 共享存储:存储应用打算与其他应用共享的文件,包括媒体文件和其他共享文件。

  • 偏好设置:以键值对形式存储私有原始数据。

  • 数据库:使用 Room 持久性库将结构化数据存储在专用数据库中。

相关名词解释

键值对:是数据库最简单的组织形式。“键”就是存的值的编号,“值”就是要存放的数据。

应用设置偏好:开发的软件需要软件参数设置功能,用户根据自己的偏好,选择合适的参数,以键值对的方式保存。这种相对较小的键值对集合,应使用SharedPreferences API。SharedPreferences对象指向包含键值对的文件,并提供读写这些键值对的简单方法。

结构化数据:顾名思义,是高度组织和整齐格式化的数据,与非结构化数据相比,是人们更容易使用的数据类型,计算机也可以轻松地搜索它。

Room持久性数据库:Room提供了一个SQLite之上的抽象层,从而在充分利用SQLite的前提下顺畅的访问数据库。对于需要处理大量结构化数据的App来说,把这些数据做本地持久化会带来很大的好处。常见的用例是缓存重要数据块。这样当设备无法连网的时候,用户仍然可以浏览内容。而用户对内容做出的任何植入都在网络恢复的时候同步到服务端。

二、Android操作系统数据存储空间的隐私期待利益边界

一般认为,对于是否具有“隐私期待利益”的判断规则有两个构成要件:首先是主观要件,即个人是否认为,并且通过行为表现出其确实享有对某事务在主观上的隐私期待;其次是客观要件,即从一般公众视角看是否认可其所主张的隐私期待是“合理的”。

第一,通过前述对Android操作系统的存储空间分层关系可以看到,在公共存储空间公有目录下的媒体文件,用户应当具有最高的隐私期待,因为这些数据通常为用户主动访问、写入,存储的信息具有用户“不愿为他人所知悉”的特征,同时这些信息的访问需要通过用户授权相关权限,因此App不能在无用户授权或超越用户授权范围的情形下访问、删除媒体文件数据。

第二,App在公共存储空间公有目录下写入的参数一般不应写入可以标示设备的参数或可能导致用户被跨站追踪的参数,避免被其他App读取导致用户个人信息泄露。

第三,在公共空间内私有目录中的信息,虽然其他App可以在一定程度上访问,但是在无正当理由或保护更高利益的前提下不得收集其他App在这一目录下的文件名等信息。

第四,在公共存储空间缓存目录下的文件清理应当谨慎,需要有明确的合法目的及满足必要性要求,并且应当注意与操作系统的“隐私保护”提示功能的适配,避免用户陷入隐私忧虑。

第五,在App的私有存储空间内的目录下,用户的隐私期待边界一般为:可以理解被本App访问、收集、写入、删除数据,因此,App对自身私有空间内数据的上述处理,需要与提供服务的目的相关,并遵循最小化原则。

三、常见存储与访问的违法情形分析

本文将各情形定性为“不合法”或“不规范”:

  • “不合法”是指该情形违反Android开发规范,且违反我国法律或行政法规,导致用户数据处于极不安全的状态;

  • “不规范”是指违反Android开发规范,但该独立行为一般不足以危害用户数据安全,但可能引发违反法律法规的情形。

此处需要说明的是,法律对APP服务进行规制大多是从保护用户个人信息的角度,而用户手机内的个人信息也是存放于手机存储中,当APP要求用户授予访问存储的权限时,并不会向用户详细说明访问的存储乃内存或外存、缓存或持久性文件,因而用户在同意授权的时候往往默认的是APP可以访问自己的所有存储空间,即所有个人信息。从这个角度分析,我国法律或行政法规中大多关于用户个人信息的条款,在空间存储方面也可适用。

1、存储方式相关

(1)将敏感数据长期存储在缓存文件中

不合法,极易导致用户数据丢失。

技术方面:应用在存储空间不够时,会自动清理缓存文件。如果只需要暂时存储敏感数据,应使用应用在内部存储空间中的指定缓存目录保存数据。

法律方面:《网络安全法》第四十二条规定,“网络运营者应当采取技术措施和其他必要措施,确保其收集的个人信息安全,防止信息泄露、毁损、丢失”。

(2)在 logcat 消息或应用的日志文件中包含敏感数据

不合法,极易导致用户数据泄露。

技术方面:在 Android 中,日志是共享资源,拥有READ_LOGS权限的所有应用均可访问。即使电话日志数据是临时数据并会在重新启动时清空,不当记录用户信息也可能在无意之中将用户数据泄露给其他应用。

法律方面:《网络安全法》第四十二条规定,“网络运营者应当采取技术措施和其他必要措施,确保其收集的个人信息安全,防止信息泄露、毁损、丢失”。

(3)包含敏感数据的文件存储在外部存储空间中

不规范。Android开发者文档中指出,包含敏感数据的文件应位于内部存储空间内的应用私有目录中。外部存储设备用于存储应用专属的大型非敏感文件,以及应用与其他应用共享的文件。

(4)将缓存文件存储在公共目录中

不规范。如果对图片进行缓存,应该把图片缓存放到/data/data/应用包名/cache/或者SDCard/Android/data/应用包名/cache/,系统空间不够时会被自动清除。

2、应用访问用户存储数据

(1)私自删除用户文件为应用获取空间

不合法,会造成用户数据丢失。

技术方面:应用应谨慎使用存储空间,应当在写入数据之前查询设备可以为应用提供多少空间,如果有足够的空间,应调用 allocateBytes() 声明空间。否则,应用可以调用包含 ACTION_MANAGE_STORAGE 操作的 intent,此 intent 会向用户显示提示,要求他们在设备上选择要移除的文件,以便应用拥有所需空间。

法律方面:《网络安全法》第四十一条规定,“网络运营者……应当依照法律、行政法规的规定和与用户的约定,处理其保存的个人信息”。第四十二条规定,“网络运营者不得泄露、篡改、毁损其收集的个人信息”。

(2)随意传输或存储用户敏感数据

不合法,容易导致用户数据泄露。

技术方面:如果应用获得了用户数据的访问权限,并且能够避免存储或传输此类数据,则不要存储或传输此类数据。应首先评估应用逻辑能否使用经过哈希算法处理或不可逆的数据格式进行实现。

法律方面:《网络安全法》第四十二条规定,“未经被收集者同意,不得向他人提供个人信息。但是,经过处理无法识别特定个人且不能复原的除外”。工信部《工业和信息化部关于开展APP侵害用户权益专项整治工作的通知》也指出,要对“私自将用户信息共享给第三方”的APP进行整顿,即未经用户同意与其他应用共享、使用用户个人信息,如设备识别信息、商品浏览记录、搜索使用习惯、常用软件应用列表等。

(3)不加验证得处理来自外部存储的数据

不合法,容易导致用户数据泄露。

技术方面:Android开发者文档指出,处理来自外部存储设备的数据时,应执行输入验证,不应在动态加载前将可执行文件或类文件存储在外部存储设备中。如果应用确实从外部存储设备中检索可执行文件,应在动态加载前对这些文件执行签名和加密验证。

法律方面:《网络安全法》第四十二条规定,“网络运营者应当采取技术措施和其他必要措施,确保其收集的个人信息安全,防止信息泄露、毁损、丢失”。

(4)将用户数据传输到服务器

• 不规范。如果应用需要访问敏感数据,应对进行必要性评估应用是需要将其传输到服务器,还是可以在客户端执行相应操作。

• Android开发者文档指出,建议在客户端运行所有需要使用敏感数据的代码,以避免传输用户数据。

3、应用访问其他应用数据

(1)不经授权访问其他应用数据

不合法,会造成用户数据泄露。

技术方面:内部存储空间只有应用本身才能访问,系统会对其加密,如需允许其他应用访问存储在内部存储空间内此目录中的持久性文件,可以使用有 FLAG_GRANT_READ_URI_PERMISSION 属性的 FileProvider;其他应用可以在具有适当权限的情况下访问这些目录,但存储在这些目录中的文件只供应用本身使用。

法律方面:此行为可定义为超出授权范围访问用户存储空间。《网络安全法》第四十二条规定,“网络运营者……不得违反法律、行政法规的规定和双方的约定收集、使用个人信息”。

(2)使用隐式Intent给另一个应用传递敏感数据

不规范。在Android开发过程中,常常遇到需要将用户账号等敏感数据传递给另一个应用的场景,此时用户数据就会暴露在网络传输中。Android开发者文档指出,此时的数据传递不应使用隐式Intent,而应选择显式Intent。因为使用隐式Intent时,Android系统通过将Intent的内容与在设备上其他应用的清单文件中声明的Intent过滤器进行比较,从而找到要启动的相应组件。此时,任何人都无法确定哪些服务将响应Intent,且用户无法看到哪些服务已启动,因而用户的敏感数据会有泄露的风险。

(3)使用SharedPreferences对象在应用之间共享数据

不规范。有许多场景需要在应用程序之间共享数据,比如多个应用共用用户名密码进行登录等。根据Android开发者文档,如果要在应用之间共享数据,不应使用SharedPreferences对象进行传输。从SharedPreferences的具体设计角度分析,由于其没有使用跨进程的锁,就算使用MODE_MULTI_PROCESS,SharedPreferences在跨进程频繁读写有可能导致数据全部丢失。根据线上统计,SharedPreferences大约会有万分之一的损坏率。因而其并不能保证跨进程数据的安全,也就是在跨进程访问时可能会导致文件损坏。

(4)不加限制得跟其他应用共享应用的内部存储数据

不规范。首先,APP在功能设计上可能需要在不同应用中共享图片、音频、联系人,开发者在处理此类与其他应用进程共享数据的场景时,可以使用内容提供程序,其不但可以为其他应用提供读取和写入权限,还能针对各种具体情况授予动态权限。其次,在共享应用内容的过程中,应根据需要强制实施只读或只写权限,使用FLAG_GRANT_READ_URI_PERMISSION 和 FLAG_GRANT_WRITE_URI_PERMISSION 标记,为客户端提供对数据的一次性访问权限。

相关名词解释

Intent:Intent 是一个消息传递对象,可以用来从其他应用组件请求操作。intent分为显式Intent和隐式Intent,显式Intent是通过提供目标应用的软件包名称或完全限定的组件类名来指定可处理Intent的应用,因此如果知道要启动的Activity或服务的类名,通常会在应用中使用显式Intent来启动组件。而隐式Intent不会指定特定的组件,而是声明要执行的常规操作,从而允许其他应用中的组件来处理。

内容提供程序:提供程序是Android的一个应用组件,通常提供自己的UI来管理数据。内容提供程序管理对结构化数据集的访问。它们封装数据,并提供用于定义数据安全性的机制。内容提供程序是连接一个进程中的数据与另一个进程中运行的代码的标准界面。

四、Android10以上版本的分区存储及使用规范

为了让用户更好地管理自己的文件并减少混乱,以 Android10(API 级别 29)及更高版本为目标平台的应用在默认情况下被赋予了对外部存储空间的分区访问权限(即分区存储)。此类应用只能访问外部存储空间上的应用专属目录,以及本应用所创建的特定类型的媒体文件(如果分区不可用,可以访问所有类型的媒体文件)。

如果使用分区后,应用需要访问其他应用创建的文件,必须满足以下条件:

  • 应用已获得READ_EXTERNAL_STORAGE权限。

  • 这些文件位于以下其中一个明确定义的媒体集合中:

▪ MediaStore.Images

▪ MediaStore.Video

▪ MediaStore.Audio

  • 如果用户卸载并重新安装应用,必须请求 READ_EXTERNAL_STORAGE 才能访问应用最初创建的文件,因为系统认为文件属于以前安装的应用版本,而不是新安装的版本。

  • 如果应用使用分区存储,将无法更新或删除其他应用存放到媒体库中的媒体文件。不过,应用仍可通过捕获平台抛出的 RecoverableSecurityException 来征得用户同意修改文件。

参考文档:

[1] Android开发者文档

[2] Android6.0 动态权限组

[3] 工业和信息化部关于开展APP侵害用户权益专项整治工作的通知

[4] 网络安全实践指南——移动互联网应用基本业务功能必要信息规范

[5] 谷歌官方Android应用架构库-房间持久化库

[6] Android 之不要滥用 SharedPreferences(上)

[7] Android 之内容提供程序(Content Provider)

[8] ContentProvider官方教程(1)何时用content provider

声明:本文来自网安寻路人,版权归作者所有。文章内容仅代表作者独立观点,不代表安全内参立场,转载目的在于传递更多信息。如有侵权,请联系 anquanneican@163.com。