Обнаружить или предотвратить, если пользователь использует поддельное местоположение

Я заблокирую пользователям использование моего приложения, если они имитируют местоположение. Поэтому я использую isFromMockProvider чтобы проверить, является ли местоположение поддельным (следуйте здесь). Но isFromMockProvider() в некоторых случаях может возвращать false для поддельных локаций.

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

Мой случай таков: я использую приложение " Поддельное местоположение GPS" для подделки местоположения, затем отключаю "Поддельное местоположение" и захожу в приложение. Затем onLocationChanged возвращает поддельное местоположение с isFromMockProvider() = false

Видеорегистратор: https://www.youtube.com/watch?v=rWVvjOCaZiI (в этом видео мое текущее местоположение - 16.06, 108.21, поддельное местоположение - 36.26,138.28. Вы можете видеть в последнем видео, местоположение - 36.26, 138.28 но isFromMockProvider=false)

Есть ли способ определить, использует ли пользователь в этом случае поддельное местоположение? Любая помощь или предложение будет с благодарностью.
ДЕМО проект

4 ответа

Рисковать реалистичным ответом

Я хотел бы дать ответ, который поможет разработчику понять аспект разработки продукта, связанный с общественностью, рискуя подвергнуться критике. Честно говоря, нельзя создавать великолепные приложения в вакууме информатики. Удовлетворение потребностей пользователей и обеспечение их баланса с безопасностью - одна из основных проблем современного программного интерфейса и поведенческого дизайна, особенно в мобильном пространстве.

С этой точки зрения, ваш вопрос: "Есть ли способ определить, использует ли пользователь в этом случае поддельное местоположение?" возможно, не самый актуальный вопрос, с которым вы сталкиваетесь. Я не уклоняюсь, задавая другой вопрос, который может помочь вам больше, и на этот вопрос я могу ответить хорошо: "Есть ли какой-нибудь способ надежно получить данные из встроенной микропрограммы геокординаты устройства пользователя, чтобы их нельзя было подделать?"

Ответ на этот вопрос "Нет"

Клиентский HTTP-контракт Android

Он не является частью соглашения клиент-сервер Android или его конкурентов, чтобы гарантировать информацию о местоположении устройства пользователя.

Практическая причина

На самом деле существует рыночная сила, которая, вероятно, будет давить на такую ​​гарантию бесконечно. Многие владельцы устройств (и ваши пользователи) хотят контролировать, знают ли люди свое истинное местоположение по соображениям конфиденциальности, а также по соображениям безопасности дома и в семье.

Решение

Следующий вопрос, который вы можете задать себе как разработчику вашего программного обеспечения: "Как приложение или библиотека могут работать и обеспечивать потребности, которые я стремлюсь заполнить определенным процентом сообщества пользователей, используя сегодняшнее (или завтрашнее) программное обеспечение для определения местоположения? "

Если вы пишете программное обеспечение для бизнес-аналитики или есть какой-то другой статистический аспект в вашей системе, то вам нужен программный эквивалент панелей ошибок. Если вы отображаете статистику, то панели ошибок будут подходящей графической функцией. Оценка процента спуферов местоположения среди населения пользователей потребует дальнейшего изучения.

Я использую два способа определения поддельных мест.

Во-первых, я проверяю расположение макета, как в другом коде здесь.

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");
    }
}

Во-вторых, я проверяю работающие приложения и сервисы, которым нужно разрешение для доступа к макету. И, кажется, работает хорошо.

public static List<String> getListOfFakeLocationApps(Context context) {
    List<String> runningApps = getRunningApps(context, false);
    for (int i = runningApps.size() - 1; i >= 0; i--) {
        String app = runningApps.get(i);
        if(!hasAppPermission(context, app, "android.permission.ACCESS_MOCK_LOCATION")){
            runningApps.remove(i);
        }
    }
    return runningApps;
}

public static List<String> getRunningApps(Context context, boolean includeSystem) {
    ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);

    List<String> runningApps = new ArrayList<>();

    List<ActivityManager.RunningAppProcessInfo> runAppsList = activityManager.getRunningAppProcesses();
    for (ActivityManager.RunningAppProcessInfo processInfo : runAppsList) {
        for (String pkg : processInfo.pkgList) {
            runningApps.add(pkg);
        }
    }

    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();
    }

    List<ActivityManager.RunningServiceInfo> runningServices = activityManager.getRunningServices(1000);
    for (ActivityManager.RunningServiceInfo serviceInfo : runningServices) {
        runningApps.add(serviceInfo.service.getPackageName());
    }

    runningApps = new ArrayList<>(new HashSet<>(runningApps));

    if(!includeSystem){
        for (int i = runningApps.size() - 1; i >= 0; i--) {
            String app = runningApps.get(i);
            if(isSystemPackage(context, app)){
                runningApps.remove(i);
            }
        }
    }
    return 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;
}

Ответы на этот вопрос SO и, в меньшей степени, ответы на этот вопрос SO, похоже, указывают на то, что вы страдаете от неудачной проблемы кэширования в FusedLocationApi, вызванной вызовом onLocationChanged с устаревшей временной меткой (таким образом, игнорируя результат, поскольку он считает, что это уже более новые данные).

Процитирую ответ Рено:

Если вы не изменились... так что новые AP могут быть обнаружены, я боюсь, что вы получите только кэшированные местоположения. Если вы хотите свежие местоположения, используйте провайдера GPS.

Решение будет заключаться в том, чтобы вместо этого вызывать местоположение из провайдера GPS следующим образом:

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

(Код выше взят из более длинного примера здесь)

Я использую этот метод в своих проектах, и он прекрасно работает до сих пор:

для 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;
}

Для api >= 18 вы должны использовать

location.isFromMockProvider();

Дело в том location.isFromMockProvider глючит и иногда показывает поддельное местоположение как его ОК!!! В этой ссылке есть обходной путь с подробным описанием Местоположение на Android: Stop Mocking Me!

подход таков:

  • Помните самое последнее местоположение, помеченное как макет

  • Если новое "не фиктивное" значение находится в пределах 1 км от последнего макета, отклоните его.

  • Очистите последнее место для макета только после 20 последовательных "не фиктивных" показаний.

Другие вопросы по тегам