检测或防止用户使用假的位置

我会阻止用户使用我的应用程序,如果他们假的位置。 所以我使用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(在这个video中,我的当前位置是16.06,108.21,假的位置是36.26,138.28。在上一个video中可以看到位置是36.26, 138.28但isFromMockProvider = false)

在这种情况下,有没有办法检测用户是否使用了伪造的位置? 任何帮助或build议将不胜感激。
DEMO项目

Solutions Collecting From Web of "检测或防止用户使用假的位置"

冒着现实的答案

我想提供一个答案,帮助开发者理解产品devise的公共关系方面,冒着批评的风险。 坦率地说,在计算机科学的真空中,不可能写出很棒的应用程序。 满足用户需求并使其与安全性平衡是当今软件界面和行为devise中的主要问题之一,特别是在移动领域。

从这个angular度来看,你的问题是:“在这种情况下,有没有办法检测用户是否使用了伪造的位置? 可能不是你所面对的最相关的问题。 通过询问这个可能对您有所帮助的其他问题,我不会回避这个问题,而且我可以很好地回答:“有没有办法从用户设备的地理坐标固件中安全地获取数据,使其不会被欺骗?

这个答案是“不是”

Android HTTP客户端合同

它不是保证用户设备位置信息的Android客户端服务器合同或其竞争对手的合同的一部分。

实际的理由

实际上有一个市场力量可能无限期地推动这样的保证。 许多设备所有者(以及您的用户)想要控制人们是否知道他们的隐私和家庭和家庭安全原因的真实位置。

下一个问题,你可以问自己,作为你的软件devise者是:“应用程序或图书馆如何工作,并提供我想要填补一定比例的用户社区使用今天(或明天)的位置欺骗软件的需求? “

如果您正在编写商业智能软件,或者您的系统有其他一些统计方面,那么您需要软件等同于错误栏。 如果你显示统计数据,那么误差线就是一个适当的图表特征。 估计位于用户总数中的位置欺骗者的百分比将需要进一步的研究。

我用两种方法来识别假的地点。

首先,我检查模拟位置,就像这里的其他代码一样。

 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中的一个不幸的caching问题,由onLocationChanged被调用一个过时的时间戳(因此忽略结果,因为它认为那里已经是较新的数据)。

引用里诺的回答 :

除非你没有改变……所以可以发现新的AP,恐怕你只能得到caching的位置。 如果你想新的地点使用GPS提供商。

解决scheme将改为从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上的位置:停止嘲笑我!

方法是:

  • 请记住标记为模拟的最新位置

  • 如果一个新的“非模拟”的读数是在最后一个模拟的1公里内,拒绝它。

  • 只有在连续20次“非模拟”读数后才能清除最后的模拟位置。