Обнаружить или предотвратить, если пользователь использует поддельное местоположение
Я заблокирую пользователям использование моего приложения, если они имитируют местоположение. Поэтому я использую 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 последовательных "не фиктивных" показаний.