Android-VPN uten root: hvem eier samtykket, rutene og blokkeringen
Samtykke, TUN-grensesnittet og systemets blokkeringspolicy er tre forskjellige tillatelser. Spor hvilken komponent som eier hver av dem, før du vurderer en Android-VPN-app.
Det er ikke root som gjør at en Android-VPN-app kan føre trafikk, blokkere lekkasjer eller overleve en omstart. Folk flest omtaler alt dette som „appen har VPN-tillatelse“, og da forsvinner tre atskilte mekanismer: samtykket brukeren gir, pakkegrensesnittet systemet setter opp, og en blokkeringspolicy plattformen eier og appen ikke definerer. Å vurdere et Android-VPN-design handler om å avklare hvilken av disse tre en påstand egentlig bygger på, siden hver av dem har ulik eier og ulikt feilmønster.
Samtykket hentes inn via en systemaktivitet
VpnService.prepare() tildeler ingen rettigheter. Mangler samtykket, returneres en intent til systemets samtykkeaktivitet, og appen viser den dialogen slik at brukeren kan godkjenne tjenesten. Verken å starte appens egen prosess, føye en tillatelse til manifestet eller vise en hjemmelaget „slå på VPN“-skjerm gir slik tilgang.
Tillatelsen har også et avgrenset omfang. Den knytter seg til en pakke i én brukerprofil, så privat kopi og jobbkopi er to ulike vedtak, og en enhetseier kan klargjøre en administrert profil på en annen måte enn en privat. Dukker aldri dialogen opp, og prepare() gir null, er samtykket allerede gitt for pakken i profilen; det sier ingenting om appens troverdighet, bare at tillatelsen foreligger.
Grensesnittet er en bestilling systemet utfører
Når samtykket er gitt, beskriver appen ønskene sine gjennom VpnService.Builder: adresser, ruter, DNS-tjenere, MTU og, der det støttes, hvilke apper som skal tas med eller holdes utenfor. establish() ber systemet lage tunnelgrensesnittet og sende den aktuelle trafikken dit. Rutevalget ligger hos systemet; appen får fildeskriptoren og må selv gjøre noe fornuftig med pakkene.
| What you observe | Who actually owns it |
|---|---|
| VPN consent dialog | The system, triggered by prepare() |
| Routes, DNS and app lists | The app requests them; the system applies them |
| One active VPN per profile | The platform; a new service replaces the old one |
| Always-on and block-without-VPN | Device policy enforced by the platform |
Tabellen samler arkitekturen på ett sted. Appen er med på pakkebehandlingen, men eier verken grensesnittet, rutetabellen eller hvor mange tjenester som kan kjøre.
Tunnelsocketen må komme seg ut av sin egen tunnel
Selve forbindelsen som bærer den krypterte trafikken, er en socket. Sendes den tilbake inn i grensesnittet appen nettopp laget, blir tunnelen avhengig av tunnelen og stopper opp. VpnService.protect() knytter socketen til underliggende nett slik at den går utenom innfangingen, og derfor beskytter en riktig klient den ytre socketen før første håndtrykk.
adb shell dumpsys connectivity | grep -i -A5 vpn
adb shell settings get global always_on_vpn_app
adb shell settings get global always_on_vpn_lockdown
Kommandoene over viser det installerte VPN-nettet, pakken for alltid på og om låsing er slått på. Dette er ren lesing: stemmer ikke verdiene med det designet påstår, gjelder påstanden appens grensesnitt og ikke systemtilstanden.
Blokkeringen kan ligge hos plattformen
Android beskriver en policy som blokkerer tilkoblinger uten VPN. Håndhevingen er en plattformkontrakt, ikke en app som prøver tilkoblingen om igjen i en løkke. Bryteren finnes i enhetens VPN-innstillinger som Blokker tilkoblinger uten VPN, og på en administrert enhet kan eieren slå den på for hele profilen. Forskjellen betyr mye når du vurderer en markedsføringspåstand: en klient kan vise hengelåsikon og koble til på nytt så ofte den vil, mens direkte trafikk fortsatt er mulig, eller dekkede apper kan holde seg frakoblet fordi systemet håndhever låsen.
Hengelåsikonet tegner appen selv, så det viser bare at appen tror den er tilkoblet. Om en annen app mister nettilgangen, avgjør plattformen, så den nyttige testen er å se den appen feile mens tunnelen er oppe.
En „alltid på“-etikett fra appbutikken erstatter heller ikke kontrakten. Alltid på og låsing er innstillinger på enheten, ofte satt av en eier, og plattformen bestemmer hvordan de virker for utelatte apper. Les innstillingen, ikke merket.
Fire spørsmål til en designgjennomgang
Først: hvem ga samtykket, for hvilken pakke og hvilken profil, og var det brukeren eller en policy for administrerte enheter? Deretter: hva ba appen om, hvilke ruter, hvilke adressefamilier og hvilke apper som ble tatt med eller utelatt? Så: hva skjer når tunnelen ikke er tilgjengelig, blokkerer systemet eller faller trafikken tilbake til underliggende nett? Til slutt: hva gjør appen med pakkene den mottar?
Det siste spørsmålet er det tillatelsesmodellen ikke kan besvare. Samtykke og et TUN-grensesnitt beviser at appen kan motta trafikk, men ikke at den krypterer den, autentiserer motparten eller gjenoppretter seg på en ryddig måte. Dette er egenskaper ved implementasjonen, og det er her en ærlig gjennomgang legger arbeidet sitt.
En unødvendig root-forespørsel blir ikke berettiget av at resultatet kalles en kill switch, og en samtykkedialog er ikke en sikkerhetsrevisjon. Hold de tre tillatelsene fra hverandre, så blir hver påstand etterprøvbar.