VPN észlelése Androidon: miért félrevezető a tun0 és a ping
Az interfész neve konvenció; a ping azt bizonyítja, hogy egy hoszt elérhető volt. Kérdezze meg a NetworkCapabilities-t, mit deklarál egy hálózat, majd reagáljon a visszahívásokra.
Az az ellenőrzés, amelyet a legtöbb Android-alkalmazás VPN észlelésére használ, egy interfésznév, és soha nem volt VPN-ellenőrzés. Azt jelenti, hogy létezik valamilyen interfész a fejlesztő által várt karakterlánccal, ami az egyik eszközön OpenVPN-t jelent, a másikon valami teljesen függetlent. A ping ugyanolyan bizonyíték: azt bizonyítja, hogy egy célpont válaszolt, nem azt, hogy egy alagút szállította a csomagot. Amit a platform valójában kínál, az hálózatok halmaza és az egyes hálózatok által deklarált transzportok, és ez más kérdés más válasszal.
Tévhit: ha létezik tun0, akkor a VPN fut
Az Android VPN-alkalmazások VpnService-implementációk, amelyek tun fájlleírót adnak át a rendszernek. Az interfész neve attól származik, aki létrehozta: az OpenVPN általában tun0-t hoz létre, a PPTP és az L2TP ppp0-t hoz létre, a WireGuard az alagútról nevezi el az interfészét, és az IPsec-alagútnak egyáltalán nem is lehet hagyományos neve. Ezért a /proc/net/dev olvasása vagy a NetworkInterface-objektumok felsorolása konvenciót keres, nem tényt.
Mindkét hibairány él. Egy eszköznek lehet tun0-ja olyan helyi alagúthoz, amelynek semmi köze a felhasználó VPN-jéhez, és egy teljesen egészséges WireGuard-alagút láthatatlan lehet egy olyan ellenőrzés számára, amely csak a tun0 karakterláncot ismeri. Az ugyanabban a függvényben jelentkező téves pozitív és téves negatív eredmény használhatatlanná teszi diagnosztikaként, mert egy „nem észlelhető VPN” szövegű támogatási jegy egyik irányban sem megbízható.
A hozzáférés is szűkült. Az újabb Android-verziók korlátozzák, mit olvashat egy közönséges alkalmazás a /proc-ból, így egy Android 8-on működő névalapú ellenőrzés későbbi eszközön üres listát adhat vissza kódmódosítás nélkül. Amikor az ellenőrzés csendesen a nyitott irányba hibázik, az alkalmazás arra jut, hogy nincs VPN azon a hardveren, ahol az alagút rendben van.
Tévhit: a sikeres ping azt jelenti, hogy az alagút forgalmat szállít
A belső hosztra küldött ping akkor sikerül, ha létezik útvonal ahhoz a hoszthoz. Osztott alagút esetén a hoszt elérhető lehet Wi-Fi-n, miközben az alagút fut és egészen mást csinál. Alkalmazásonkénti VPN esetén a pingelő alkalmazás kizárható az alagútból, így az ICMP-visszhang kívülre kerül és visszatér, ami az ellenkezőjét bizonyítja annak, amit az ellenőrzés feltételez.
Az ICMP és a TCP is külön szűrődik. Egy alagút, amely engedélyezi a visszhangkéréseket, de eldobja a szolgáltatás által igényelt TCP-portot, átmegy a ping-teszten és elbukik az alkalmazásban, és ez a túloldali tűzfal-szabály, nem az eszköz tulajdonsága. A sikeres ping egyetlen őszinte olvasata az, hogy ez a célpont ebben a pillanatban válaszolt erre a protokollra.
Mit jelent valójában a platform
Az API 21-től a ConnectivityManager.getAllNetworks() visszaadja az összes hálózatot, amelyet a rendszer ismer, a getNetworkCapabilities(network) pedig jelzi, mit deklarál az a hálózat. Egy VPN-hálózat TRANSPORT_VPN-t hordoz. Az összes hálózat bejárása egy kérdésre válaszol: fut-e jelenleg bármilyen VPN ezen az eszközön?
Egy második, szűkebb kérdéshez más hívás kell. A getActiveNetwork() visszaadja azt a hálózatot, amelyet az alkalmazás forgalma most használna, és ugyanazon képesség ellenőrzése rajta azt válaszolja: átmenne-e a forgalmam egy VPN-en? A kettő eltérhet, és az eltérés tanulságos. Egy futó, de nem aktuális VPN alkalmazásonkénti útvonalakra vagy osztott alagút konfigurációjára utal, nem hiányzó alagútra.
Network active = cm.getActiveNetwork();
NetworkCapabilities caps = cm.getNetworkCapabilities(active);
boolean viaVpn = caps != null
&& caps.hasTransport(NetworkCapabilities.TRANSPORT_VPN);
Védje mindkét hívást. A képességek null értékűek lehetnek egy olyan hálózatnál, amely eltűnt a felsorolás és a lekérdezés között, és az aktív hálózat null lehet átadás közben. A NET_CAPABILITY_NOT_VPN a másik irányban hasznos: annak megerősítésére, hogy egy hálózat biztosan nem alagút, nem csupán arra, hogy hiányzik róla a VPN-transzport jelzője.
A NetworkInfo ugyanaz a tévhit volt API-val
A ConnectivityManager.TYPE_VPN a NetworkInfo-val ennek az ellenőrzésnek a második generációja, és okkal elavult. A modern verziókban a VPN-hez visszaadott objektum null lehet, miközben az alagút csatlakoztatva van, így a null-t „nincs VPN”-ként kezelő kód csendesen téves negatívot jelent azokon az eszközökön, ahol a válasz a legfontosabb.
A kísérő adás sem jobb. A CONNECTIVITY_CHANGE dokumentált története szerint kihagyja a VPN-átmeneteket, beleértve azokat a build-eket, amelyekben csak VPN csatlakozásakor vagy leválásakor sült el, nem minden hálózatváltozáskor. A rá épített figyelő olyan állapotváltozót állít elő, amely a platform nézetétől eltávolodik a következő teljes újracsatlakozásig.
Reagáljon visszahívásokra a lekérdezés helyett
Regisztráljon egy NetworkRequestet, amely TRANSPORT_VPN-t követel és eltávolítja a NET_CAPABILITY_NOT_VPN-t, majd hívja meg a registerNetworkCallbacket ezzel a kéréssel. Ekkor az onAvailable és az onLost jelzi, mikor változik a deklaráció, és ez az az esemény, amely valóban érdekli. Szüntesse meg a visszahívás regisztrációját, amikor a komponens leáll, különben az alkalmazás hivatkozást tart egy halott képernyőre, és a visszahívás tovább sül el.
NetworkRequest req = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_VPN)
.removeCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
.build();
cm.registerNetworkCallback(req, callback);
Ehhez ACCESS_NETWORK_STATE kell, ami normál engedély: nincs root, nincs felhasználói hozzájárulási párbeszéd, nincs VPN-engedély. Az időzítőn történő lekérdezés az az alternatíva, amelyre az emberek visszaesnek, és meríti az akkumulátort, miközben továbbra is kihagy egy intervallumnál rövidebb átmenetet, ami pontosan az az átmenet, amelyről a felhasználó beszámol.
Mit nem tud még mindig megmondani a válasz
Egyik API sem jelenti, hogy az alagút egészséges-e, melyik szerveren végződik, vagy hogy egy adott alkalmazás forgalma benne van-e. Egy VPN-hálózat létezhet használható útvonalak nélkül, és egy alkalmazásonkénti alagút kizárhatja a kérdést feltevő alkalmazást.
Ezért rögzítse, mit látott az ellenőrzés, ne csak a logikai értéket. Naplózza a hálózatazonosítót, a deklarált transzportokat és azt, hogy az a hálózat aktív volt-e. Egy „nem észlelhető VPN” szövegű jelentés használhatatlan, míg az, amely szerint egy hálózat TRANSPORT_VPN-t deklarált, de a getActiveNetwork() Wi-Fi-t adott vissza, közvetlenül útvonalakra vagy kizárási listára mutat, és ez olyan javítás, amelyen valaki dolgozhat.