Die Prüfung, die die meisten Android-Apps zur Erkennung eines VPN verwenden, ist ein Schnittstellenname, und sie war nie eine VPN-Prüfung. Sie meldet, dass irgendeine Schnittstelle mit einer Zeichenkette existiert, die der Entwickler erwartet hat, was auf einem Gerät OpenVPN bedeutet und auf einem anderen etwas völlig Unabhängiges. Ein Ping ist dieselbe Art von Beweis: Er beweist, dass ein Ziel geantwortet hat, nicht dass ein Tunnel das Paket befördert hat. Was die Plattform tatsächlich bereitstellt, ist eine Menge von Netzen und die Transports, die jedes davon deklariert, und das ist eine andere Frage mit einer anderen Antwort.

Mythos: Wenn tun0 existiert, läuft ein VPN

Android-VPN-Apps sind VpnService-Implementierungen, die dem System einen tun-Dateideskriptor übergeben. Der Schnittstellenname stammt von dem, der ihn erzeugt hat: OpenVPN erzeugt üblicherweise tun0, PPTP und L2TP erzeugen ppp0, WireGuard benennt seine Schnittstelle nach dem Tunnel, und ein IPsec-Tunnel zeigt möglicherweise gar keinen konventionellen Namen. Das Lesen von /proc/net/dev oder das Aufzählen von NetworkInterface-Objekten sucht daher nach einer Konvention, nicht nach einer Tatsache.

Beide Fehlerrichtungen sind lebendig. Ein Gerät kann tun0 für einen lokalen Tunnel haben, der nichts mit dem VPN des Nutzers zu tun hat, und ein völlig gesunder WireGuard-Tunnel kann für eine Prüfung unsichtbar sein, die nur die Zeichenkette tun0 kennt. Falsch positive und falsch negative Ergebnisse in derselben Funktion machen sie als Diagnose unbrauchbar, denn einem Support-Ticket, das „kein VPN erkannt“ sagt, ist in keine Richtung zu trauen.

Auch der Zugriff ist enger geworden. Neuere Android-Versionen beschränken, was eine gewöhnliche App aus /proc lesen darf, sodass eine namensbasierte Prüfung, die unter Android 8 funktionierte, auf einem späteren Gerät ohne jede Codeänderung eine leere Liste zurückgeben kann. Wenn die Prüfung stillschweigend offen fehlschlägt, schließt die App, dass es kein VPN gibt, auf Hardware, bei der der Tunnel in Ordnung ist.

Mythos: Ein erfolgreicher Ping bedeutet, dass der Tunnel Verkehr trägt

Ein Ping zu einem internen Host gelingt, wann immer eine Route zu diesem Host existiert. Bei Split-Tunnelling kann der Host über Wi-Fi erreichbar sein, während der Tunnel läuft und etwas völlig anderes tut. Bei einem Per-App-VPN kann die App, die den Ping ausführt, vom Tunnel ausgeschlossen sein, sodass das ICMP-Echo außerhalb davon hinausgeht und zurückkommt, was das Gegenteil dessen beweist, was die Prüfung annimmt.

ICMP und TCP werden auch getrennt gefiltert. Ein Tunnel, der Echo-Anfragen erlaubt, aber den TCP-Port verwirft, den ein Dienst braucht, besteht den Ping-Test und scheitert in der Anwendung, und das ist eine Firewall-Regel auf der Gegenseite und keine Eigenschaft des Geräts. Die einzige ehrliche Lesart eines erfolgreichen Pings ist, dass dieses Ziel dieses Protokoll in diesem Moment beantwortet hat.

Was die Plattform tatsächlich meldet

Ab API 21 gibt ConnectivityManager.getAllNetworks() jedes Netz zurück, das das System kennt, und getNetworkCapabilities(network) meldet, was dieses Netz deklariert. Ein VPN-Netz trägt TRANSPORT_VPN. Eine Schleife über alle Netze beantwortet eine Frage: Ist auf diesem Gerät gerade irgendein VPN aktiv?

Eine zweite, engere Frage braucht einen anderen Aufruf. getActiveNetwork() gibt das Netz zurück, das der Verkehr der App gerade nutzen würde, und die Prüfung derselben Fähigkeit darauf beantwortet: würde mein Verkehr über ein VPN gehen? Beide können sich widersprechen, und der Widerspruch ist aufschlussreich. Ein VPN, das läuft, aber nicht aktiv ist, deutet auf Per-App-Routing oder eine Split-Tunnel-Konfiguration hin, nicht auf einen fehlenden Tunnel.

Network active = cm.getActiveNetwork();
NetworkCapabilities caps = cm.getNetworkCapabilities(active);
boolean viaVpn = caps != null
    && caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN);

Sichern Sie beide Aufrufe ab. Fähigkeiten können null sein für ein Netz, das zwischen Auflistung und Abfrage verschwunden ist, und das aktive Netz kann während einer Übergabe null sein. NET_CAPABILITY_NOT_VPN ist in der anderen Richtung nützlich, um zu bestätigen, dass ein Netz definitiv kein Tunnel ist, statt dass ihm nur das VPN-Transportflag fehlt.

NetworkInfo war derselbe Mythos mit einer API

ConnectivityManager.TYPE_VPN mit NetworkInfo ist die zweite Generation dieser Prüfung, und sie ist aus gutem Grund veraltet. In modernen Versionen kann das für ein VPN zurückgegebene Objekt null sein, während der Tunnel verbunden ist, sodass Code, der null als „kein VPN“ behandelt, stillschweigend ein falsch negatives Ergebnis auf den Geräten meldet, auf denen die Antwort am wichtigsten ist.

Auch der Broadcast, der sie üblicherweise begleitet, ist nicht besser. CONNECTIVITY_CHANGE hat eine dokumentierte Geschichte verpasster VPN-Übergänge, einschließlich Builds, in denen er nur ausgelöst wurde, wenn ein VPN verbunden oder getrennt wurde, statt bei jeder Netzänderung. Ein darauf aufbauender Listener erzeugt eine Zustandsvariable, die von der Sicht der Plattform abdriftet, bis zur nächsten vollständigen Neuverbindung.

Reagieren Sie auf Callbacks statt auf Polling

Registrieren Sie eine NetworkRequest, die TRANSPORT_VPN verlangt und NET_CAPABILITY_NOT_VPN entfernt, und rufen Sie dann registerNetworkCallback mit dieser Anforderung auf. onAvailable und onLost sagen Ihnen dann, wann sich die Deklaration ändert, und das ist das Ereignis, das Sie tatsächlich interessiert. Heben Sie die Registrierung des Callbacks auf, wenn die Komponente stoppt, sonst hält die App eine Referenz auf einen toten Bildschirm und der Callback feuert weiter.

NetworkRequest req = new NetworkRequest.Builder()
    .addTransportType(NetworkCapabilities.TRANSPORT_VPN)
    .removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
    .build();
cm.registerNetworkCallback(req, callback);

Das erfordert ACCESS_NETWORK_STATE, eine normale Berechtigung: kein Root, kein Dialog zur Nutzereinwilligung, keine VPN-Berechtigung. Polling per Timer ist die Alternative, zu der Leute zurückgreifen, und es verbraucht Akku, während es einen Übergang, der kürzer als das Intervall ist, trotzdem verpasst, und genau das ist der Übergang, den ein Nutzer meldet.

Was die Antwort Ihnen immer noch nicht sagen kann

Keine dieser APIs meldet, ob der Tunnel gesund ist, an welchem Server er endet oder ob der Verkehr einer bestimmten App darin liegt. Ein VPN-Netz kann ohne nutzbare Routen existieren, und ein Per-App-Tunnel kann die App ausschließen, die die Frage stellt.

Halten Sie also fest, was die Prüfung gesehen hat, nicht nur das Boolean. Protokollieren Sie die Netzwerkkennung, die deklarierten Transports und ob dieses Netz aktiv war. Ein Bericht, der „kein VPN erkannt“ sagt, ist unbrauchbar, während einer, der sagt, ein Netz habe TRANSPORT_VPN deklariert, aber getActiveNetwork() habe Wi-Fi zurückgegeben, direkt auf Routing oder auf eine Ausschlussliste zeigt, und das ist eine Korrektur, mit der jemand arbeiten kann.

Ähnliche Artikel