当前位置: 首页 > 知识库问答 >
问题:

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

曾弘扬
2023-03-14

如果用户伪造位置,我会阻止他们使用我的应用程序。所以我使用isFromMockProvider来检查位置是否是假的(请点击此处)。但是isFromMockProvider()在某些情况下可能会为伪造的位置返回false。

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

录像机:https://www.youtube.com/watch?v=rWVvjOCaZiI(在这段视频中,我的当前位置是16.06108.21,假位置是36.26138.28。你可以在上一段视频中看到,位置是36.26138.28,但isFromMockProvider=false)

在这种情况下,是否有任何方法可以检测用户是否使用了虚假位置?任何帮助或建议都将不胜感激。
DEMO项目

共有3个答案

岳安福
2023-03-14

关于这个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);

(上面的代码来自一个较长的示例)

宦瀚
2023-03-14

我使用两种方法来识别假位置。首先,我检查模拟位置,就像这里的其他代码一样。

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

(更新)

不幸的是,谷歌禁止应用程序接收当前运行的应用程序列表。(从5.1.1开始,但我仍然可以在运行Android7.1的测试设备中获得应用列表)

现在,通过使用UsageStatsManager,您只能获得最近使用的应用程序列表(具有请求运行时权限),例如Android 5.1.1及以上版本——getRunningAppProcesses()仅返回我的应用程序包

所以,如果用户关闭或退出假定位应用程序,我无法确定。

现在,要获取虚假位置应用程序的列表,我尝试获取位置,如果location.isFromMockProvider()返回true,我扫描设备上所有已安装的应用程序,需要访问模拟位置的权限,如下所示:

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;
}
任昊苍
2023-03-14

冒着现实答案的风险

我想提供一个答案,帮助开发者理解产品设计的公共关系方面,冒着被批评的风险。坦率地说,在计算机科学的真空中,一个人无法编写出伟大的应用程序。满足用户需求并在安全性之间取得平衡是当今软件界面和行为设计的主要问题之一,尤其是在移动领域。

从这个角度来看,你的问题是,“在这种情况下,有没有办法检测用户是否使用了假位置?”可能不是你面临的最中肯的问题。我并不是在回避这个可能对你有更多帮助的问题,我可以很好地回答这个问题:“有没有办法安全地从用户设备的geocoordinate固件获取数据,这样就不会被欺骗?”

这个问题的答案是:“不。”

Android HTTP客户端合约

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

实际原因

实际上,市场力量可能会无限期地反对这样的保证。出于隐私以及家庭和家庭安全的原因,许多设备所有者(以及您的用户)希望控制人们是否知道他们的真实位置。

溶液

作为软件设计师,你可以问自己的下一个问题是,“应用程序或库如何工作,如何满足我使用今天(或明天)的位置欺骗软件的用户群体的一定比例的需求?”

如果你正在编写商业智能软件,或者你的系统还有其他一些统计方面,那么你需要与误差条相当的软件。如果显示统计数据,那么错误条将是一个合适的图形功能。估计用户群中位置欺骗的百分比需要进一步研究。

 类似资料:
  • 问题内容: 有没有一种方法可以检测当前用户是否正在使用jQuery / JavaScript使用iPad? 问题答案: iPad检测 通过查看属性,您应该能够检测到iPad用户: iPhone / iPod检测 同样,用于检查iPhone或iPods等设备的属性:

  • 我有一个Mapstruct映射器,在那里我必须做一些字符串转换服务。即,从自然语言短语列表到实用程序类中定义的不透明常量。不,我这次不使用枚举。它涉及一些Levenshtein检查和预定义的单词列表。 我在Spring bean中有一个方法,我想使用它来映射我的DTO中的单个字符串字段。 如果我在映射中使用Mapstruct的uses属性,我发现Mapstruct会滥用definedBean。ge

  • 问题内容: 好的。 我可以从我的理解中得知某人正在滚动。因此,我试图找出有人停下来时该如何捕捉。从上面的示例中,您可以看到发生滚动时,我正在从一组元素中删除一个类。但是,我想在用户停止滚动时重新打开该类。 这样做的原因是,我打算在页面滚动时进行中转节目,以使页面具有我正在尝试的特殊效果。但是我尝试在滚动时删除的一个类与该效果冲突,因为它对某种性质是透明的。 问题答案: 更新资料 我写了一个扩展来增

  • 出于学习目的,我正在使用OAuth2开发一个REST API Angular 4应用程序。 有些受保护的endpoint只能由经过身份验证的用户调用。让我们以更新用户配置文件的endpoint为例: /users/{user_id} 登录用户将能够通过转到更新配置文件屏幕来更新自己的配置文件,该屏幕在后台将调用 /users/{user_id}发送带有新信息和访问令牌的有效载荷。 我们如何防止恶意

  • 我试图阻止用户在谷歌地图的某个区域之外移动。然而,似乎没有一种简单的方法来做到这一点。第一次在这里求你发发慈悲吧。 我尝试了以下方法,试图在用户滚动时进行轮询,但问题是OncamaChange触发的频率不够,因此它的工作不稳定且不准确。我知道一定有某种方法可以做到这一点,因为它似乎是一个简单而基本的功能。

  • 有没有一种方法可以检测用户何时在chrome浏览器(移动和桌面)中嘲弄他的位置?试过谷歌搜索,但我只看到了如何实际模仿和如何在原生移动应用程序中检测。