IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android数据存储安全规范 -> 正文阅读

[移动开发]Android数据存储安全规范

标题理论概述

存储数据对于移动应用程序至关重要,应将尽可能少的敏感数据存储在永久性本地存储中,但是在大多数实际场景中,必须存储某种类型的用户数据。如果敏感数据没有受到应用程序适当保护,就很容易受到攻击。

保护身份验证Token、私人信息和其他敏感数据是移动应用安全的关键。

保存数据的准则概括:公共数据应该对所有人可用,但敏感和私有数据必须受到保护,或者更好的是将敏感数据存储在设备存储之外。

除了保护敏感数据之外,还需要确保从任务存储源读取的数据都应该经过验证。

数据存储方法概述

Android根据用户需求提供了多种数据存储的方法,以下介绍 Android平台常用的几种数据存储方法:

Shared Preferences

SQLite Databases

Firebase Databases

Realm Databases

Internal Storage

External Storage

Keystore

Shared Preferences

SharedPreferences API通常用于永久保存小的集合,用Key- Value的形式存储。数据存储通过SharedPreferences 对象写入XML文件。SharedPreferences 对象有两种模式 world-readable(所有App都可以访问)和private。误用SharedPreferences API常常会导致敏感数据暴漏。

参考例子:

SharedPreferences sharedPref = getSharedPreferences("key", MODE_WORLD_READABLE);
SharedPreferences.Editor editor = sharedPref.edit();
editor.putString("username", "administrator");
editor.putString("password", "supersecret");
editor.commit();

一旦代码被执行,key.xml文件将被创建,并存储数据。这段代码违反了几种最佳实践:

  • The username and password are stored in clear text in
    /data/data/<packagename>/shared_prefs/key.xml.

username和password以明文的形式存储在/data/data//shared_prefs/key.xml.

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<string name="username">administrator</string>
<string name="password">supersecret</string>
</map>
MODE_WORLD_READABLE allows all applications to access and read the contents of
key.xml.

MODE_WORLD_READABLE 允许所有应用去访问和读取 key.xml 的内容。
root@hermes:/data/data/sg.vp.owasp_mobile.myfirstapp/shared_prefs # ls -la-rw-rw-r-- u0_a118 170 2016-04-23 16:51 key.xml

请注意 MODE_WORLD_READABLE 和 MODE_WORLD_WRITEABLE 在 API 17 已被弃用。尽
管较新的设备可能不受此影响,但使用 Android:targetSdkVersion 值小于 17 编译的应用
程序,如果运行在 Android 4.2 之前发布的操作系统版本上运行,则可能会受到影响。

最佳实践

  • 强烈建议使用MODE_PRIVATE。
    getSharedPreferences(“filename”,Context.MODE_PRIVATE);
  • Encrypting Shared Preferences with the AndroidX Security Library
    对于敏感数据存储,使用AndroidX 安全库提供的EncryptedSharedPreferences

备注: 仅支持minSdkVersion 23及以上。
使用SharedPreferences存储数据是明文的,当我们存储敏感数据的时候需要进行加密,避免敏感数据泄漏。值得庆幸的是 AndroidX Security library被添加,让min-sdk为23+应用使用SharedPreferences存储加密变得容易和方便。

参考:https://bignerdranch.com/blog/encrypting-shared-preferences-with-the-androidx-security-library/

使用说明
首先在module的build.gradle文件中添加依赖。

implementation “androidx.security:security-crypto:1.0.0-alpha02”
备注:点击链接获取最新版本

添加了依赖之后,下一步是在Android KeyStore创建一个加密master key和store。将下面的代码添加到你计划创建EncryptedSharedPreferences实例前面。

val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)

我们指定了一个默认的key,AES256_GCM_SPEC,用于创建master key。虽然推荐使用这个规范,如果你需要对如何生成密钥有更多的控制你也可以自定义KeyGenParameterSpec。

最后我们只需要一个EncryptedSharedPreferences实例,它对SharedPreferences进行了包装并且为我们处理所有的加密。不同于SharedPreferences,我们可以从Context#getSharedPreferences或Activity#getPreferences获取,我们需要创建自己的EncryptedSharedPreferences实例。

val sharedPreferences = EncryptedSharedPreferences.create(
    "shared_preferences_filename",
    masterKeyAlias,
    context,
    EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
    EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)

我们指定了shared preferences的文件名,之前创建的masterKeyAlias,和一个context。最后两个参数是key和value加密的scheme。它们是库提供的唯一的选项。

创建了EncryptedSharedPreferences实例后,可以使用它像SharedPreferences一样读取和存储值。

val keyGenParameterSpec = MasterKeys.AES256_GCM_SPEC
val masterKeyAlias = MasterKeys.getOrCreate(keyGenParameterSpec)
val sharedPreferences = EncryptedSharedPreferences.create(
    "shared_preferences_filename",
    masterKeyAlias,
    context,
    EncryptedSharedPreferences.PrefKeyEncryptionScheme.AES256_SIV,
    EncryptedSharedPreferences.PrefValueEncryptionScheme.AES256_GCM
)
// storing a value
sharedPreferences
    .edit()
    .putString("some_key", "some_data")
    .apply()
// reading a value
sharedPreferences.getString("some_key", "some_default_value") // -> "some_data"

如果使用EncryptedSharedPreferences存储数据,文件内容示例:

<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="ATP1ABa3NIlOap2c7iNkVaUcQmTocrnpkXl0PyI=">AU+p3hwqCgvlDOtIaawFHWVDf4rFsqghM7ivFTEJesrRp19D+zk7tqsqlGZPLAbryHI=</string>
    <string name="__androidx_security_crypto_encrypted_prefs_key_keyset__">12a901802f1a5d2fbc5cd3c9b545a89ca8ace8f125f8e601a8ac51929303ead8a2bbdf5428bd054360b97c1727ef93ef63b64f43ceac92156f3aee9402dd247009d9779571c6ceacfcd4e7123665cc9dd94c44c5c2c6241a8de070d365d94010f8affb6097d4b0fec1c628120a8f901c23caa03d32ecc6ce270e3cc3341e6455b87a80474b3818c3ad678faa4199a9a45078b218c89b8c5a8cbd1780a68b4f8196eb5153b6422df2bdfee6541a44089680d49f03123c0a30747970652e676f6f676c65617069732e636f6d2f676f6f676c652e63727970746f2e74696e6b2e4165735369764b65791001189680d49f032001</string>
    <string name="__androidx_security_crypto_encrypted_prefs_value_keyset__">128801da6fdef289b2c6e2933c341b1b3df3b39330671d76df362ba8b0a1d807cdc9d2d4d7bc3062139377e4fa61428f3817c0e368c3196c95fdbcca3c37075e7132abae1fe0f128ceef7278a06a01e0cacf29edc1f3c1c1d37875c27c0cf5d86d0b2bb39efcac84828f664838b77aa4c406028af912e860cad8bff51aca6aaf45167d5ab5c8e57bf05db61a44089cbca7fd04123c0a30747970652e676f6f676c65617069732e636f6d2f676f6f676c652e63727970746f2e74696e6b2e41657347636d4b65791001189cbca7fd042001</string>
</map>

可以看到key和value被加密了并且存储了两个keysets,一个是shared preference的keys另一个是values。Keysets包含加密和解密shared preference数据的key。之前创建的master key用于加密这些keysets,这样它们就可以和提供的数据一起存储在shared preference文件中。

SQLite 数据库(未加密)
SQLite 是一种将数据存储在.db文件中的 SQL 数据库引擎。Android SDK内置了对 SQLite 数据库的支持。用于管理数据库的主要包是android.database.sqlite. 例如,您可以使用以下代码在Activity中存储敏感信息:
Java代码示例:

SQLiteDatabase notSoSecure = openOrCreateDatabase("privateNotSoSecure", MODE_PRIVATE, null);
notSoSecure.e x e cSQL("CREATE TABLE IF NOT EXISTS Accounts(Username VARCHAR, Password VARCHAR);");
notSoSecure.e x e cSQL("INSERT INTO Accounts VALUES('admin','AdminPass');");
notSoSecure.close();

Kotlin代码示例:

var notSoSecure = openOrCreateDatabase("privateNotSoSecure", Context.MODE_PRIVATE, null)
notSoSecure.e x e cSQL("CREATE TABLE IF NOT EXISTS Accounts(Username VARCHAR, Password VARCHAR);")
notSoSecure.e x e cSQL("INSERT INTO Accounts VALUES('admin','AdminPass');")
notSoSecure.close()

一旦Activity被调用,数据库文件privateNotSoSecure将与提供的数据一起创建,并存储在明文文件/data/data//databases/privateNotSoSecure中。

除了SQLite数据库外,数据库的目录可能还包含几个文件:

Journal files: These are temporary files used to implement atomic commit and rollback.

Journal files:用于实现原子提交和回滚的临时文件。

Lock files: The lock files are part of the locking and journaling feature, which was designed to improve SQLite concurrency and reduce the writer starvation problem.

Lock files:锁文件是锁定和日志记录特性的一部分,旨在改善SQLite并发性并减少写入器短缺问题。

敏感信息不应该存储在未加密的SQLite数据库中。

SQLite 数据库(加密)
使用 SQLCipher 库,SQLite 数据库可以通过密码加密。

Java 代码示例:

SQLiteDatabase secureDB = SQLiteDatabase.openOrCreateDatabase(database, "password123", null);
secureDB.e x e cSQL("CREATE TABLE IF NOT EXISTS Accounts(Username VARCHAR,Password VARCHAR);");
secureDB.e x e cSQL("INSERT INTO Accounts VALUES('admin','AdminPassEnc');");
secureDB.close();

Kotlin 代码示例:

var secureDB = SQLiteDatabase.openOrCreateDatabase(database, "password123", null)
secureDB.e x e cSQL("CREATE TABLE IF NOT EXISTS Accounts(Username VARCHAR,Password VARCHAR);")
secureDB.e x e cSQL("INSERT INTO Accounts VALUES('admin','AdminPassEnc');")
secureDB.close()

Secure ways to retrieve the database key i n c l u d e:
检索数据库密钥的安全方法包括:

Asking the user to decrypt the database with a PIN or password once the app is opened (weak passwords and PINs are vulnerable to brute force attacks)
打开应用程序后,要求用户用PIN或密码解密数据库(弱密码和PIN容易受到暴力破解攻击)

Storing the key on the server and allowing it to be accessed from a web service only (so that the app can be used only when the device is online)
将密钥存储在服务器上,并只允许从web服务访问它(因此,应用程序只能在设备在线时使用)

Internal Storage 内部存储
您可以将文件存储在设备的内部存储,文件保存在内部存储默认不能被设备上的其他App访问。如果卸载App,这个文件会被删除。
Java 代码示例:

FileOutputStream fos = null;
try {
   fos = openFileOutput(FILENAME, Context.MODE_PRIVATE);
   fos.write(test.getBytes());
   fos.close();
} catch (FileNotFoundException e) {
   e.printStackTrace();
} catch (IOException e) {
   e.printStackTrace();
}

Kotlin代码示例:

var fos: FileOutputStream? = null
fos = openFileOutput("FILENAME", Context.MODE_PRIVATE)
fos.write(test.toByteArray(Charsets.UTF_8))
fos.close()

在程序开发过程中,设置file的访问模式为 Context.MODE_PRIVATE。

External Storage 外部存储
每个Android设备都支持共享的外部存储,这个外部存储是可以是可以移动的(如SD卡)或内置的(不能移动的)。文件保存在外部存储,是可以被所有App读取的。当开启 USB 大容量存储时,用户可以修改。
Java代码示例:

File file = new File (Environment.getExternalFilesDir(), "password.txt");
String password = "SecretPassword";
FileOutputStream fos;
    fos = new FileOutputStream(file);
    fos.write(password.getBytes());
    fos.close();

Kotlin代码示例:

val password = "SecretPassword"
val path = context.getExternalFilesDir(null)
val file = File(path, "password.txt")
file.appendText(password)

一旦Activity被调用,文件将被创建,数据将被存储在外部存储中的一个明文文件中。
当用户卸载应用程序时,存储在应用程序文件夹外的文件(data/data//)不会被删除。最后,值得注意的是,攻击者可以使用外部存储来允许在某些情况下对应用程序进行任意控制。
不要将App的敏感数据存储在External Storage。

参考:
OWASP 移动应用安全测试指南
OWASP 移动应用安全验证标准
Android:测试数据存储 - https://github.com/OWASP/owasp-mstg/blob/master/Document/0x05dTesting-Data-Storage.md

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-08-18 12:49:12  更:2021-08-18 12:49:38 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 9:57:53-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码