Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 開發中的若干安全問題 之 Activity篇

Android 開發中的若干安全問題 之 Activity篇

編輯:關於Android編程

1.    本app內部使用的activity一定要設置為非公開                   不准備對外公開的activity一定要設置為非公開,以防止被人非法調用     [html]   <activity   android:name=".PrivateActivity"   android:label="@string/app_name"   android:exported="false" />     <activity android:name=".PrivateActivity" android:label="@string/app_name" android:exported="false" />                        同時,一定要注意的是, 非公開的Activity不能設置intent-filter                    因為,如果假設在同一機器上,有另外一個app有同樣的intent-filter的話, 調用該Activity的intent會喚醒android的選擇畫面, 讓你選擇使用那個app接受該intent。這樣就會事實上繞過了非公開的設置。   2.    不要指定taskAffinity Android中的activity全都歸屬於task管理 , 簡單說來task是一種stack的數據結構, 先入後出。   一般來說, 如果不指明歸屬於什麼task, 同一個app內部的所有Activity都會存續在一個task中,task的名字就是app的packageName。   因為在同一個andorid設備中,不會有兩個同packageName的app存在,所以能保證Activity不被攻擊。   但是如果你指明taskAffinity,比如如下   [html]   <application android:icon="@drawable/icon" android:label="@string/app_name">            <activity android:name=".Activity1"                      android:taskAffinity="com.winuxxan.task"                      android:label="@string/app_name">            </activity>            <activity android:name=".Activity2">                <intent-filter>                    <action android:name="android.intent.action.MAIN" />                    <category android:name="android.intent.category.LAUNCHER" />                </intent-filter>            </activity>        </application>     <application android:icon="@drawable/icon" android:label="@string/app_name">          <activity android:name=".Activity1"                    android:taskAffinity="com.winuxxan.task"                    android:label="@string/app_name">          </activity>          <activity android:name=".Activity2">              <intent-filter>                  <action android:name="android.intent.action.MAIN" />                  <category android:name="android.intent.category.LAUNCHER" />              </intent-filter>          </activity>      </application>     那此時,惡意軟件中的Activity如果也聲明為同樣的taskAffinity,那他的Activity就會啟動到你的task中,就會有機會拿到你的intent   3.    不要指定LaunchMode(默認standard模式)              Android中Activity的LaunchMode分成 以下四種              Standard:   這種方式打開的Activity不會被當作rootActivity,會生成一個新的Activity的instance,會和打開者在同一個task內                singleTop:      和standard基本一樣,唯一的區別在於如果當前task第一個Activity就是該Activity的話,就不會生成新的instance              singleTask:系統會創建一個新task(如果沒有啟動應用)和一個activity新實例在新task根部,然後,如果activity實例已經存在單獨的task中,系統會調用已經存在activity的 onNewIntent()方法,而不是存在新實例,僅有一個activity實例同時存在。               singleInstance: 和singleTask相似,除了系統不會讓其他的activities運行在所有持有的task實例中,這個activity是獨立的,並且task中的成員只有它,任何其他activities運行這個activity都將打開一個獨立的task。                  所有發送給root Activity(根Activiy)的intent都會在android中留下履歷。所以一般來說嚴禁用singleTask或者singleInstance來啟動畫面。                  然而,即使用了standard來打開畫面,也可能會出問題,比如如果調用者的Activity是用singleInstance模式打開,即使用standard模式打開被調用Activity,因為調用者的Activitytask是不能有其他task的, 所以android會被迫生成一個新的task,並且把被調用者塞進去,最後被調用者就成了rootActivity。   程序如下:   AndroidManifest.xml    [html]   <?xml version="1.0" encoding="utf-8"?>   <manifest xmlns:android="http://schemas.android.com/apk/res/android"      package="org.jssec.android.activity.privateactivity"      android:versionCode="1"   android:versionName="1.0" >   <uses-sdk android:minSdkVersion="8" />      <application        android:icon="@drawable/ic_launcher"        android:label="@string/app_name" >        <!—root Activity以”singleInstance”模式啟動 -->        <!—不設置taskAffinity-->      <activity        android:name=".PrivateUserActivity"         android:label="@string/app_name"         android:launchMode="singleInstance" >       <intent-filter>        <action android:name="android.intent.action.MAIN" />        <category android:name="android.intent.category.LAUNCHER" />       </intent-filter>   </activity>   <!-- 非公開Activity -->   <!—啟動模式為”standard” -->   <!—不設置taskAffinity-->      <activity         android:name=".PrivateActivity"         android:label="@string/app_name"        android:exported="false" />   </application>   </manifest>     <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"    package="org.jssec.android.activity.privateactivity"    android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" />    <application      android:icon="@drawable/ic_launcher"      android:label="@string/app_name" >      <!—root Activity以”singleInstance”模式啟動 -->      <!—不設置taskAffinity-->    <activity      android:name=".PrivateUserActivity"       android:label="@string/app_name"       android:launchMode="singleInstance" >     <intent-filter>      <action android:name="android.intent.action.MAIN" />      <category android:name="android.intent.category.LAUNCHER" />     </intent-filter> </activity> <!-- 非公開Activity --> <!—啟動模式為”standard” --> <!—不設置taskAffinity-->    <activity       android:name=".PrivateActivity"       android:label="@string/app_name"      android:exported="false" /> </application> </manifest>   非公開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", 機密數據");           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", 機密數據");         setResult(RESULT_OK, intent);         finish();         }     }     調用非公開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模式啟動非公開Activity                Intent intent = new Intent();               intent.setClass(this, PrivateActivity.class);               intent.putExtra("PARAM", "機密數據");               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模式啟動非公開Activity             Intent intent = new Intent();             intent.setClass(this, PrivateActivity.class);             intent.putExtra("PARAM", "機密數據");             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.    發給Activity的intent不要設定為FLAG_ACTIVITY_NEW_TASK 就算上面的Activity的lauchMode設置完善了, 在打開intent的時候還是能指定打開模式。   比如在intent中指明用FLAG_ACTIVITY_NEW_TASK模式的話,發現該activity不存在的話,就會強制新建一個task。如果同時設置了FLAG_ACTIVITY_MULTIPLE_TASK+ FLAG_ACTIVITY_NEW_TASK,就無論如何都會生成新的task,該Activity就會變成rootActiviy,並且intent會被留成履歷   5.    Intent中數據的加密 Activity中數據的傳遞都依靠intent, 很容易被攻擊, 所以 就算同一個app內部傳遞數據, 最好還是要加密, 加密算法很多   6.    明確ActivityName發送Intent    明確Activity發送Intent,能夠避免被惡意軟件截取。    同一app內部的發送     [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內部的發送   [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就能避免所有的問題,                                  如果有一個惡意軟件故意做成和你發送目標同packageName, 同ActivityName, 此時的intent就會被截取   7.    跨app接受Intent時,要明確對方的身份 接受到別的app發來的intent時,要能確定對方的身份。   一個好方法是比對對方的app的hashcode。   當前,前提是調用者要用startActivityForResult(),因為只有這個方法,被調用者才能得到調用者的packageName   代碼如下:   被調用的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, "不是白名單內部的。", Toast.LENGTH_LONG).show();       finish();       return;       }       }              public void onReturnResultClick(View view) {               Intent intent = new Intent();               intent.putExtra("RESULT", "機密數據");               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, "不是白名單內部的。", Toast.LENGTH_LONG).show();     finish();     return;     }     }           public void onReturnResultClick(View view) {             Intent intent = new Intent();             intent.putExtra("RESULT", "機密數據");             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<String, String> mWhitelists = new HashMap<String, String>();              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<String, String> mWhitelists = new HashMap<String, String>();           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,只要按照如下樣子,就能取出這台手機上所有task上所有根Activity接受到的intent   AndroidManifest.xml   [html]   <manifest xmlns:android="http://schemas.android.com/apk/res/android"       package="org.jssec.android.intent.maliciousactivity"       android:versionCode="1"       android:versionName="1.0" >          <uses-sdk           android:minSdkVersion="8"           android:targetSdkVersion="15" />          <application           android:icon="@drawable/ic_launcher"           android:label="@string/app_name"           android:theme="@style/AppTheme" >           <activity               android:name=".MaliciousActivity"               android:label="@string/title_activity_main" >               <intent-filter>                   <action android:name="android.intent.action.MAIN" />                      <category android:name="android.intent.category.LAUNCHER" />               </intent-filter>           </activity>       </application>          <uses-permission android:name="android.permission.GET_TASKS" />      </manifest>     <manifest xmlns:android="http://schemas.android.com/apk/res/android"     package="org.jssec.android.intent.maliciousactivity"     android:versionCode="1"     android:versionName="1.0" >       <uses-sdk         android:minSdkVersion="8"         android:targetSdkVersion="15" />       <application         android:icon="@drawable/ic_launcher"         android:label="@string/app_name"         android:theme="@style/AppTheme" >         <activity             android:name=".MaliciousActivity"             android:label="@string/title_activity_main" >             <intent-filter>                 <action android:name="android.intent.action.MAIN" />                   <category android:name="android.intent.category.LAUNCHER" />             </intent-filter>         </activity>     </application>       <uses-permission android:name="android.permission.GET_TASKS" />   </manifest> 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<ActivityManager.RecentTaskInfo> 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<ActivityManager.RecentTaskInfo> list = activityManager                     .getRecentTasks(100, ActivityManager.RECENT_WITH_EXCLUDED);             for (ActivityManager.RecentTaskInfo r : list) {                 Intent intent = r.baseIntent;                 Log.v("baseIntent", intent.toString());             }         }     }     9.    Intent數據遺漏到LogCat的可能性 如果像如下代碼,那Intent中發送的數據就會被自動寫入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);     #以上#      
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved