新聞中心
1. 本app內(nèi)部使用的activity一定要設(shè)置為非公開

創(chuàng)新互聯(lián)-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價(jià)比嘉定網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫(kù),直接使用。一站式嘉定網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋嘉定地區(qū)。費(fèi)用合理售后完善,十多年實(shí)體公司更值得信賴。
不準(zhǔn)備對(duì)外公開的activity一定要設(shè)置為非公開,以防止被人非法調(diào)用
- android:name=".PrivateActivity"
- android:label="@string/app_name"
- android:exported="false" />
- android:name=".PrivateActivity"
- android:label="@string/app_name"
- android:exported="false" />
同時(shí),一定要注意的是, 非公開的Activity不能設(shè)置intent-filter
因?yàn)?,如果假設(shè)在同一機(jī)器上,有另外一個(gè)app有同樣的intent-filter的話, 調(diào)用該Activity的intent會(huì)喚醒a(bǔ)ndroid的選擇畫面, 讓你選擇使用那個(gè)app接受該intent。這樣就會(huì)事實(shí)上繞過(guò)了非公開的設(shè)置。
2. 不要指定taskAffinity
Android中的activity全都?xì)w屬于task管理 , 簡(jiǎn)單說(shuō)來(lái)task是一種stack的數(shù)據(jù)結(jié)構(gòu), 先入后出。
一般來(lái)說(shuō), 如果不指明歸屬于什么task, 同一個(gè)app內(nèi)部的所有Activity都會(huì)存續(xù)在一個(gè)task中,task的名字就是app的packageName。
因?yàn)樵谕粋€(gè)andorid設(shè)備中,不會(huì)有兩個(gè)同packageName的app存在,所以能保證Activity不被攻擊。
但是如果你指明taskAffinity,比如如下
- [html]
- android:taskAffinity="com.winuxxan.task"
- android:label="@string/app_name">
- android:taskAffinity="com.winuxxan.task"
- android:label="@string/app_name">
那此時(shí),惡意軟件中的Activity如果也聲明為同樣的taskAffinity,那他的Activity就會(huì)啟動(dòng)到你的task中,就會(huì)有機(jī)會(huì)拿到你的intent
3. 不要指定LaunchMode(默認(rèn)standard模式)
Android中Activity的LaunchMode分成 以下四種
Standard: 這種方式打開的Activity不會(huì)被當(dāng)作rootActivity,會(huì)生成一個(gè)新的Activity的instance,會(huì)和打開者在同一個(gè)task內(nèi)
singleTop: 和standard基本一樣,唯一的區(qū)別在于如果當(dāng)前task第一個(gè)Activity就是該Activity的話,就不會(huì)生成新的instance
singleTask:系統(tǒng)會(huì)創(chuàng)建一個(gè)新task(如果沒有啟動(dòng)應(yīng)用)和一個(gè)activity新實(shí)例在新task根部,然后,如果activity實(shí)例已經(jīng)存在單獨(dú)的task中,系統(tǒng)會(huì)調(diào)用已經(jīng)存在activity的 onNewIntent()方法,而不是存在新實(shí)例,僅有一個(gè)activity實(shí)例同時(shí)存在。
singleInstance: 和singleTask相似,除了系統(tǒng)不會(huì)讓其他的activities運(yùn)行在所有持有的task實(shí)例中,這個(gè)activity是獨(dú)立的,并且task中的成員只有它,任何其他activities運(yùn)行這個(gè)activity都將打開一個(gè)獨(dú)立的task。
所有發(fā)送給root Activity(根Activiy)的intent都會(huì)在android中留下履歷。所以一般來(lái)說(shuō)嚴(yán)禁用singleTask或者singleInstance來(lái)啟動(dòng)畫面。
然而,即使用了standard來(lái)打開畫面,也可能會(huì)出問(wèn)題,比如如果調(diào)用者的Activity是用singleInstance模式打開,即使用standard模式打開被調(diào)用Activity,因?yàn)檎{(diào)用者的Activitytask是不能有其他task的, 所以android會(huì)被迫生成一個(gè)新的task,并且把被調(diào)用者塞進(jìn)去,最后被調(diào)用者就成了rootActivity。
程序如下:
- AndroidManifest.xml
- [html]
- package="org.jssec.android.activity.privateactivity"
- android:versionCode="1"
- android:versionName="1.0" >
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name" >
- android:name=".PrivateUserActivity"
- android:label="@string/app_name"
- android:launchMode="singleInstance" >
- android:name=".PrivateActivity"
- android:label="@string/app_name"
- android:exported="false" />
- package="org.jssec.android.activity.privateactivity"
- android:versionCode="1"
- android:versionName="1.0" >
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name" >
- android:name=".PrivateUserActivity"
- android:label="@string/app_name"
- android:launchMode="singleInstance" >
- android:name=".PrivateActivity"
- android:label="@string/app_name"
- android:exported="false" />
非公開Activity的代碼如下:
- [java]
- package org.jssec.android.activity.privateactivity;
- import android.app.Activity;
- import android.content.Intent;
- import android.os.Bundle;
- import android.view.View;
- import android.widget.Toast;
- public class PrivateActivity extends Activity {
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.private_activity);
- String param = getIntent().getStringExtra("PARAM");
- Toast.makeText(this, String.format("「%s」取得。", param),
- Toast.LENGTH_LONG).show();
- }
- public void onReturnResultClick(View view) {
- Intent intent = new Intent();
- intent.putExtra("RESULT", 機(jī)密數(shù)據(jù)");
- setResult(RESULT_OK, intent);
- finish();
- }
- }
- package org.jssec.android.activity.privateactivity;
- import android.app.Activity;
- import android.content.Intent;
- import android.os.Bundle;
- import android.view.View;
- import android.widget.Toast;
- public class PrivateActivity extends Activity {
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.private_activity);
- String param = getIntent().getStringExtra("PARAM");
- Toast.makeText(this, String.format("「%s」取得。", param),
- Toast.LENGTH_LONG).show();
- }
- public void onReturnResultClick(View view) {
- Intent intent = new Intent();
- intent.putExtra("RESULT", 機(jī)密數(shù)據(jù)");
- setResult(RESULT_OK, intent);
- finish();
- }
- }
調(diào)用非公開Activity者,以standard模式打開
- [java]
- package org.jssec.android.activity.privateactivity;
- import android.app.Activity;
- import android.content.Intent;
- import android.os.Bundle;
- import android.view.View;
- import android.widget.Toast;
- public class PrivateUserActivity extends Activity {
- private static final int REQUEST_CODE = 1;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.user_activity);
- }
- public void onUseActivityClick(View view) {
- // 用standard模式啟動(dòng)非公開Activity
- Intent intent = new Intent();
- intent.setClass(this, PrivateActivity.class);
- intent.putExtra("PARAM", "機(jī)密數(shù)據(jù)");
- startActivityForResult(intent, REQUEST_CODE);
- }
- @Override
- public void onActivityResult(int requestCode, int resultCode,
- Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (resultCode != RESULT_OK)
- return;
- switch (requestCode) {
- case REQUEST_CODE:
- String result = data.getStringExtra("RESULT");
- break;
- }
- }
- }
- package org.jssec.android.activity.privateactivity;
- import android.app.Activity;
- import android.content.Intent;
- import android.os.Bundle;
- import android.view.View;
- import android.widget.Toast;
- public class PrivateUserActivity extends Activity {
- private static final int REQUEST_CODE = 1;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.user_activity);
- }
- public void onUseActivityClick(View view) {
- // 用standard模式啟動(dòng)非公開Activity
- Intent intent = new Intent();
- intent.setClass(this, PrivateActivity.class);
- intent.putExtra("PARAM", "機(jī)密數(shù)據(jù)");
- startActivityForResult(intent, REQUEST_CODE);
- }
- @Override
- public void onActivityResult(int requestCode, int resultCode,
- Intent data) {
- super.onActivityResult(requestCode, resultCode, data);
- if (resultCode != RESULT_OK)
- return;
- switch (requestCode) {
- case REQUEST_CODE:
- String result = data.getStringExtra("RESULT");
- break;
- }
- }
- }
4. 發(fā)給Activity的intent不要設(shè)定為FLAG_ACTIVITY_NEW_TASK
就算上面的Activity的lauchMode設(shè)置完善了, 在打開intent的時(shí)候還是能指定打開模式。
比如在intent中指明用FLAG_ACTIVITY_NEW_TASK模式的話,發(fā)現(xiàn)該activity不存在的話,就會(huì)強(qiáng)制新建一個(gè)task。如果同時(shí)設(shè)置了FLAG_ACTIVITY_MULTIPLE_TASK+ FLAG_ACTIVITY_NEW_TASK,就無(wú)論如何都會(huì)生成新的task,該Activity就會(huì)變成rootActiviy,并且intent會(huì)被留成履歷
5. Intent中數(shù)據(jù)的加密
Activity中數(shù)據(jù)的傳遞都依靠intent, 很容易被攻擊, 所以 就算同一個(gè)app內(nèi)部傳遞數(shù)據(jù), 最好還是要加密, 加密算法很多
6. 明確ActivityName發(fā)送Intent
明確Activity發(fā)送Intent,能夠避免被惡意軟件截取。
同一app內(nèi)部的發(fā)送
- [java]
- Intent intent = new Intent(this, PictureActivity.class);
- intent.putExtra("BARCODE", barcode);
- startActivity(intent);
- Intent intent = new Intent(this, PictureActivity.class);
- intent.putExtra("BARCODE", barcode);
- startActivity(intent);
- 不同app內(nèi)部的發(fā)送
- [java]
- Intent intent = new Intent();
- intent.setClassName(
- "org.jssec.android.activity.publicactivity",
- "org.jssec.android.activity.publicactivity.PublicActivity");
- startActivity(intent);
- Intent intent = new Intent();
- intent.setClassName(
- "org.jssec.android.activity.publicactivity",
- "org.jssec.android.activity.publicactivity.PublicActivity");
- startActivity(intent);
但是,要注意的是!
不是指明了packageName和ActivityName就能避免所有的問(wèn)題,
如果有一個(gè)惡意軟件故意做成和你發(fā)送目標(biāo)同packageName, 同ActivityName, 此時(shí)的intent就會(huì)被截取
7. 跨app接受Intent時(shí),要明確對(duì)方的身份
接受到別的app發(fā)來(lái)的intent時(shí),要能確定對(duì)方的身份。
一個(gè)好方法是比對(duì)對(duì)方的app的hashcode。
當(dāng)前,前提是調(diào)用者要用startActivityForResult(),因?yàn)橹挥羞@個(gè)方法,被調(diào)用者才能得到調(diào)用者的packageName
代碼如下:
被調(diào)用的Activity
- [java]
- package org.jssec.android.activity.exclusiveactivity;
- import org.jssec.android.shared.PkgCertWhitelists;
- import org.jssec.android.shared.Utils;
- import android.app.Activity;
- import android.content.Context;
- import android.content.Intent;
- import android.os.Bundle;
- import android.view.View;
- import android.widget.Toast;
- public class ExclusiveActivity extends Activity {
- // hashcode的白名單
- private static PkgCertWhitelists sWhitelists = null;
- private static void buildWhitelists(Context context) {
- boolean isdebug = Utils.isDebuggable(context);
- sWhitelists = new PkgCertWhitelists();
- sWhitelists
- .add("org.jssec.android.activity.exclusiveuser", isdebug ?
- "0EFB7236 328348A9 89718BAD DF57F544 D5CCB4AE B9DB34BC 1E29DD26 F77C8255"
- :
- "1F039BB5 7861C27A 3916C778 8E78CE00 690B3974 3EB8259F E2627B8D 4C0EC35A");
- }
- private static boolean checkPartner(Context context, String pkgname) {
- if (sWhitelists == null)
- buildWhitelists(context);
- return sWhitelists.test(context, pkgname);
- }
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- // check白名單
- if (!checkPartner(this, getCallingPackage())) {
- Toast.makeText(this, "不是白名單內(nèi)部的。", Toast.LENGTH_LONG).show();
- finish();
- return;
- }
- }
- public void onReturnResultClick(View view) {
- Intent intent = new Intent();
- intent.putExtra("RESULT", "機(jī)密數(shù)據(jù)");
- setResult(RESULT_OK, intent);
- finish();
- }
- }
- package org.jssec.android.activity.exclusiveactivity;
- import org.jssec.android.shared.PkgCertWhitelists;
- import org.jssec.android.shared.Utils;
- import android.app.Activity;
- import android.content.Context;
- import android.content.Intent;
- import android.os.Bundle;
- import android.view.View;
- import android.widget.Toast;
- public class ExclusiveActivity extends Activity {
- // hashcode的白名單
- private static PkgCertWhitelists sWhitelists = null;
- private static void buildWhitelists(Context context) {
- boolean isdebug = Utils.isDebuggable(context);
- sWhitelists = new PkgCertWhitelists();
- sWhitelists
- .add("org.jssec.android.activity.exclusiveuser", isdebug ?
- "0EFB7236 328348A9 89718BAD DF57F544 D5CCB4AE B9DB34BC 1E29DD26 F77C8255"
- :
- "1F039BB5 7861C27A 3916C778 8E78CE00 690B3974 3EB8259F E2627B8D 4C0EC35A");
- }
- private static boolean checkPartner(Context context, String pkgname) {
- if (sWhitelists == null)
- buildWhitelists(context);
- return sWhitelists.test(context, pkgname);
- }
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.main);
- // check白名單
- if (!checkPartner(this, getCallingPackage())) {
- Toast.makeText(this, "不是白名單內(nèi)部的。", Toast.LENGTH_LONG).show();
- finish();
- return;
- }
- }
- public void onReturnResultClick(View view) {
- Intent intent = new Intent();
- intent.putExtra("RESULT", "機(jī)密數(shù)據(jù)");
- setResult(RESULT_OK, intent);
- finish();
- }
- } [java]
- PkgCertWhitelists.java
- [java]
- package org.jssec.android.shared;
- import java.util.HashMap;
- import java.util.Map;
- import android.content.Context;
- public class PkgCertWhitelists {
- private Map
mWhitelists = new HashMap (); - public boolean add(String pkgname, String sha256) {
- if (pkgname == null)
- return false;
- if (sha256 == null)
- return false;
- sha256 = sha256.replaceAll(" ", "");
- if (sha256.length() != 64)
- return false;
- sha256 = sha256.toUpperCase();
- if (sha256.replaceAll("[0-9A-F]+", "").length() != 0)
- return false;
- mWhitelists.put(pkgname, sha256);
- return true;
- }
- public boolean test(Context ctx, String pkgname) {
- String correctHash = mWhitelists.get(pkgname);
- return PkgCert.test(ctx, pkgname, correctHash);
- }
- }
- package org.jssec.android.shared;
- import java.util.HashMap;
- import java.util.Map;
- import android.content.Context;
- public class PkgCertWhitelists {
- private Map
mWhitelists = new HashMap (); - public boolean add(String pkgname, String sha256) {
- if (pkgname == null)
- return false;
- if (sha256 == null)
- return false;
- sha256 = sha256.replaceAll(" ", "");
- if (sha256.length() != 64)
- return false;
- sha256 = sha256.toUpperCase();
- if (sha256.replaceAll("[0-9A-F]+", "").length() != 0)
- return false;
- mWhitelists.put(pkgname, sha256);
- return true;
- }
- public boolean test(Context ctx, String pkgname) {
- String correctHash = mWhitelists.get(pkgname);
- return PkgCert.test(ctx, pkgname, correctHash);
- }
- }
- PkgCert.java
- [java]
- package org.jssec.android.shared;
- import java.security.MessageDigest;
- import java.security.NoSuchAlgorithmException;
- import android.content.Context;
- import android.content.pm.PackageInfo;
- import android.content.pm.PackageManager;
- import android.content.pm.PackageManager.NameNotFoundException;
- import android.content.pm.Signature;
- public class PkgCert {
- public static boolean test(Context ctx, String pkgname,
- String correctHash) {
- if (correctHash == null)
- return false;
- correctHash = correctHash.replaceAll(" ", "");
- return correctHash.equals(hash(ctx, pkgname));
- }
- public static String hash(Context ctx, String pkgname) {
- if (pkgname == null)
- return null;
- try {
- PackageManager pm = ctx.getPackageManager();
- PackageInfo pkginfo = pm.getPackageInfo(pkgname,
- PackageManager.GET_SIGNATURES);
- if (pkginfo.signatures.length != 1)
- return null;
- Signature sig = pkginfo.signatures[0];
- byte[] cert = sig.toByteArray();
- byte[] sha256 = computeSha256(cert);
- return byte2hex(sha256);
- } catch (NameNotFoundException e) {
- return null;
- }
- }
- private static byte[] computeSha256(byte[] data) {
- try {
- return MessageDigest.getInstance("SHA-256").digest(data);
- } catch (NoSuchAlgorithmException e) {
- return null;
- }
- }
- private static String byte2hex(byte[] data) {
- if (data == null)
- return null;
- final StringBuilder hexadecimal = new StringBuilder();
- for (final byte b : data) {
- hexadecimal.append(String.format("%02X", b));
- }
- return hexadecimal.toString();
- }
- }
- package org.jssec.android.shared;
- import java.security.MessageDigest;
- import java.security.NoSuchAlgorithmException;
- import android.content.Context;
- import android.content.pm.PackageInfo;
- import android.content.pm.PackageManager;
- import android.content.pm.PackageManager.NameNotFoundException;
- import android.content.pm.Signature;
- public class PkgCert {
- public static boolean test(Context ctx, String pkgname,
- String correctHash) {
- if (correctHash == null)
- return false;
- correctHash = correctHash.replaceAll(" ", "");
- return correctHash.equals(hash(ctx, pkgname));
- }
- public static String hash(Context ctx, String pkgname) {
- if (pkgname == null)
- return null;
- try {
- PackageManager pm = ctx.getPackageManager();
- PackageInfo pkginfo = pm.getPackageInfo(pkgname,
- PackageManager.GET_SIGNATURES);
- if (pkginfo.signatures.length != 1)
- return null;
- Signature sig = pkginfo.signatures[0];
- byte[] cert = sig.toByteArray();
- byte[] sha256 = computeSha256(cert);
- return byte2hex(sha256);
- } catch (NameNotFoundException e) {
- return null;
- }
- }
- private static byte[] computeSha256(byte[] data) {
- try {
- return MessageDigest.getInstance("SHA-256").digest(data);
- } catch (NoSuchAlgorithmException e) {
- return null;
- }
- }
- private static String byte2hex(byte[] data) {
- if (data == null)
- return null;
- final StringBuilder hexadecimal = new StringBuilder();
- for (final byte b : data) {
- hexadecimal.append(String.format("%02X", b));
- }
- return hexadecimal.toString();
- }
- }
8. 所有根Activity中的intent都能被所有app共享
所有的app,只要按照如下樣子,就能取出這臺(tái)手機(jī)上所有task上所有根Activity接受到的intent
- AndroidManifest.xml
- [html]
- package="org.jssec.android.intent.maliciousactivity"
- android:versionCode="1"
- android:versionName="1.0" >
- android:minSdkVersion="8"
- android:targetSdkVersion="15" />
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name"
- android:theme="@style/AppTheme" >
- android:name=".MaliciousActivity"
- android:label="@string/title_activity_main" >
- package="org.jssec.android.intent.maliciousactivity"
- android:versionCode="1"
- android:versionName="1.0" >
- android:minSdkVersion="8"
- android:targetSdkVersion="15" />
- android:icon="@drawable/ic_launcher"
- android:label="@string/app_name"
- android:theme="@style/AppTheme" >
- android:name=".MaliciousActivity"
- android:label="@string/title_activity_main" >
- MaliciousActivity.java
- [java]
- package org.jssec.android.intent.maliciousactivity;
- import java.util.List;
- import android.app.Activity;
- import android.app.ActivityManager;
- import android.content.Intent;
- import android.os.Bundle;
- import android.util.Log;
- public class MaliciousActivity extends Activity {
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.malicious_activity);
- ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
- List
list = activityManager - .getRecentTasks(100, ActivityManager.RECENT_WITH_EXCLUDED);
- for (ActivityManager.RecentTaskInfo r : list) {
- Intent intent = r.baseIntent;
- Log.v("baseIntent", intent.toString());
- }
- }
- }
- package org.jssec.android.intent.maliciousactivity;
- import java.util.List;
- import android.app.Activity;
- import android.app.ActivityManager;
- import android.content.Intent;
- import android.os.Bundle;
- import android.util.Log;
- public class MaliciousActivity extends Activity {
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.malicious_activity);
- ActivityManager activityManager = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
- List
list = activityManager - .getRecentTasks(100, ActivityManager.RECENT_WITH_EXCLUDED);
- for (ActivityManager.RecentTaskInfo r : list) {
- Intent intent = r.baseIntent;
- Log.v("baseIntent", intent.toString());
- }
- }
- }
9. Intent數(shù)據(jù)遺漏到LogCat的可能性
如果像如下代碼,那Intent中發(fā)送的數(shù)據(jù)就會(huì)被自動(dòng)寫入LogCat
- [java]
- Uri uri = Uri.parse("mailto:[email protected]");
- Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
- startActivity(intent);
- Uri uri = Uri.parse("mailto:[email protected]");
- Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
- startActivity(intent);
- 如果像如下,就能避免
- [java]
- Uri uri = Uri.parse("mailto:");
- Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
- intent.putExtra(Intent.EXTRA_EMAIL, new String[] {"[email protected]"});
- startActivity(intent);
- Uri uri = Uri.parse("mailto:");
- Intent intent = new Intent(Intent.ACTION_SENDTO, uri);
- intent.putExtra(Intent.EXTRA_EMAIL, new String[] {"[email protected]"});
- startActivity(intent);
文章標(biāo)題:Android開發(fā)中的若干安全問(wèn)題之Activity篇
網(wǎng)站路徑:http://fisionsoft.com.cn/article/djposid.html


咨詢
建站咨詢
