首先要感谢《android安全攻防实战》这本书。简单实用。这篇文章是我把这本简单总结了下。希望能对大家有所帮助。也非常欢迎大家来反馈,交流。


验证app的签名(防篡改)


首先我们了解下app的证书和签名


app的证书是以把开发者的id和他们app以密码学的方式关联起来的方式实现的。它被推送到app市场的app中,表示开发者身份。


app的签名确保了不会被其他app所冒充。所以在app被安装之前,都必须要经过签名。




怎么做:


第一种方法:


①:Java JDK中有一个名叫“jarsigner” 的工具,我们可以用它来搞定这个苦差事。你要做的就是执行:


jarsigner -verify -verbose [你的apk文件存放路径]


②:现在,你要做的就是查看屏幕上输出的.jar验证字符串。它表示这个app的签名已经被验证了。


第二种方法:


①获取签名
keytool -list -v -keystore your_app.store 


(或者你自己方法获取,直接在打包的地方复制)


②复制SHA1 hash到你的类中设为静态字符串(冒号去掉)
private static String CERTIFICATE_SHA1="你的签名";


③编写运行时获取.apk文件当前签名的代码


public static boolean validateAppSignature(Context context){
   //get the signature form the package manager
   PackageInfo packageInfo=context.getPackageManager()
   .getPackageInfo(context.getPackageName);
   Signature[] appSignatures=packageInfo.signatures;
   
   //this sample only checks the first certificate
   for (Signature signature:appSignatures){
       byte[] signatureBytes=signature.toByteArray();
       //calc sha1 inhex
       String currentSignature=calcSHA1(signaturrentSignature);
       //comapare signaturesd
       return CERTIFICATE_SHA1.equalsIgoreCase(currentSignature);
   }
   return flase;
}



我们之前存的签名是SHA1 hash,现在我们一样要转换成(十六进制)格式:



private static String calsSHA1(byte[] signature){
   MessageDigest digest=MessageDigest.getInstance("SHA1");
   digest.update(signature);
   byte[] signatureHash=digest.digest();
   return bytesToHex(signatureHash);
}
public static String bytesToHex(bytes[] bytes){
   fianl char[] hexArray={'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'};
   char[] hexChars=new char[bytes.length*2];
   int v;
   for(int j=0;j
     
     
      
      >>4];
      hexChars[j*2+1]=hexArray[v&0x0F];
   }  
   return new String(jexCjars);
}






保护app组件


保护app组件的途径有两条:一是正确使用AndroidManifest.xml,二是在代码级别上强制进行权限检查。


首先你要了解你组件的用途、为什么要保护他,以及当一个恶意app向你的app发送intent、访问它的数据时,你的用户将会面临什么样的风险。所以在配置你的AndroidManifest.xml文件,或在你的app中加入权限检查代码之前,先得老老实实回答上面的问题。



怎么做:



一、使用exported属性决定是否允许组件被其他app调用,true为可以。


语法:<组件名 android:exported="false">这里的组件名字可以是activity,provider,service,receiver</组件名>
 
android 4.2中修改了默认行为(默认为false,即不允许其他app访问,可以把android:minSDKVersion或者android:targetSDKVersion设为17)


二、通过定制权限保护组件


①在添加定制之前,你先得声明表示permission标签的字符串,你可以通过编辑位于你的app项目文件夹中的res/values/strings.xml文件来完成。
 
<string name="custom_permission_label">Custom Permission</string>
 
②在你的app中添加保护级别normal的定制权限,你可以通过在AndroidManifest.xml文件夹中加入下列字符串来完成这一任务。
 
<permission android:name="android:permission.CUSTOM_PERMISSION"
 
android:protectionLevel="normal"
 
android:description="My custom permission"
 
android:label="@string/custom_permission_label">
 
 
 
 

  ps:android:name  定义了权限的名称,也就是用来表示这一权限的字符串值。 

 

  android:protectionLevel:它定义了该权限的保护级别,以及控制用户是否被提示同意授予这一权限。 
 
 它有四个级别: 
 

  normal:不会提示,自动授予。 
 
 dangerous:被用来定义那些会使用户暴漏在财务、名誉或法律风险下权限的。 
 
 signature:这个权限会被自动赋予那些由定义该权限的app相同签名的app。(自己app共享) 
 
 signatureOrSystem:该权限会被自动赋予那些作为系统镜像的一部分。或者有定义该权限的app相同签名的app。 
 
 
 
 
③把自己定制的权限添加到组件中去。
 
<组件名 ......
 
android:permission="android.permission.CUSTOM_PERMISSION">
 
④你也可以通过在app的AndroidManifest.xml文件中添加<uses-permission/>标签的方式,让其他app也能请求这一权限:
 
<uses-permission android:name="android.permission.CUSTOME_PERMISSION">



保护content provider的路径

由于content provider经常会记录与用户登录活动密切相关的数据,所以在app的各类组件中,content provider可能是最容易被黑客盯上的目标。
 
关于content provider你必须要知道URI是什么。
 
URI(Uniform resource identifiers)即统一资源定位符,是content provider用来标识给数据库的。
 
比如: content://com.myprovider.android/email/inbox 
 
 
 
 
 
 
怎么做:
 
 
 
 
 
 
 
①为了加固你的content provider , 需要设置一个用于管理所有与你的认证相关路径的读和写权限的permission,你可以在你的android manifest 添加下面这个provider。
 
<provider android:enabled="true"
 
android:exported="true" //上面已经讲到了,exported为true的时候,外部应用才可以访问到,4.2之后默认为false
 
android:authorities="com.android.myAuthority" 
android:name="com.myapp.provider" 
android:permission="[permission name]"></provicer>
 
ps:[permission name]是其他app读或写必须拥有的权限。
 
 
 
 
②content provider会有一些他们存放相关数据的content路径,可以给她们加上读写权限。
 
<provider
 
android:writePermission="[write permission name]"
 
android:readPermission="[read permission name]">
 
</provider>
 
上面的android:writePermission和android:readPermission用来声明当别的app访问任何读相关(query)或写相关(update和insert)操作时,必须拥有特定权限。读和写必须分别独立进行声明。
 
 
 
 
例:
 
Chrome app:
 
<provider android:name="com.google.androdi.apps.chrome.ChromeBrowserProvider" 
android:readPermission="com.android.browser.permission.READ_HISTORY_BOOKMARKS" 
android:writePermission="com.android.browser.permission.WIRTE_HISTORY_BOOKMARKS" 
android:exported="true"></provider>
 
我们可以使用AndroidManifest.xml文件中<path-permission>元素,为每个单独的路径设置不同的权限:
 
<provider......>
 <path-permission android:path="/[path name]" 
android:permission="[read/write permission name]"
 
android:readPermissoin="[read permission name]"
 
android:writePermission="{write permission name">
 
</provider>
 
ps:path级的读写权限比provider权限优先级高。
 

 
③URI授权:
 <provider ..>
    <grant-uri-permission android:path="[path name]">
 </provider> 
(一般不会使用,除非确保其他app都能对某个路径增改查,才可以用。)




防御SQL注入攻击



避免用: SQLiteDatabase.rawQuery();
 
推荐用:SQLiteStatement(预先编译好,提供对参数的绑定和转换)
和
SQLiteDatabase类中query,insert,update,delete







本篇就写到这,会尽快完成第二篇的总结,如果您看到这了,就评论反馈一下吧。