web-dev-qa-db-ja.com

ユーザーが偽の場所を使用しているかどうかを検出または防止する

ユーザーが場所を偽る場合、アプリの使用をブロックします。そこで、isFromMockProviderを使用して、場所が偽物であるかどうかを確認します( here に従ってください)。ただし、場合によっては、isFromMockProvider()は偽の場所に対してfalseを返すことがあります。

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

私の場合は:私はアプリを使用します Fake GPS location 場所への偽物のために、偽物の場所を無効にしてアプリに行きます。次に、onLocationChangedはisFromMockProvider() = falseで偽の場所を返します

ビデオレコーダーhttps://www.youtube.com/watch?v=rWVvjOCaZiI (このビデオでは、現在の場所は16.06、108.21、偽の場所は36.26,138.28です。最後のビデオでは、場所は36.26,138.28ですが、isFromMockProvider = falseです。

この場合、ユーザーが偽の場所を使用しているかどうかを検出する方法はありますか?どんな助けや提案も大歓迎です。
DEMOプロジェクト

14
Phan Van Linh

現実的な回答のリスク

開発者が製品デザインの広報の側面を理解し、批判の危険を冒すのに役立つ答えを提供したいと思います。率直に言って、コンピューターサイエンスの世界では素晴らしいアプリを書くことはできません。ユーザーのニーズを満たし、それらをセキュリティとバランスさせることは、特にモバイル分野において、今日のソフトウェアインターフェイスと動作設計における主要な問題の1つです。

この観点から、あなたの質問、「この場合、ユーザーが偽の場所を使用しているかどうかを検出する方法はありますか?」あなたが直面する最も適切な質問ではないかもしれません。私はあなたにもっと役立つかもしれないこの他の質問をすることで回避しているわけではなく、私はよく答えることができます:「ユーザーのデバイスのジオコーディネイトファームウェアからデータを安全に取得して、なりすましができないようにする方法はありますか?」

これに対する答えは、「いいえ」です。

Android HTTPクライアント契約

Androidクライアント/サーバー契約、またはユーザーのデバイスの位置情報を保証する競合他社の契約の一部ではありません。

実用的な理由

実際には、おそらくこのような保証に無期限に反対する市場力があります。多くのデバイス所有者(およびユーザー)は、プライバシーとホームおよび家族のセキュリティ上の理由から、人々が自分の本当の位置を知っているかどうかを制御したいと考えています。

ソリューション

ソフトウェアのデザイナーとして自問できる次の質問は、「アプリ(またはライブラリ)はどのように機能し、今日(または明日)のロケーションスプーフィングソフトウェアを使用して、特定の割合のユーザーコミュニティで満たす必要があるニーズにどのように対応できますか? 」

ビジネスインテリジェンスソフトウェアを書いている場合、またはシステムに他の統計的側面がある場合、エラーバーに相当するソフトウェアが必要です。統計を表示する場合、エラーバーは適切なグラフ機能になります。ユーザーの人口のうち、場所のなりすましの割合を推定するには、さらに調査する必要があります。

13
FauChristian

2つの方法で偽の場所を特定します。まず、ここの他のコードのように、モックの場所を確認します。

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

(更新)

残念ながら、Googleはアプリケーションが現在実行中のアプリのリストを受け取ることを禁止しています。 (5.1.1以降でしたが、実行されたテストデバイスでアプリリストを取得できますAndroid 7.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;
}
12
John

回答 このSO質問 および程度は低いが このSO question は、古いタイムスタンプでonLocationChangedが呼び出されたためにFusedLocationApiでの不幸なキャッシングの問題に苦しんでいることを示しているようです(したがって、新しいデータがあると考えられるため、結果を無視します)。

リノの答えを引用するには

変更していない限り...新しいAPを検出できるようにするために、キャッシュされた場所のみを取得することを恐れています。新しい場所が必要な場合は、GPSプロバイダーを使用します。

解決策は、代わりに GPS Provider から場所を呼び出すことです:

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

(上記のコードは、より長い例 here からのものです)

4
Temporary

私は自分のプロジェクトでこの方法を使用していますが、今まで完璧に機能します:

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はバグがあり、時々OKとしてモックされた場所を表示します!!!このリンクには詳細が記載された回避策があります Androidでの場所:Mocking Me!

アプローチは次のとおりです。

  • モックとしてラベル付けされた最新の場所を覚えておいてください

  • 新しい「非モック」読み取り値が最後のモックから1 km以内にある場合は、拒否します。

  • 20回連続の「非モック」読み取り後、最後のモック位置のみをクリアします。

1