PlainApp Security Deep Dive

Hoe PlainApp je gegevens beschermt op het lokale Wi-Fi-netwerk — met bewijs uit de open-source code.

Waarom is beveiliging op het lokale netwerk belangrijk?

Wanneer je een app gebruikt om je Android-telefoon vanaf een PC-browser te beheren, reizen je sms-berichten, foto's, contacten en bestanden over je Wi-Fi-netwerk. De meeste apps verzenden dit alles als leesbare platte tekst. Iedereen op hetzelfde netwerk kan het meelezen.

Je denkt misschien: "Het is mijn eigen Wi-Fi thuis, wie zou er meekijken?" Maar denk eens aan:

  • Openbaar Wi-Fi — Cafés, luchthavens, hotels — iedereen in de buurt kan verkeer onderscheppen
  • Gedeelde netwerken — Huisgenoten, gasten of gehackte IoT-apparaten
  • Kwetsbaarheden in de router — Verouderde firmware met bekende beveiligingslekken

Als een app je sms-berichten als platte tekst via HTTP verstuurt, kan iedereen met een gratis tool als Wireshark ze meelezen. Dit is geen theorie — het is kinderlijk eenvoudig.

De meeste apps voor telefoonbeheer doen precies dit. Open Chrome DevTools (F12 → tabblad Network) terwijl je hun webinterface gebruikt, en je kunt elke API-aanroep in platte JSON lezen — sms-inhoud, bestandspaden, namen van contacten, alles.

Wat PlainApp anders doet

PlainApp past meerdere lagen van encryptie en authenticatie toe. Hier volgt wat elke laag doet, in eenvoudige taal.

Security Architecture Comparison
1

GraphQL API — Vraag alleen op wat je nodig hebt

De meeste apps gebruiken ouderwetse REST API's: één URL per bron, die alles teruggeeft of je het nu nodig hebt of niet. PlainApp gebruikt GraphQL — een moderne aanpak waarbij de browser precies de benodigde gegevens opvraagt, niets meer.

Waarom dit belangrijk is:

  • Minder gegevens blootgesteld — Als je alleen een bestandsnaam nodig hebt, is dat het enige dat wordt verzonden
  • Eén endpoint — Er is één /graphql-URL in plaats van tientallen, wat het aanvalsoppervlak verkleint
  • Rechten per query — Elke query controleert zijn eigen rechten (bijv. het lezen van sms vereist READ_SMS-rechten, ook nadat je bent ingelogd)
2

Versleutelde API-aanroepen (XChaCha20-Poly1305)

Dit is het grootste verschil tussen PlainApp en de meeste concurrenten.

Elk API-verzoek en -antwoord — inclusief HTTP- en WebSocket-verkeer — wordt versleuteld met XChaCha20-Poly1305, in zowel HTTP- als HTTPS-modus. Dit is een modern AEAD-versleutelingsalgoritme dat wordt gebruikt door WireGuard VPN, Cloudflare en Googles Tink-bibliotheek.

In eenvoudige bewoordingen:

  1. 1Je browser bouwt een verzoek op (bijv. "toon mijn SMS")
  2. 2Het voegt een tijdstempel en willekeurige nonce toe voor bescherming tegen replay-aanvallen
  3. 3Het versleutelt de volledige payload tot onleesbare binaire data met XChaCha20-Poly1305
  4. 4De versleutelde binaire data wordt over het netwerk verzonden — geen base64, geen JSON, puur binair
  5. 5Alleen je telefoon kan het ontsleutelen en valideren

Zelfs als iemand het verkeer onderschept, ziet diegene alleen betekenisloze binaire data — geen leesbare JSON.

Encrypted API Flow

Over de encryptie:

  • 256-bit sleutel (extreem sterk)
  • 192-bit nonce (voorkomt bepaalde aanvalstypen die andere ciphers treffen)
  • Ingebouwde manipulatiedetectie — als iemand de data onderweg wijzigt, mislukt de ontsleuteling
  • Android-kant: aangedreven door Google Tink
  • Webkant: aangedreven door @noble/ciphers (een grondig gecontroleerde JavaScript-cryptobibliotheek)
3

Digitale handtekeningen (Ed25519) — Apparaat-naar-apparaat

Wanneer twee PlainApp-apparaten met elkaar communiceren (bijv. telefoon-naar-telefoon chat), is versleuteling alleen niet voldoende — elk apparaat moet verifiëren dat het bericht echt van het andere apparaat komt en niet is vervalst.

tijdstempel

Het ontvangende apparaat verifieert de handtekening met de publieke sleutel van de afzender en weigert verzoeken met een tijdstempel ouder dan 5 minuten. Dit voorkomt zowel vervalsing als replay-aanvallen.

4

Veilige apparaatkoppeling (ECDH Key Exchange)

Wanneer twee PlainApp-apparaten met elkaar koppelen (bijv. twee telefoons), hebben ze een gedeelde encryptiesleutel nodig. Maar het versturen van een sleutel over het netwerk zou het doel ondermijnen.

PlainApp lost dit op met ECDH (Elliptic Curve Diffie-Hellman) key exchange — dezelfde wiskunde die wordt gebruikt in TLS, Apple's Secure Enclave en WebAuthn:

  1. 1Elk apparaat genereert een sleutelpaar (publiek + privé)
  2. 2Ze wisselen alleen hun publieke sleutels uit
  3. 3Elk apparaat combineert zijn eigen privésleutel met de publieke sleutel van het andere apparaat
  4. 4Beide komen uit op hetzelfde gedeelde geheim — zonder dat dit ooit over het netwerk gaat
ECDH Pairing Flow
5

Versleutelde URL's — Bestandspaden verbergen

Hier is een detail dat de meeste mensen niet opmerken: URL-paden onthullen ook informatie.

Wanneer je een bestand downloadt, vertelt de URL de server welk bestand je wilt. In veel apps ziet de URL er zo uit:

/fs?path=/sdcard/DCIM/Camera/photo_2024.jpg

Iedereen die verkeer afluistert (of in de browsergeschiedenis kijkt) kan precies zien welk bestand je hebt geopend en hoe de mappenstructuur van je telefoon eruitziet.

PlainApp versleutelt het bestandspad in de URL:

/fs?id=AxK9f2mQ7vR3xP8nWz...

De id-parameter is het bestandspad, versleuteld met XChaCha20-Poly1305 met een willekeurige sleutel genaamd URL Token. Standaard is deze sleutel permanent en blijft behouden na het herstarten van de app, zodat gedeelde links blijven werken. Zonder de sleutel kan niemand het echte pad raden, je bestanden opsommen of download-URL's samenstellen.

URL Encryption Comparison
6

HTTPS met zelfondertekende certificaten

PlainApp genereert een zelfondertekend TLS-certificaat op je telefoon. Dit creëert een versleutelde HTTPS-tunnel — dezelfde technologie die banken en websites gebruiken.

Het certificaat gebruikt:

  • ECDSA met P-256-curve (dezelfde als grote websites)
  • SHA256-handtekening
  • Opgeslagen in de privédirectory van de app

In combinatie met de applicatielaag XChaCha20-encryptie krijg je twee onafhankelijke lagen van versleuteling. Het kraken van de ene laag helpt niet zonder ook de andere te kraken.

7

Inlogbeveiliging

PlainApp beperkt inlogpogingen om het raden van wachtwoorden te voorkomen:

  • 5 pogingen per 60 seconden per IP-adres
  • Daarna worden verbindingen geweigerd
  • Verlopen rate-limit-records worden automatisch opgeruimd

HTTP vs HTTPS: een eerlijke toelichting

PlainApp biedt zowel HTTP- als HTTPS-modus. Dit is het eerlijke verschil:

HTTP-modusHTTPS-modus
Bestandspad-URL'sVersleuteld (XChaCha20)Versleuteld (XChaCha20)
API-aanroepenVersleuteld (XChaCha20)Versleuteld (XChaCha20 + TLS)
BestandsdownloadsNiet versleuteld tijdens transportVersleuteld door TLS
Voor wieBeginners (geen SSL-waarschuwing in de browser)Privacybewuste gebruikers

Beide modi versleutelen API-aanroepen met XChaCha20-Poly1305 — je sms-berichten, contacten en andere gegevens worden altijd versleuteld, ongeacht HTTP of HTTPS.

HTTP-modus bestaat omdat zelfondertekende certificaten een browserwaarschuwing veroorzaken ("Uw verbinding is niet privé"). Voor minder technische gebruikers is deze waarschuwing verwarrend en beangstigend. HTTP vermijdt deze drempel.

HTTP-modus versleutelt echter geen bestandsoverdrachten. Als je op een gedeeld of openbaar netwerk zit, gebruik dan altijd HTTPS.

Aanbeveling: gebruik HTTPS wanneer mogelijk. Accepteer de browserwaarschuwing voor het zelfondertekende certificaat — dit is normaal en te verwachten bij een app voor het lokale netwerk.

8

Supply Chain Security — Minder dependencies, minder risico

Een beveiligingsrisico waar veel gebruikers niet aan denken: supply chain attacks. Wanneer een app honderden bibliotheken van derden gebruikt, kan elke daarvan gecompromitteerd zijn — met kwaadaardige code die gegevens steelt, cryptocurrency minet of backdoors opent.

Dit is bijzonder gevaarlijk in het JavaScript-ecosysteem. In de afgelopen jaren werden populaire npm-pakketten zoals event-stream, ua-parser-js en colors gekaapt om kwaadaardige code in miljoenen projecten te injecteren. Als de webinterface van een app voor telefoonbeheer honderden JS-pakketten binnenhaalt, is elk daarvan een potentieel aanvalspunt.

PlainApp kiest bewust voor een andere aanpak: gebruik zo weinig mogelijk dependencies en bouw maatwerk wanneer dat zinvol is.

Android-app (plain-app)

  • Geen dependency injection framework (geen Hilt, geen Dagger) — gebruikt eenvoudige singletons
  • Cryptografie wordt afgehandeld door slechts 3 bekende bibliotheken: Google Tink, Bouncy Castle en de ingebouwde Java Cryptography Extension
  • Server draait op Ktor (officieel Kotlin-framework van JetBrains) — geen obscure serverbibliotheeken van derden

Webinterface (plain-desktop)

Slechts 28 runtime dependencies — veel minder dan de gemiddelde webapp. Verschillende onderdelen die normaal npm-pakketten vereisen, zijn helemaal zelf gebouwd:

FunctionaliteitGemiddelde appPlainApp
Notificatiegeluidenhowler.js / tone.jsEigen Web Audio API synth
UUID-generatieuuid npm-pakketEigen RFC 4122-implementatie
Bestandsuploadsuppy / axiosEigen chunked upload met wachtrij
WebRTC-videoTwilio / Daily.co SDKEigen PeerConnection-code
HTTP-clientaxios / kyIngebouwde browser fetch

Cryptografie aan de webkant gebruikt @noble/ciphers — een grondig gecontroleerde JavaScript-cryptobibliotheek zonder dependencies — in plaats van grote, opgeblazen alternatieven.

Waarom dit belangrijk is

Elke dependency die je toevoegt, is code waar je geen controle over hebt. De aanpak van PlainApp betekent:

  • Minder plekken waar kwaadaardige code zich kan verstoppen
  • Kleiner aanvalsoppervlak
  • Eenvoudiger te controleren (de volledige codebase is open source)
  • Geen risico door verlaten of gekaapte npm-pakketten

Wat veel concurrenten fout doen

Dit gaat niet over het noemen van specifieke apps. Het gaat om veelvoorkomende patronen in deze categorie:

BeveiligingsaspectPlainAppGangbaar bij concurrenten
API-verkeerVersleuteld (XChaCha20) in zowel HTTP als HTTPSPlatte JSON via HTTP
WebSocket-verkeerVersleutelde binaire frames (XChaCha20)Platte tekst JSON of geen WebSocket
Auth-token afhandelingGebruikt als versleutelingssleutel, nooit in headersVerzonden in HTTP-headers (makkelijk te lekken)
Bestands-URL'sVersleutelde pad-ID'sBestandspaden in platte tekst
Key exchangeECDH (P-256)Geen
VerzoekondertekeningEd25519 (alleen apparaat-naar-apparaat)Geen
Replay-bescherming30 s + nonce (Web); 5 min + handtekening (apparaat)Geen
InlogbeschermingSnelheidslimiet + tweefactorenbevestigingOnbeperkte pogingen
Datarouting100% lokaal, geen cloudSommige routeren via de cloud
DependenciesMinimaal, bekende bibliothekenHonderden npm-pakketten
BroncodeOpen source (GPL-3.0)Grotendeels closed source

De makkelijkste manier om elke app te controleren: open Chrome DevTools (F12) → tabblad Network terwijl je de app gebruikt. Als je API-antwoorden als JSON kunt lezen, heeft de app geen encryptie.

Controleer het zelf

PlainApp is volledig open source. Elke bewering in dit artikel kun je verifiëren in de code.

github.com/plainhub/plain-app (GPL-3.0)
Verifieerbare release-integriteit

Geautomatiseerde, traceerbare en manipulatiebestendige release-pipeline

Alle installatiepakketten worden automatisch gebouwd door de GitHub- en F-Droid-infrastructuur. Builds zijn traceerbaar, veilig en zonder handmatige build-ingrepen. PlainApp is een van de weinige Android-apps met zowel verifieerbare builds (SLSA) als VirusTotal-gescande releases.

Geautomatiseerde builds op GitHub en F-Droid

Release-artefacten worden gegenereerd door CI-pipelines in plaats van lokale handmatige packaging. Elk artefact is terug te herleiden naar broncommits en reproduceerbare build-workflows.

SLSA Provenance (Niveau 3) + VirusTotal-scan

Elke release bevat SLSA-provenance-attestaties en VirusTotal-scanresultaten, waardoor de supply chain auditbaar wordt en gebruikers de integriteit kunnen controleren vóór installatie.

Veelgestelde vragen

Is PlainApp echt versleuteld als ik HTTP-modus gebruik?

Ja. Alle API-aanroepen (sms, contacten, bestandslijsten, enz.) worden versleuteld met XChaCha20-Poly1305 in zowel HTTP- als HTTPS-modus. In HTTP-modus worden alleen bestandsdownloads niet versleuteld. Gebruik HTTPS voor volledige bescherming.

Waarom toont mijn browser een beveiligingswaarschuwing?

PlainApp gebruikt een zelfondertekend TLS-certificaat voor encryptie. Omdat het niet is uitgegeven door een publieke Certificate Authority, toont je browser een waarschuwing. Dit is normaal en te verwachten bij een app voor het lokale netwerk. Klik op 'Geavanceerd' en 'Doorgaan' om verder te gaan.

Kan iemand op hetzelfde Wi-Fi-netwerk mijn gegevens zien?

Met PlainApp niet. API-aanroepen worden versleuteld met XChaCha20-Poly1305, en in HTTPS-modus worden bestandsoverdrachten ook versleuteld door TLS. Bij de meeste concurrerende apps wel — hun API-aanroepen worden als platte tekst JSON verzonden.

Hoe verhoudt PlainApp zich tot een VPN?

Een VPN versleutelt verkeer tussen je apparaat en de VPN-server. PlainApp versleutelt verkeer tussen je telefoon en browser op het lokale netwerk. Ze dienen verschillende doelen — PlainApp beveiligt lokaal apparaatbeheer, terwijl een VPN internetverkeer beveiligt.

Is de code echt open source?

Ja. PlainApp is gelicenseerd onder GPL-3.0. De volledige broncode is beschikbaar op github.com/plainhub/plain-app. Je kunt elke encryptie-implementatie inspecteren, zelf bouwen vanuit de broncode of bijdragen.

Neem de controle over uw telefoon terug.

Geen cloud-tussenpersoon. Geen maandelijkse kosten. Alleen uw telefoon en uw browser.