概述

移动应用开发中,往往有跨进程通信的需求,方便地实现程序间的数据共享。 Android 提供了多种 IPC 通信的方式,给开发人员带来了便利,但如果选择或使用不当,就有可能发生各种各样的风险。

安全准则

A. 应用程序间的数据共享尽量优先采用 content provider ,尽量不要用带全局读写属性的 IPC 文件进行进程间通信(比如全局读写的 SharedPreferences )。

B. 如果应用中的 content provider Broadcast Receivers Activities Services 不需要被其它应用程序访问,应在 android manifest 文件中显式声明 android:exported=”false”

C. 如果组件需要被其它应用程序访问,应使用 permission 标签设置权限;如果只是在同一开发者开发的程序之间进行 IPC ,优先考虑使用 signature 级别的 permission

D. content provider 支持对指定的 Uri 分别设置读写权限,建议只开放能完成任务的最小权限。

E. 使用 content provider 提供外部应用程序进行数据库存取时应使用带占位符的参数化查询防 SQL 注入。

F. 不要使用基于 socket IPC 通信方式。

详细描述

A. android manifest 文件中将 permission 的属性 android:protectionLevel 设置为 ”signature” ,此时,具有相同签名的并且申请了相应权限的应用才可访问该组件,参考附录 4

B. content provider 的独立读写权限设置方法可参考附录 5

C. SQLiteDatabase 对象的部分内置方法是可以有效防 SQL 注入的,比如 query(),insert(),update delete() ,另外, 正确地 使用 rawQuery() 也可以防止 SQL 注入,而 execSQL() 方法建议避免使用,如何正确的使用这些方法和相关错误案例请参考附录 3

备注

A. 当使用基于 socket IPC 方式时,设备上的应用都可以访问该监听端口,最差的情况是端口完全对外开放监听,相当于任何应用都可以通过网络跟该端口进行通信。

B. 当一个组件定义了 intent-filter ,其 android:exported 的默认值为 true ,否则为 false

注:如果IE浏览器显示格式不正确,请使用chrome浏览器。

附录:

3 、SQLite防止SQL注入漏洞方案

11.1.1、使用SQLiteDatabase对象自带的防SQL注入的方法,比如query(),insert(),update和delete():

DatabaseHelper dbhelper = new DatabaseHelper(SqliteActivity. this ,"sqliteDB");

SQLiteDatabase db = dbhelper.getReadableDatabase();

/*查询操作,userInputID和userInputName是用户可控制的输入数据 */

Cursor cur = db.query("user", new String[]{"id","name"}, "id=? and name=?", new String[]{userInputID,userInputName}, null, null, null);

/* 插入记录操作*/

ContentValues val = new ContentValues();

val.put("id", userInputID);

val.put("name", userInputName);

db.insert("user", null , val);

/*更新记录操作*/

ContentValues val = new ContentValues();

val.put("id", userInputName);

db.update("user", val, "id=?", new String[]{userInputID });

/*删除记录操作*/

db.delete("user", "id=? and name=?", new String[]{userInputID , userInputName });

11.1.2、正确地使用SQLiteDatabase对象的rawQuery()方法(仅以查询为例):

DatabaseHelper dbhelper = new DatabaseHelper(SqliteActivity. this ,"sqliteDB");

SQLiteDatabase db = dbhelper.getReadableDatabase();

/* userInputID和userInputName是用户可控制的输入数据*/

String[] selectionArgs = new String[]{userInputID , userInputName };

String sql = "select * from user where id=? and name=?";//正确!此处绑定变量

Cursor curALL = db.rawQuery( sql , selectionArgs);

11.1.3、以下为 错误案例 !仅供参考:

DatabaseHelper dbhelper = new DatabaseHelper(SqliteActivity. this ,"sqliteDB");

SQLiteDatabase db = dbhelper.getReadableDatabase();

/*案例1:错误地使用rawQuery(),未绑定参数*/

String sql = "select * from user where id='" + userInputID +"'";//错误!动态拼接,未绑定变量

Cursor curALL = db.rawQuery(sql, null);

/*案例2:使用execSQL()方法*/

String sql = "INSERT INTO user values('"+userInputID +"','"+userInputName +"')";//错误同上

db.execSQL(sql);

4 、使用signature级别的permission:

在应用的AndroidManifest.xml定义如下:

<!- 声明权限 ->

<permission

android:protectionLevel= " signature "

android:name= "com.hik.helloworld.activity" />

<activity

android:name= "com.hik.helloworld.TestActivity"

android:label= "@string/app_name"

android:exported= "false"

<!- 为对应组件使用权限 ->

android:permission= "com.hik.helloworld.activity" >

</activity>

则只有相同数字签名的应用申请该权限后才可以访问该组件。

5 、contentProvider的独立读写权限设置

/* 只允许外部程序对特定Uri进行读操作时的样例设置 */

< provider android:name=". MyContentProvider "

android:authorities=" com.hik.contentprovider.MyContentProvider "

android:exported= "true"
android:multiprocess="true"
android:readPermission="

com.hik.contentprovider.MyContentProvider .permission.read">
<path-permission android:pathPattern="/queryData/.*"
android:permission="

com.hik.contentprovider.MyContentProvider .permission.read "/>
</ provider >

/* 只允许外部程序对特定Uri进行写操作时的样例设置 */

< provider android:name=". MyContentProvider "

android:authorities=" com.hik.contentprovider.MyContentProvider "

android:exported= "true"
android:multiprocess="true"
android:writePermission="

com.hik.contentprovider.MyContentProvider .permission.write">
<path-permission android:pathPattern="/updateData/.*"
android:permission="

com.hik.contentprovider.MyContentProvider .permission.write "/>
</ provider >