如何在运行时验证已安装的应用程序以防止网络钓鱼攻击?

时间:2016-04-06 07:23:07

标签: android security android-intent phishing

我的设备中有一个支付应用程序,我的应用程序连接到该应用程序的服务以获取启动支付活动的待处理意图,然后在onActivityResult()方法中监听结果。(类似于应用内购买方案)

我为付款意图设置了包裹名称。但是你知道,这并不能保证支付应用程序是值得信赖的。如果某人通过具有相同包名称和相同的aidl-service实现的未知来源安装假应用程序,那么它可以给我挂起意图和网络钓鱼我的用户信息。

我通过某种机制验证付款结果,并且只保证我的应用程序免于虚假付款,但我的应用程序用户在网络钓鱼应用程序中输入数据。(此段落说我的问题不信任付款响应应用程序,我的问题是在启动活动之前信任支付应用程序

我知道一些方法,我可以检查其他应用程序签名和公钥。 如果 Android操作系统保证公钥和签名是只读的并且与已安装的应用程序匹配,我可以依赖它并检查支付应用程序的公钥,然后再向其发送意图但我猜这些不是现成的,只在安装时检查匹配。

任何防止网络钓鱼攻击的建议(类似或不同的方法)?

更新: 约50%的应用程序用户直接从我的公司网站安装应用程序(Unknown-Source)。

3 个答案:

答案 0 :(得分:5)

Android提供了一种原生方法,用于验证是否已从Play商店,甚至亚马逊应用商店安装应用。但它是否适用于其他应用程序?

看一下方法:

PackageManager.getInstallerPackageName(String packageName) (Documentation)

幸运的是,getInstallerPackageName接受了一个String包名,这意味着你可以将它提供给你的邻居应用程序包ID,并让它返回安装它的包名!

此示例将告诉您邻居应用是否由Play商店安装,即com.android.vending

if(context.getPackageManager().getInstallerPackageName("com.untrusted.app") ==
  "com.android.vending") {                          //   ^^^ CHANGE ^^^

  //Verified; app installed directly from Play Store
}

亚马逊应用商店有一个奇怪的包名:

if(context.getPackageManager().getInstallerPackageName("com.untrusted.app") ==
  "com.amazon.venezia") {

  //Verified; app installed directly from Amazon App Store
}

这应该是你需要的一切,希望它有所帮助!

答案 1 :(得分:3)

如果主应用程序通过包管理器api获得支付应用程序的签名并验证它(例如,通过签名的whitlist)会怎么样?验证过程也可以在主应用服务器中进行。

编辑: 这样的事情

        List<byte[]> whitelist = ... ; // load from config or something...

        PackageManager pm = this.getPackageManager();
        String packageName = "payment.app.package.name";

        PackageInfo packageInfo = pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
        Signature[] signatures = packageInfo.signatures;

        for (Signature sig : signatures) {
            InputStream input = new ByteArrayInputStream(sig.toByteArray());
            CertificateFactory cf = CertificateFactory.getInstance("X509");
            X509Certificate cert = (X509Certificate) cf.generateCertificate(input);

            PublicKey pb = cert.getPublicKey();
            if (! whitelist.contains(pb.getEncoded())) {
                throw new Exception("public key is not valid");
            }
        }

答案 2 :(得分:2)

您正在寻找的是应用程序签名和权限的组合。为了澄清,这只有在您拥有两个应用程序并且可以使用相同的密钥签名时才有效。

根据文件:

  

&#34;签名&#34;:系统仅在请求的应用程序授予的权限   使用与声明的应用程序相同的证书进行签名   许可。如果证书匹配,则系统自动进行   在不通知用户或要求用户的情况下授予许可   用户的明确批准。

这意味着Android操作系统本身强制要求两个应用程序的签名必须匹配才能授予权限。这就是您想要的,因为如果有人试图欺骗您的应用,则不会授予权限。由于他们无法使用您的私钥签署他们的应用程序,因此他们无法伪造您的签名。

要在提供付款服务的应用程序的清单中创建权限,您应该声明如下:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp.paymentservice" >

    <permission android:name="com.example.myapp.paymentservice.permission.PAYMENT"
        android:label="@string/permission_payment_label"
        android:description="@string/permission_payment_description"
        android:protectionLevel="signature" />
    ...

然后,当您声明服务时,请使用您声明的android:permission保护它:

    ...
    <service android:name=".BillingService"
        android:permission="com.example.myapp.paymentservice.permission.PAYMENT" />
</manifest>

最后,您在客户端应用程序中声明您使用了该权限:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myapp.clientapp" >

    <uses-permission android:name="com.example.myapp.paymentservice.permission.PAYMENT" />
    ...
</manifest>

只要两个应用程序使用相同的签名密钥签名,操作系统就会授予权限。有关详情,请查看http://developer.android.com/guide/topics/security/permissions.html#defininghttp://developer.android.com/guide/topics/manifest/permission-element.html