web-dev-qa-db-fra.com

Détecter ou empêcher si l'utilisateur utilise un faux emplacement

Je vais empêcher un utilisateur d'utiliser mon application s'il simule l'emplacement. J'utilise donc isFromMockProvider pour vérifier si l'emplacement est faux (suivez ici ). Mais isFromMockProvider() peut retourner false pour les emplacements falsifiés dans certains cas.

public void onLocationChanged(Location location) {
    textView.append("long:"+location.getLatitude()+" - lat:"+location.getLongitude()+" - isMock :"+location.isFromMockProvider() + "\n");
}

Mon cas est : j'utilise l'application Fake GPS location pour fake to a location puis je désactive fake location et vais sur mon application . Ensuite, onLocationChanged renvoie le faux emplacement avec isFromMockProvider() = false

Enregistreur vidéo : https://www.youtube.com/watch?v=rWVvjOCaZiI (dans cette vidéo, mon courant l'emplacement est 16.06, 108.21, le faux emplacement est 36.26.138.28. Vous pouvez voir dans la dernière vidéo l'emplacement est 36.26.138.28 mais isFromMockProvider = false)

Existe-t-il un moyen de détecter si un utilisateur utilise un faux emplacement dans ce cas? Toute aide ou suggestion serait grandement appréciée.
projet DEMO

14
Phan Van Linh

Risquer une réponse réaliste

Je voudrais apporter une réponse qui aide le développeur à comprendre l'aspect des relations publiques de la conception des produits, en prenant le risque d'être critiqué. Franchement, on ne peut pas écrire d'excellentes applications dans un vide informatique. Satisfaire les besoins des utilisateurs et les équilibrer avec la sécurité est l'un des principaux problèmes de l'interface logicielle et de la conception comportementale aujourd'hui, en particulier dans l'espace mobile.

De ce point de vue, votre question: "Existe-t-il un moyen de détecter si un utilisateur utilise un faux emplacement dans ce cas?" n'est peut-être pas la question la plus pertinente à laquelle vous êtes confronté. Je ne suis pas évasif en posant cette autre question qui peut vous aider davantage et c'est une chose à laquelle je peux bien répondre: "Existe-t-il un moyen d'obtenir en toute sécurité les données du firmware géocoordonné de l'appareil de l'utilisateur de sorte qu'elles ne puissent pas être usurpées?"

La réponse à celle-ci est "non".

Contrat client HTTP Android

Il ne fait pas partie du contrat client-serveur Android Android ou de ses concurrents) pour garantir les informations de localisation des appareils des utilisateurs.

Raison pratique

Il existe en fait une force de marché qui va probablement pousser indéfiniment contre une telle garantie. De nombreux propriétaires d'appareils (et vos utilisateurs) souhaitent contrôler si les gens connaissent leur véritable emplacement pour des raisons de confidentialité et de sécurité de la maison et de la famille.

Solution

La question suivante que vous pouvez vous poser en tant que concepteur de votre logiciel est la suivante: "Comment l'application ou la bibliothèque peuvent-elles fonctionner et répondre aux besoins que je cherche à combler avec un certain pourcentage de la communauté d'utilisateurs utilisant le logiciel d'usurpation de position d'aujourd'hui (ou de demain)? "

Si vous écrivez un logiciel de Business Intelligence ou qu'il existe un autre aspect statistique de votre système, vous avez besoin de l'équivalent logiciel des barres d'erreur. Si vous affichez les statistiques, les barres d'erreur seraient une fonction graphique appropriée. L'estimation du pourcentage de spoofers de localisation sur une population d'utilisateurs nécessiterait une étude plus approfondie.

13
FauChristian

J'utilise deux façons d'identifier les faux emplacements. Tout d'abord, je vérifie l'emplacement fictif, comme dans d'autres codes ici.

public static boolean isMockLocationOn(Location location, Context context) {
    if (Android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
        return location.isFromMockProvider();
    } else {
        String mockLocation = "0";
        try {
            mockLocation = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return !mockLocation.equals("0");
    }
}

Deuxièmement, je vérifie les applications et services en cours d'exécution, qui ont besoin d'une autorisation pour accéder à l'emplacement fictif.

public static List<String> getListOfFakeLocationApps(Context context) {
    List<String> runningApps = getRunningApps(context);
    List<String> fakeApps = new ArrayList<>();
    for (String app : runningApps) {
        if(!isSystemPackage(context, app) && hasAppPermission(context, app, "Android.permission.ACCESS_MOCK_LOCATION")){
            fakeApps.add(getApplicationName(context, app));
        }
    }
    return fakeApps;
}

public static List<String> getRunningApps(Context context, boolean includeSystem) {
    ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
    HashSet<String> runningApps = new HashSet<>();
    try {
        List<ActivityManager.RunningAppProcessInfo> runAppsList = activityManager.getRunningAppProcesses();
        for (ActivityManager.RunningAppProcessInfo processInfo : runAppsList) {
            runningApps.addAll(Arrays.asList(processInfo.pkgList));
        }
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    try {
        //can throw securityException at api<18 (maybe need "Android.permission.GET_TASKS")
        List<ActivityManager.RunningTaskInfo> runningTasks = activityManager.getRunningTasks(1000);
        for (ActivityManager.RunningTaskInfo taskInfo : runningTasks) {
            runningApps.add(taskInfo.topActivity.getPackageName());
        }
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    try {
        List<ActivityManager.RunningServiceInfo> runningServices = activityManager.getRunningServices(1000);
        for (ActivityManager.RunningServiceInfo serviceInfo : runningServices) {
            runningApps.add(serviceInfo.service.getPackageName());
        }
    } catch (Exception ex) {
        ex.printStackTrace();
    }
    return new ArrayList<>(runningApps);
}

public static boolean isSystemPackage(Context context, String app){
    PackageManager packageManager = context.getPackageManager();
    try {
        PackageInfo pkgInfo = packageManager.getPackageInfo(app, 0);
        return (pkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
    } catch (PackageManager.NameNotFoundException e) {
        e.printStackTrace();
    }
    return false;
}

public static boolean hasAppPermission(Context context, String app, String permission){
    PackageManager packageManager = context.getPackageManager();
    PackageInfo packageInfo;
    try {
        packageInfo = packageManager.getPackageInfo(app, PackageManager.GET_PERMISSIONS);
        if(packageInfo.requestedPermissions!= null){
            for (String requestedPermission : packageInfo.requestedPermissions) {
                if (requestedPermission.equals(permission)) {
                    return true;
                }
            }
        }
    } catch (PackageManager.NameNotFoundException e) {
        e.printStackTrace();
    }
    return false;
}

public static String getApplicationName(Context context, String packageName) {
    String appName = packageName;
    PackageManager packageManager = context.getPackageManager();
    try {
        appName = packageManager.getApplicationLabel(packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA)).toString();
    } catch (PackageManager.NameNotFoundException e) {
        e.printStackTrace();
    }
    return appName;
}

(Mise à jour)

Malheureusement, Google interdit aux applications de recevoir la liste des applications en cours d'exécution. (C'était depuis 5.1.1, mais je peux toujours obtenir la liste des applications dans les appareils de test exécutés Android 7.1)

Maintenant, vous ne pouvez obtenir que la liste des applications récemment utilisées (avec l'autorisation d'exécution de la demande) en utilisant UsageStatsManager, par exemple comme ici Android 5.1.1 et supérieur - getRunningAppProcesses () renvoie uniquement mon package d'application

Donc, si l'utilisateur ferme ou quitte l'application de faux emplacement, je ne peux pas le déterminer.

Et maintenant, pour obtenir la liste des applications de faux emplacement, j'essaie d'obtenir des emplacements, et si location.isFromMockProvider () retourne vrai, je scanne le périphérique pour toutes les applications installées, qui ont besoin d'une autorisation pour accéder à l'emplacement fictif comme ceci:

public static List<String> getListOfFakeLocationAppsFromAll(Context context) {
    List<String> fakeApps = new ArrayList<>();
    List<ApplicationInfo> packages = context.getPackageManager().getInstalledApplications(PackageManager.GET_META_DATA);
    for (ApplicationInfo aPackage : packages) {
        boolean isSystemPackage = ((aPackage.flags & ApplicationInfo.FLAG_SYSTEM) != 0);
        if(!isSystemPackage && hasAppPermission(context, aPackage.packageName, "Android.permission.ACCESS_MOCK_LOCATION")){
            fakeApps.add(getApplicationName(context, aPackage.packageName));
        }
    }
    return fakeApps;
}
12
John

Réponses sur Cette SO question et dans une moindre mesure les réponses sur This SO question semblent indique que vous souffrez d'un problème de mise en cache malheureux dans le FusedLocationApi causé par l'appel de onLocationChanged avec un horodatage obsolète (ignorant ainsi le résultat car il pense qu'il existe déjà des données plus récentes).

Pour citer réponse de Reno :

À moins que vous n'ayez pas changé ... afin que de nouveaux points d'accès puissent être découverts, je crains que vous n'obtiendrez que des emplacements mis en cache. Si vous voulez de nouveaux emplacements, utilisez le fournisseur GPS.

La solution sera d'appeler à la place un emplacement à partir du fournisseur GPS comme ceci:

LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
LocationListener locationListener = new MyLocationListener();
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 5000, 10, locationListener);

(Le code ci-dessus provient d'un exemple plus long ici )

4
Temporary

J'utilise cette méthode dans mes projets et elle fonctionne parfaitement jusqu'à présent:

pour api <18

//returns true if mock location enabled, false if not enabled.
public static boolean isMockLocationOn(Context context) {
    if (Settings.Secure.getString(context.getContentResolver(), Settings.Secure.ALLOW_MOCK_LOCATION).equals("0"))
        return false;
    else
        return true;
}

Pour api> = 18, vous devez utiliser

location.isFromMockProvider();

Le point est location.isFromMockProvider est bogué et parfois il montrera un emplacement simulé comme OK !!! Il y a une solution de contournement dans ce lien avec tous les détails Emplacement sur Android: Arrêtez de me moquer!

l'approche est la suivante:

  • Rappelez-vous l'emplacement le plus récent étiqueté comme une maquette

  • Si une nouvelle lecture "non fictive" se trouve à moins de 1 km de la dernière simulation, rejetez-la.

  • Effacez uniquement le dernier emplacement fictif après 20 lectures "non fictives" consécutives.

1
Omid Heshmatinia