12

我的设备中有一个支付应用程序,我的应用程序连接到该应用程序的服务以获得启动支付活动的挂起意图,然后在 onActivityResult() 方法中收听结果。(类似于应用内购买场景)

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

我使用某种机制验证支付结果,并且只保护我的应用程序免受虚假支付结果的影响,但我的应用程序用户在钓鱼应用程序中输入他们的数据。(这一段说我的问题是不信任支付应用程序的响应,我的问题是在启动他们的活动之前信任支付应用程序

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

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

更新:大约 50% 的应用程序用户直接从我的公司网站安装应用程序(未知来源)。

4

3 回答 3

5

Android offers a native method to verify if an app has been installed from Play Store, even Amazon App Store. But does it work on other apps?

Take a look at the method:

PackageManager.getInstallerPackageName(String packageName) (Documentation)

Fortunately for you, getInstallerPackageName accepts a String package name, which means you can feed it your neighbor-app's package id, and have it return the package name that installed it!

This example will tell you if your neighbor-app was installed by Play Store, aka com.android.vending:

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

  //Verified; app installed directly from Play Store
}

Amazon app store has an odd package name:

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

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

This should be everything you need, hope it helps!

于 2016-04-12T06:30:55.833 回答
3

您正在寻找的是应用签名和权限的组合。澄清一下,这仅在您拥有这两个应用程序并且可以使用相同的密钥对其进行签名的情况下才有效。

根据文档:

“签名”:只有当请求应用程序使用与声明权限的应用程序相同的证书签名时,系统才会授予该权限。如果证书匹配,系统会自动授予权限,无需通知用户或请求用户明确批准。

这意味着 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

于 2016-04-15T23:58:49.533 回答
3

如果主应用程序通过包管理器 api 获得支付应用程序的签名并验证它(例如通过签名白名单)怎么办?验证过程也可以发生在主应用服务器中。

编辑:类似的东西

        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");
            }
        }
于 2016-04-14T16:18:32.687 回答