feat(android): read the snapshot from a Gitea repo, and close cleartext
PMA.SyncClient now speaks two transports, told apart by the URL: /api/v1/repos/ means the Gitea contents API, anything else the old PHP endpoint. The PHP path stays until the Windows side publishes to the repo too - without it the migration would need both ends flipped at once, with no way back. Three things differ on the Gitea side and nothing else: the header is "token <PAT>" not "Bearer" (swapping them gives a silent 401), the body is JSON carrying base64 content, and the fingerprint comparison happens in the client since the API has no If-None-Match. The body is re-downloaded, which is cheap; the four-second key derivation is still avoided. The result's ETag field carries the sha, so the caller cannot tell which transport answered. usesCleartextTraffic is replaced by a network config that permits cleartext to 127.0.0.1 and localhost BY NAME and forbids it everywhere else. The attribute drew no such line: a mistyped URL sent the bearer token over the network in the clear with nothing to signal it. Now it fails immediately, which is the behaviour worth having. Verified in the built APK: res/xml/network_security_config.xml is present and the manifest attribute resolves to a real resource id. NOT verified against a real repo - that needs a private repo, a published vault-sync.json and a token, none of which exist yet. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+32
-8
@@ -177,11 +177,15 @@ Par ordre de gravité, pas de taille.
|
||||
1. ~~**`android:allowBackup="false"`**~~ — **fait**. Le répertoire privé
|
||||
contient `vault.bin` ; la sauvegarde Google n'a rien à y faire, même si ce
|
||||
qui en sortirait serait chiffré par une clé restée dans la puce.
|
||||
2. **Retirer `usesCleartextTraffic="true"`** du manifeste, et servir
|
||||
l'endpoint en HTTPS avec un vrai certificat. L'attribut est marqué
|
||||
TEST-ONLY ; le jeton Bearer circule à chaque appel. **Gardé pour l'instant** :
|
||||
le retirer casserait l'essai local par `adb reverse`, qui passe en clair.
|
||||
Voir §6 : passer par Gitea réglerait ce point sans rien héberger de plus.
|
||||
2. ~~**Retirer `usesCleartextTraffic="true"`**~~ — **fait le 2026-08-17**, mais
|
||||
pas en le retirant : remplacé par un
|
||||
[network_security_config.xml](android-res/xml/network_security_config.xml) qui
|
||||
n''autorise le clair que vers `127.0.0.1` et `localhost`, **nommément**, et
|
||||
l''interdit partout ailleurs. L''attribut ne faisait pas cette distinction :
|
||||
une URL mal saisie envoyait le jeton en clair sur le réseau sans que rien ne
|
||||
le signale. Maintenant elle échoue tout de suite, ce qui est le comportement
|
||||
voulu — bruyant, pas silencieux. L''essai local par `adb reverse` continue de
|
||||
passer.
|
||||
3. ~~**`FLAG_SECURE`**~~ — **fait**, [PMA.ScreenGuard.pas](PMA.ScreenGuard.pas),
|
||||
**en Release seulement** : en debug le drapeau interdit aussi les captures
|
||||
d'écran de développement. Le vérifier demande donc une compilation Release.
|
||||
@@ -414,9 +418,29 @@ purge. Ça ne l'est pas sur GitHub, où le chiffré part chez un tiers, répliqu
|
||||
et sauvegardé, avec un dépôt rendu public par erreur pour scénario du pire :
|
||||
attaquable hors ligne, par n'importe qui, pour toujours.
|
||||
|
||||
**À faire si on s'y engage** : un `PMA.SyncClient` qui parle cette API (le GET
|
||||
authentifié existe déjà), la même chose côté poste Windows en remplacement de
|
||||
WebDAV, et une purge périodique de l'historique — sans quoi le dépôt grossit
|
||||
### État : la lecture est écrite, elle n'a jamais parlé à un vrai dépôt
|
||||
|
||||
[PMA.SyncClient.pas](PMA.SyncClient.pas) parle les deux transports, **reconnus à
|
||||
l'URL** : `/api/v1/repos/` → Gitea, sinon l'endpoint PHP. Le PHP est gardé le
|
||||
temps que le poste Windows publie aussi dans le dépôt ; sans lui la migration
|
||||
exigerait de tout basculer d'un coup, des deux côtés, sans retour possible.
|
||||
|
||||
Ce qui change côté Gitea, et rien d'autre : l'en-tête est `token <PAT>` et non
|
||||
`Bearer` (l'un à la place de l'autre donne un 401 muet), le corps est du JSON
|
||||
dont il faut extraire `content` en base64, et **la comparaison d'empreinte se
|
||||
fait dans le client** — l'API ne connaît pas `If-None-Match`, donc le corps est
|
||||
retéléchargé, mais la dérivation de clé, elle, est bien épargnée. Le champ
|
||||
`ETag` du résultat porte le `sha` : l'appelant n'a pas à savoir lequel des deux
|
||||
transports lui répond.
|
||||
|
||||
**Rien de tout cela n'a encore atteint un vrai dépôt.** Il faut, dans cet ordre :
|
||||
un dépôt privé, un fichier `vault-sync.json` publié par le poste Windows, un
|
||||
jeton limité à ce dépôt, puis l'URL de contenu complète dans *Server settings*.
|
||||
Tant que le poste publie sur `sync.php`, le téléphone continue de lire là.
|
||||
|
||||
**Reste à faire** : le PUT (§4 seulement — la lecture n'écrit rien), la même
|
||||
bascule côté poste Windows en remplacement de WebDAV, et une purge périodique de
|
||||
l'historique — sans quoi le dépôt grossit
|
||||
d'un instantané complet à chaque synchronisation, pièces jointes comprises.
|
||||
|
||||
## Ordre proposé
|
||||
|
||||
Reference in New Issue
Block a user