Android での GPS スプーフィングを防止/検出する最善の方法を探しています。これがどのように達成されるか、またそれを止めるために何ができるかについて何か提案はありますか? 私は、ユーザーが GPS を偽装するために模擬位置をオンにする必要があると推測しています。これが行われた場合、彼らは GPS を偽装できますか?
モックの場所が有効になっているかどうかを検出する必要があると思いますか? 他の提案はありますか?
私はいくつかの調査を行い、ここで結果を共有しました。これは他の人にとって役立つかもしれません.
まず、MockSetting オプションがオンになっているかどうかを確認できます。
public static boolean isMockSettingsON(Context context) {
// returns true if mock location enabled, false if not enabled.
if (Settings.Secure.getString(context.getContentResolver(),
Settings.Secure.ALLOW_MOCK_LOCATION).equals("0"))
return false;
else
return true;
}
android.permission.ACCESS_MOCK_LOCATION
2 つ目は、 (位置偽装アプリ)を使用している他のアプリがデバイスにあるかどうかを確認できます。
public static boolean areThereMockPermissionApps(Context context) {
int count = 0;
PackageManager pm = context.getPackageManager();
List<ApplicationInfo> packages =
pm.getInstalledApplications(PackageManager.GET_META_DATA);
for (ApplicationInfo applicationInfo : packages) {
try {
PackageInfo packageInfo = pm.getPackageInfo(applicationInfo.packageName,
PackageManager.GET_PERMISSIONS);
// Get Permissions
String[] requestedPermissions = packageInfo.requestedPermissions;
if (requestedPermissions != null) {
for (int i = 0; i < requestedPermissions.length; i++) {
if (requestedPermissions[i]
.equals("android.permission.ACCESS_MOCK_LOCATION")
&& !applicationInfo.packageName.equals(context.getPackageName())) {
count++;
}
}
}
} catch (NameNotFoundException e) {
Log.e("Got exception " , e.getMessage());
}
}
if (count > 0)
return true;
return false;
}
上記の 1 番目と 2 番目の方法の両方が当てはまる場合、場所が偽装または偽造されている可能性が高くなります。
これで、Location Manager の API を使用してスプーフィングを回避できます。
両方のプロバイダー (ネットワークと GPS) から位置情報の更新を要求する前に、テスト プロバイダーを削除できます。
LocationManager lm = (LocationManager) getSystemService(LOCATION_SERVICE);
try {
Log.d(TAG ,"Removing Test providers")
lm.removeTestProvider(LocationManager.GPS_PROVIDER);
} catch (IllegalArgumentException error) {
Log.d(TAG,"Got exception in removing test provider");
}
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000, 0, locationListener);
removeTestProvider(~) が Jelly Bean 以降のバージョンで非常にうまく機能することを確認しました。この API は、Ice Cream Sandwich まで信頼性がないように見えました。
Flutter Update: Geolocatorを
使用してPosition
オブジェクトのisMocked
プロパティを確認します。
これを行う唯一の方法は、MockLocations を防止する Location Spoofing を防ぐことのようです。欠点は、より良い信号を得るために Bluetooth GPS デバイスを使用しているユーザーがいるということです。それらのユーザーは、モックの場所を使用する必要があるため、アプリを使用できません。
これを行うには、次のことを行いました。
// returns true if mock location enabled, false if not enabled.
if (Settings.Secure.getString(getContentResolver(),
Settings.Secure.ALLOW_MOCK_LOCATION).equals("0"))
return false;
else return true;
数年後にこのスレッドに出くわしました。2016 年には、ほとんどの Android デバイスの API レベルが 18 以上になるため、 Fernandoが指摘したようにLocation.isFromMockProvider()に依存する必要があります。
さまざまな Android デバイスやディストリビューションで、偽の場所やモックの場所を広範囲に実験しました。残念ながら、.isFromMockProvider()は 100% 信頼できるわけではありません。時々、偽の場所が mock としてラベル付けされません。これは、Google Location API の誤った内部融合ロジックが原因のようです。
詳細を知りたい場合は、これについて詳細なブログ投稿を書きました。要約すると、Location API から位置情報の更新をサブスクライブし、偽の GPS アプリをオンにして、各Location.toString()の結果をコンソールに出力すると、次のように表示されます。
位置更新のストリームで、1 つの位置が他の位置と同じ座標を持っているのに、モックとしてフラグが立てられておらず、位置の精度がはるかに低いことに注目してください。
この問題を解決するために、すべての最新の Android バージョン (API レベル 15 以降) でモックの場所を確実に抑制するユーティリティ クラスを作成しました。
LocationAssistant - Android で簡単に位置情報を更新
基本的には、最後の既知のモック ロケーションから 1 km 以内にある非モック ロケーションを「不信」にし、それらをモックとしてラベル付けします。これは、かなりの数の非モック ロケーションが到着するまで行われます。LocationAssistantは、偽の場所を拒否できるだけでなく、場所の更新を設定して購読する手間のほとんどから解放されます。
実際の位置情報の更新のみを受け取る (つまり、モックを抑制する) には、次のように使用します。
public class MyActivity extends Activity implements LocationAssistant.Listener {
private LocationAssistant assistant;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
// You can specify a different accuracy and interval here.
// The last parameter (allowMockLocations) must be 'false' to suppress mock locations.
assistant = new LocationAssistant(this, this, LocationAssistant.Accuracy.HIGH, 5000, false);
}
@Override
protected void onResume() {
super.onResume();
assistant.start();
}
@Override
protected void onPause() {
assistant.stop();
super.onPause();
}
@Override
public void onNewLocationAvailable(Location location) {
// No mock locations arriving here
}
...
}
onNewLocationAvailable()
現在、実際の位置情報でのみ呼び出されます。実装する必要があるリスナー メソッドが他にもいくつかありますが、質問のコンテキスト (GPS スプーフィングを防ぐ方法) では、基本的にはこれだけです。
もちろん、ルート化された OS では、通常のアプリでは検出できない位置情報を偽装する方法を見つけることができます。
セルタワーの一般的な場所を知っている場合は、現在のセルタワーが指定された場所と一致するかどうかを確認できます(10マイル以上などの大きな誤差範囲内)。
たとえば、ユーザーが特定の場所(たとえば店舗)にいる場合にのみアプリが機能のロックを解除する場合は、GPSとセルタワーを確認できます。現在、gpsスプーフィングアプリもセルタワーをスプーフィングしていないため、全国の誰かがあなたの特別な機能にスプーフィングしようとしているのかどうかを確認できます(たとえば、Disney Mobile Magicアプリを考えています)。
これは、Llamaアプリがデフォルトで場所を管理する方法です。これは、セルタワーIDのチェックがgpsよりもはるかにバッテリー消費量が少ないためです。非常に特定の場所には役立ちませんが、自宅と職場が数マイル離れている場合は、2つの一般的な場所を非常に簡単に区別できます。
もちろん、これにはユーザーがセル信号を持っている必要があります。また、そのエリアのすべてのセルタワーID(すべてのネットワークプロバイダー)を知っている必要があります。そうしないと、フォールスネガティブのリスクがあります。
Google Maps Geolocation APIを使用して、携帯電話基地局の三角測量または Wifi アクセス ポイントの情報に基づいて追加のチェックを追加できます
CellTowers に関する情報を取得する最も簡単な方法
final TelephonyManager telephonyManager = (TelephonyManager) appContext.getSystemService(Context.TELEPHONY_SERVICE);
String networkOperator = telephonyManager.getNetworkOperator();
int mcc = Integer.parseInt(networkOperator.substring(0, 3));
int mnc = Integer.parseInt(networkOperator.substring(3));
String operatorName = telephonyManager.getNetworkOperatorName();
final GsmCellLocation cellLocation = (GsmCellLocation) telephonyManager.getCellLocation();
int cid = cellLocation.getCid();
int lac = cellLocation.getLac();
結果をサイトと比較できます
Wifi アクセス ポイントに関する情報を取得するには
final WifiManager mWifiManager = (WifiManager) appContext.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
if (mWifiManager != null && mWifiManager.getWifiState() == WifiManager.WIFI_STATE_ENABLED) {
// register WiFi scan results receiver
IntentFilter filter = new IntentFilter();
filter.addAction(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION);
BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
List<ScanResult> results = mWifiManager.getScanResults();//<-result list
}
};
appContext.registerReceiver(broadcastReceiver, filter);
// start WiFi Scan
mWifiManager.startScan();
}