Twee heel verschillende dingen heten het blokkeren van platte tekst op Android. Het ene is een beleid binnen een app dat de eigen netwerkstack opdraagt gewone HTTP te weigeren. Het andere is een regeling op apparaatniveau die alles op de telefoon in één keer probeert te beschermen, meestal door verkeer via een VPN te leiden. Ze lossen verschillende problemen op, en het tweede kan het eerste niet repareren: een tunnel beschermt het stuk tussen de telefoon en de uitgang, terwijl een app die HTTP spreekt nog steeds HTTP spreekt met alles achter die uitgang.

Twee problemen onder één naam

Het eerste probleem is de payload: de app schrijft inloggegevens of tokens in een verzoek dat niet end-to-end versleuteld is, dus elke hop die het kan zien, kan het lezen. Het tweede is het pad: de app kan volledig in orde zijn, en je wilt nog steeds dat de lokale Wi-Fi-operator niets leert over waar hij verbinding mee maakt. Een VPN pakt het tweede aan en alleen het tweede.

Dat onderscheid bepaalt wie de oplossing bezit. De payload is van degene die de app levert, want alleen die code bepaalt welke URL wordt aangeroepen en of een certificaat wordt gevalideerd. Het pad is van het apparaat of het netwerk, want het gaat erom waar pakketten reizen, niet wat ze bevatten.

Pad één: de app die je beheert

Als je de app levert, is de oplossing een transportbeslissing plus een beleid dat die afdwingt. Richt elk eindpunt op HTTPS, laat validatie van hostnaam en certificaat aan staan, en verwijder de terugval naar gewone HTTP in plaats van hem één configuratievlag verderop te laten liggen. Een omleiding helpt hier niet: een HTTP-verzoek dat een 301 krijgt, heeft zijn headers en body al openlijk verzonden, dus inloggegevens mogen nooit reizen in de veronderstelling dat de omleiding ze beschermt.

Vanaf Android 7.0 drukt Network Security Configuration het beleid uit. Het bestand hieronder is de kleinste bruikbare versie: het weigert overal platte tekst, tenzij een domeinspecifieke uitzondering iets anders zegt.

<network-security-config>
    <base-config cleartextTrafficPermitted="false" />
</network-security-config>

Verwijs vanuit het manifest naar die resource en lees dan de uitzonderingen die al bestaan voordat je levert. Een oud debug-domein of een SDK-vermelding van een betaalprovider kan stilzwijgend platte tekst weer toestaan voor de host die je probeerde te beschermen.

Twee versiedetails zijn de moeite waard. Apps die op API 28 of hoger richten, weigeren standaard al platte tekst, dus een app die nog gewone HTTP spreekt, richt zich op een ouder API-niveau of heeft ergens een uitzondering. En als er een netwerkbeveiligingsconfiguratie is, beslist het oudere manifestattribuut android:usesCleartextTraffic de vraag niet meer, waardoor het wijzigen van die vlag soms niets lijkt te doen.

Pad twee: de app die je niet beheert

Voor een geïnstalleerde app van derden kun je de beveiligingsconfiguratie niet herschrijven met een gebruikersinstelling. De realistische opties zijn de eigen beveiligde-verbindingsinstellingen van het product, een onderhouden alternatief dat al TLS gebruikt, of een beleid voor beheerde apparaten dat beperkt wat het apparaat mag uitvoeren. Dat zijn verschillende toezeggingen, en alleen de derde schaalt naar een vloot.

Netwerkregels zijn zwakkere vervangers dan ze lijken. Alleen poort 443 toestaan dwingt geen geauthenticeerd TLS af, omdat veel protocollen met platte tekst naar die poort kunnen worden verplaatst, en poort 80 blokkeren dekt geen platte tekst op andere poorten. De modus alleen-HTTPS van een browser is ook beperkt tot die browser, niet tot een aparte mail- of chat-app op dezelfde telefoon.

Een onderscheppingscertificaat installeren om verkeer versleuteld te laten lijken is een vertrouwenswijziging, geen oplossing. Het vervangt de serverauthenticatie van de app door de jouwe, en een app die certificaten vanaf het begin niet valideerde, wordt daarmee niet gerepareerd; je hebt alleen verplaatst wie de stroom kan lezen.

Waar een VPN helpt en waar niet

Een tunnel is het juiste middel als de eis is dat het lokale netwerk niets over de verbinding leert, of dat de app een privédienst bereikt die hij anders niet bereikt. Het is het verkeerde middel om een HTTP-client HTTPS te laten spreken, omdat het verzoek de tunnel precies zo verlaat als het erin ging.

Android voegt een praktische beperking toe: het platform draait maar één VPN-dienst tegelijk. Een lokale filter-app die die plek inneemt, concurreert met de tunnel op afstand die je echt nodig hebt, en tussen beide wisselen is een handeling op apparaatniveau in plaats van appniveau. Die ene plek is een goede reden om het payloadbeleid in de app te houden en de tunnel voor het pad te bewaren.

Het enige geval waarin beide overlappen is een privéserver die je zelf ook beheert. Daar kan een tunnel het pad privé maken, ook al is het protocol nog platte tekst, wat de blootstelling vermindert maar niet verandert wat de payload is. Behandel dat als risicoreductie, niet als versleuteling van de gegevens.

Wat je moet testen voordat je het geblokkeerd noemt

Begin met de standaard die mensen verrast: een app die op API 28 of hoger richt, zou platte tekst al moeten weigeren, dus controleer het doelniveau voordat je configuratie toevoegt die een standaard dupliceert. Test dan bewust het faalgeval: een verzoek naar een gewone HTTP-host moet mislukken en niet stilzwijgend slagen via een terugval.

adb shell dumpsys package com.example.app | grep -E 'targetSdk|versionName'
adb logcat | grep -i cleartext

Bevestig voor het succesgeval dat de HTTPS-aanroep een echte certificaatketen valideert en dat een proxy met een niet-vertrouwd certificaat wordt geweigerd in plaats van getolereerd. Een handshake die tegen welk certificaat dan ook slaagt, heeft niets bewezen over transportbeveiliging.

Noteer voor een app van derden de grens die je echt kunt afdwingen, of dat nu een beleid voor beheerde apparaten, een netwerkregel of niets is, en zeg dat in de acceptatiecriteria. De Android-gids voor beveiligingsconfiguratie noemt de standaarden van het platform en welke componenten het beleid respecteren, en dat is het deel dat bepaalt of je configuratie überhaupt iets doet.

Gerelateerde artikelen