Η λανθασμένη υπόθεση είναι ότι μια εφαρμογή VpnService γυρίζει έναν διακόπτη του συστήματος και από εκεί και πέρα το τηλέφωνο προστατεύεται. Αυτό που συμβαίνει στην πραγματικότητα είναι στενότερο: η εφαρμογή ζητά συγκατάθεση, χτίζει μια διεπαφή, διαβάζει και γράφει πακέτα μέσω ενός περιγραφέα αρχείου, και επιστρέφει τη διεπαφή όταν σταματά. Μόνο μία τέτοια σήραγγα μπορεί να είναι ζωντανή κάθε φορά, και το σύστημα μπορεί να την πάρει χωρίς να ρωτήσει. Το μεγαλύτερο μέρος της μπερδεμένης συμπεριφοράς που οι άνθρωποι καταλογίζουν στο πρωτόκολλο προέρχεται από αυτό το μοντέλο ιδιοκτησίας.

Αντιμετωπίστε κάθε μετάβαση του κύκλου ζωής ως μέρος του σχεδιασμού και όχι ως ακραία περίπτωση.

Μύθος: η εφαρμογή ενεργοποιεί το VPN του τηλεφώνου

Η ροή ξεκινά με VpnService.prepare(), που επιστρέφει ένα Intent το οποίο πρέπει να αποδεχτεί ο χρήστης, και συνεχίζεται με establish(), που μπορεί ακόμη να αποτύχει για λόγους που η εφαρμογή δεν ελέγχει. Η δημιουργία δεύτερης σήραγγας απενεργοποιεί την πρώτη διεπαφή και αντικαθιστά την ειδοποίηση του συστήματος. Πουθενά σε αυτή την ακολουθία η εφαρμογή δεν κατέχει διακόπτη για όλη τη συσκευή· κατέχει τον περιγραφέα που της δόθηκε και τα πακέτα που προσγειώνονται σε αυτόν. Ακόμη και η μόνιμη ειδοποίηση ανήκει στο σύστημα και όχι στον σχεδιασμό της εφαρμογής.

Επειδή η διεπαφή παραχωρείται και δεν αρπάζεται, ένας άλλος πελάτης μπορεί να πάρει τη θέση ανά πάσα στιγμή. Η εφαρμογή το μαθαίνει μέσω επιστροφής κλήσης και όχι μέσω τιμής επιστροφής.

Η αναγκαστική διακοπή της εφαρμογής φαίνεται στο συρτάρι ειδοποιήσεων για τον ίδιο λόγο: το σύστημα αναφέρει τι παραχώρησε, όχι τι επέλεξε να δείξει η εφαρμογή.

Μύθος: η συγκατάθεση δίνεται μία φορά

Όταν το σύστημα παραδώσει τη σήραγγα σε άλλον πελάτη, στέλνει μια συναλλαγή ανάκλησης στον binder που επιστρέφει η βασική κλάση από onBind(). Μια εφαρμογή που αντικαθιστά αυτόν τον binder με δικό της πρέπει να προωθήσει η ίδια τη συναλλαγή ανάκλησης, αλλιώς το VpnService.onRevoke() δεν ενεργοποιείται ποτέ και η υπηρεσία συνεχίζει να γράφει σε περιγραφέα που δεν υπάρχει πια. Η έγκριση μπορεί επίσης να ανακληθεί από τον χρήστη, και το σύστημα μπορεί να σταματήσει την υπηρεσία κατά τη διάρκεια επανεκκίνησης ή επανασύνδεσης πάντα ενεργής.

Η αντιμετώπιση είναι συγκεκριμένη δουλειά και όχι μια γραμμή αρχείου καταγραφής: σταματήστε να διαβάζετε τον περιγραφέα, κλείστε τον, καθαρίστε τη σημαία σύνδεσης και πείτε στον χρήστη σε ποια κατάσταση βρίσκεται. Έπειτα αποφασίστε αν η εφαρμογή πρέπει να επανασυνδεθεί, να ρωτήσει ξανά ή να περιμένει, γιατί σε διαμόρφωση πάντα ενεργή το σύστημα μπορεί να επαναφέρει την υπηρεσία μόνο του.

Μια εφαρμογή που αντιμετωπίζει την ανάκληση ως εξαίρεση και όχι ως κατάσταση θα αναφέρει ότι είναι συνδεδεμένη ενώ κάθε πακέτο που γράφει απορρίπτεται, που είναι ο χειρότερος δυνατός τρόπος αποτυχίας για ένα εργαλείο ιδιωτικότητας.

Μύθος: η διεπαφή φιλτράρει την κίνηση για εσάς

Αυτό που φτάνει είναι ακατέργαστο IP. Η σύλληψη ή το φιλτράρισμα σημαίνει ανάλυση κεφαλίδων IP, TCP, UDP και IPv6, παρακολούθηση ροών και εγγραφή απαντήσεων πίσω στον περιγραφέα, κάτι που κάνει μια εφαρμογή σύλληψης ένα μικρό δίκτυο στοίβας και όχι μια υποδομή συστήματος. Το protect() υπάρχει ώστε η ίδια η υποδοχή μεταφοράς της εφαρμογής να παρακάμπτει τη σήραγγά της αντί να συλλαμβάνει τον εαυτό της, και επιστρέφει μια λογική τιμή που αξίζει έλεγχο σε κάθε υποδοχή που δημιουργείται αφού η σήραγγα είναι ενεργή.

Οι λίστες επιτρεπόμενων και απαγορευμένων εφαρμογών στον κατασκευαστή καθορίζουν ποιανού η κίνηση μπαίνει στη διεπαφή. Δεν παρέχουν πρόσβαση σε ωφέλιμο φορτίο πέρα από τα πακέτα IP που μεταφέρει η διεπαφή, και κανένα API δεν εκθέτει τη σήραγγα άλλου πελάτη ούτε απαριθμεί τα προφίλ VPN του συστήματος. Οι δυνατότητες δικτύου μπορούν να πουν ότι ένα δίκτυο είναι μεταφορά VPN, αλλά όχι ποιανού είναι η σήραγγα.

Γι’ αυτό οι ερωτήσεις κατάστασης για άλλες εφαρμογές παίρνουν αντιφατικές απαντήσεις: η πληροφορία απλώς δεν εκτίθεται στη διεργασία σας.

Τι απαιτεί πραγματικά ο κύκλος ζωής

Παρακολουθήστε τις καταστάσεις που ελέγχετε: προετοιμασμένη, εγκατεστημένη, ανακληθείσα, σταματημένη. Η επανασύνδεση πρέπει να είναι αποσυναρμολόγηση και μετά εγκατάσταση, με έναν μόνο ιδιοκτήτη του περιγραφέα και της υποδοχής μεταφοράς, γιατί δύο στοιχεία που συναγωνίζονται να εγκαταστήσουν θα μαλώσουν για τη μία διαθέσιμη θέση. Οι εκκινήσεις στο παρασκήνιο είναι περιορισμένες στο σύγχρονο Android, οπότε μια εκπομπή κατά την εκκίνηση δεν είναι εγγύηση· χρησιμοποιήστε υπηρεσία προσκηνίου με την ειδοποίησή της όπου το επιτρέπει το επίπεδο API.

Καταγράψτε τη μετάβαση και όχι μόνο την τελική κατάσταση, και κρατήστε τη μηχανή καταστάσεων αρκετά μικρή ώστε να δοκιμάζετε κάθε διαδρομή. Μια κατάσταση που δεν μπορείτε να φτάσετε σε δοκιμή είναι μια κατάσταση που θα συναντήσετε στο πεδίο χωρίς αρχείο καταγραφής.

Δοκιμάστε τον κύκλο ζωής σε συσκευή

Οι εξομοιωτές μπορούν να προσποιηθούν συνδεσιμότητα αλλά όχι πειστικά τη διαδρομή συγκατάθεσης και ανάκλησης, οπότε εκτελέστε τα σε πραγματική συσκευή με δεύτερο πελάτη VPN εγκατεστημένο για να κλέψει τη θέση.

adb shell dumpsys connectivity | grep -i -A3 vpn
adb shell dumpsys activity services | grep -i -A2 vpnservice
adb logcat -s VpnService:V AndroidRuntime:E
adb shell am force-stop com.example.vpnapp

Ξεκινήστε τη σήραγγα, επιβεβαιώστε την ειδοποίηση και τη διεπαφή, έπειτα συνδέστε τον άλλο πελάτη και παρατηρήστε αν εκτελείται η επιστροφή κλήσης ανάκλησης και αν καθαρίζει η κατάσταση. Σταματήστε βίαια την εφαρμογή και ελέγξτε αν κάτι εξακολουθεί να αναφέρει ζωντανό περιγραφέα.

Επαναλάβετε την ακολουθία μετά από επανεκκίνηση πάντα ενεργή, γιατί αυτή η διαδρομή διαφέρει από μια σύνδεση που ξεκινά ο χρήστης. Αυτό που ψάχνετε δεν είναι ένα πράσινο εικονίδιο αλλά απόδειξη ότι η εφαρμογή σταμάτησε να χρησιμοποιεί περιγραφέα που δεν κατείχε πια.

Σχετικά άρθρα