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

Android 如何在运行时验证已安装的应用程序以防止网络钓鱼攻击?,android,security,android-intent,phishing,Android,Security,Android Intent,Phishing,我的设备中有一个支付应用程序,我的应用程序连接到该应用程序的服务以获取启动支付活动的挂起意图,然后在onActivityResult()方法中侦听结果。(类似于应用程序内购买场景) 我为支付意图设置包名。但你们知道,这并不能保证支付应用程序是可信的。如果有人使用相同的软件包名称和相同的aidl服务实现通过未知源安装了假应用程序,那么它可以向我提供挂起的意图和钓鱼我的用户信息 我用某种机制来验证支付结果,这只会保护我的应用程序不受虚假支付结果的影响,但我的应用程序用户会在钓鱼程序中输入他们的数据。

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

我为支付意图设置包名。但你们知道,这并不能保证支付应用程序是可信的。如果有人使用相同的软件包名称和相同的aidl服务实现通过未知源安装了假应用程序,那么它可以向我提供挂起的意图和钓鱼我的用户信息

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

我知道一些可以检查其他应用程序签名和公钥的方法如果Android操作系统保证公钥和签名是只读的,并且与已安装的应用程序相匹配,我可以信赖这一点,并在发送意向之前检查支付应用程序的公钥。但我想这些还没有准备好,只能在安装时检查匹配

有没有关于防止网络钓鱼攻击的建议(类似或不同的方法)

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

Android提供了一种本地方法来验证应用程序是否已从Play Store,甚至亚马逊应用程序商店安装。但它在其他应用程序上有效吗

看看这个方法:

PackageManager.getInstallerPackageName(字符串packageName)

幸运的是,
getInstallerPackageName
接受一个字符串包名,这意味着您可以将邻居应用程序的包id提供给它,并让它返回安装它的包名

此示例将告诉您,您的邻居应用程序是否由Play Store(又名
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
}

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

如果主应用程序通过package manager 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");
            }
        }
列表白名单=…;//从配置或其他地方加载。。。
PackageManager pm=this.getPackageManager();
String packageName=“payment.app.package.name”;
PackageInfo PackageInfo=pm.getPackageInfo(packageName,PackageManager.GET_签名);
签名[]签名=packageInfo.signatures;
用于(签名:签名){
InputStream input=new ByteArrayInputStream(sig.toByteArray());
CertificateFactory cf=CertificateFactory.getInstance(“X509”);
X509证书证书=(X509证书)cf.generateCertificate(输入);
PublicKey pb=cert.getPublicKey();
如果(!whitelist.contains(pb.getEncoded())){
抛出新异常(“公钥无效”);
}
}

您要查找的是应用程序签名和权限的组合。澄清一下,只有当您拥有两个应用程序并且可以使用相同的密钥对它们进行签名时,这才有效

根据文件:

“签名”:仅当请求的应用程序 使用与声明的应用程序相同的证书进行签名 许可。如果证书匹配,系统将自动 授予权限而不通知用户或请求 用户的明确批准

这意味着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" />
    ...
最后,在客户端应用程序中声明使用该权限:

<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>

...

只要两个应用程序使用相同的签名密钥签名,操作系统就会授予该权限。有关更多信息,请查看,我是否理解您的要求。请确认我做了。您有一个服务器和一个客户端,并希望在请求/向其发送数据期间验证该服务器实际上是您的,而不是任何黑客。我是否正确???@Ram否,我有一个通过服务连接到支付应用程序的应用程序(aidl实现)。我想在将我的用户发送到之前验证付款应用程序。(实际上,我的应用程序和支付应用程序都有服务器,但我的问题不是验证它们)安卓操作系统可以在安装应用程序之前为您提供公钥验证保证,但如果用户不知道“避免未知源设置”等,这也很容易被黑客攻击。相信应用程序就在用户手中,即使他们对此非常小心,受损设备也会导致用户数据受损。一旦在系统上安装应用程序,您不能期望该设备在每次运行时都对其进行验证。这完全取决于用户和操作系统设置。数据隐私在安装后掌握在应用程序手中。在发送数据之前,您可以使用公钥加密来验证应用程序是否信任服务器。如果应用程序本身不受信任,则无法保存客户端。让用户了解的信任设置的唯一方法
<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>