add historical content
This commit is contained in:
commit
007030b209
11 changed files with 1514 additions and 0 deletions
18
about.md
Normal file
18
about.md
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
---
|
||||||
|
title: "About"
|
||||||
|
date: 2019-03-24T23:22:48+01:00
|
||||||
|
draft: false
|
||||||
|
---
|
||||||
|
My name is Florian Maury. I am a network and protocol security specialist.
|
||||||
|
|
||||||
|
[My resume](/cv.pdf)
|
||||||
|
|
||||||
|
My social media links:
|
||||||
|
|
||||||
|
* [Twitter](https://twitter.com/X_Cli_Public)
|
||||||
|
* [Fediverse](https://infosec.exchange/@X_Cli)
|
||||||
|
|
||||||
|
My Contact Info:
|
||||||
|
|
||||||
|
* florian.maury-website@broken-by-design.fr
|
||||||
|
|
3
posts/_index.md
Normal file
3
posts/_index.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
---
|
||||||
|
title: "All posts"
|
||||||
|
---
|
128
posts/anti-pass-sanitaire.md
Normal file
128
posts/anti-pass-sanitaire.md
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
---
|
||||||
|
title: "Vacciné et contre le pass sanitaire"
|
||||||
|
slug: anti-pass-sanitaire
|
||||||
|
authors: "Florian Maury"
|
||||||
|
description: "Un billet d'humeur contre l'extension du pass sanitaire, son principe en général et ses dérives autoritaires"
|
||||||
|
date: 2021-07-19T00:00:00+02:00
|
||||||
|
type: posts
|
||||||
|
draft: false
|
||||||
|
categories:
|
||||||
|
- france
|
||||||
|
tags:
|
||||||
|
- privacy
|
||||||
|
- human rights
|
||||||
|
- tyranny
|
||||||
|
- covid19
|
||||||
|
- pass sanitaire
|
||||||
|
---
|
||||||
|
|
||||||
|
Je suis contre le pass sanitaire, et je suis pourtant vacciné.
|
||||||
|
|
||||||
|
Comme il semble importer à certains de comprendre qui parle pour ensuite savoir l'écouter, voici un court résumé sur ma personne. Les gens intéressés uniquement par le fond peuvent sauter la prochaine section de ce billet.
|
||||||
|
|
||||||
|
# Qui suis-je ?
|
||||||
|
|
||||||
|
Je suis Florian Maury. J'ai 38 ans, et je suis en surpoids (IMC 29.5), sans autre comorbidité connue. Je ne suis ni rattaché, ni sympathisant d'aucun parti politique. Mon seul engagement politique significatif est l'antispécisme.
|
||||||
|
|
||||||
|
Pendant la pandémie, mon emploi a été maintenu et j'ai pu le continuer, en bénéficiant du télétravail à 100%. Ce jour encore, je suis en télétravail.
|
||||||
|
|
||||||
|
Ma compagne, elle, travaille avec des enfants et est donc exposée à un risque de contamination plus important que d'autres corps de métier.
|
||||||
|
|
||||||
|
J'ai fait le choix de me vacciner parce que j'ai considéré les risques de ne pas me vacciner, et ceux de me vacciner, et j'ai jugé que la balance bénéfices/risques, dans mon cas, penchait vers l'intérêt d'être vacciné. C'est un calcul personnel, effectué exclusivement sur des considérations égoïstes, et à aucun moment pour protéger les autres. À ma place, vous auriez pu faire un choix différent ; chacun son corps.
|
||||||
|
|
||||||
|
Pour protéger les autres, je respecte les gestes barrières depuis la première heure. J'ai porté un masque, conformément aux directives sanitaires, y compris en forêt, lorsque je croisais d'autres promeneurs (ce qui pourra paraître hygiéniste à certains). En fait, je portais même un masque dans les transports en commun et sur mon lieu de travail AVANT la pandémie de covid-19, lorsque j'étais malade. Par respect pour mes collègues de travail, et mes compagnons de voyage de la région parisienne.
|
||||||
|
|
||||||
|
# Quels sont les problèmes du pass sanitaire ?
|
||||||
|
|
||||||
|
Le pass sanitaire présente de nombreux problèmes, sur le plan technique, sur le plan juridique et sur le plan éthique.
|
||||||
|
Dans ce billet, il pourra être question du vaccin, puisque les deux sont quasiment indissociables depuis les annonces d'Emmanuel Macron, ce 12 juillet 2021.
|
||||||
|
|
||||||
|
## Aspect technique
|
||||||
|
|
||||||
|
Depuis la parution du pass sanitaire, de nombreuses critiques sont formulées sur sa réalisation technique.
|
||||||
|
|
||||||
|
Dans [cet article](https://www.broken-by-design.fr/posts/pass-sanitaire/), Piotr Chmielnicki et moi-même avons analysé le contenu du pass sanitaire, et l'avons comparé aux annonces gouvernementales, aux déclarations sur les sites gouvernementaux, et à l'avis de la CNIL.
|
||||||
|
|
||||||
|
Nous avons révélé que le pass contenait de nombreuses informations sans rapport avec la finalité du pass, dont des informations médicales, et des informations affectant la vie privée, et pouvant causer des risques dans le cadre du vol d'identité. Ces affirmations ont été confirmées par de nombreuses études indépendantes et publiées à la même période. On pensera à celle de [Mathis Hammel](https://twitter.com/MathisHammel/status/1397902091067265026), celle de [Christian Quest](https://cq94.medium.com/la-fin-du-pseudonymat-dans-tousanticovid-932d50de11ee), aux excellentes analyses de TousAntiCovid Verif par [Gilbsgilbs](https://twitter.com/gilbsgilbs), ou à l'implémentation de preuves de concept comme [sanipasse.fr](https://sanipasse.fr/) et celle de [Bastien Le Querrec](https://twitter.com/BLeQuerrec/status/1401290053687644169), de la Quadrature du Net.
|
||||||
|
|
||||||
|
Il est intéressant de noter que dans son [avis du 12 mai](https://www.cnil.fr/sites/default/files/atoms/files/deliberation_2021-054_du_12_mai_2021_portant_avis_sur_le_projet_de_mise_en_place_dun_passe_sanitaire.pdf), la CNIL disait d'ailleurs à juste titre :
|
||||||
|
|
||||||
|
> 8. La Commission estime que l’accès à un lieu ne saurait, par principe, être conditionné
|
||||||
|
à la divulgation d’informations relatives à l’état de santé des personnes, y compris
|
||||||
|
s’agissant de lieux qui n’ont pas trait à la vie quotidienne. En effet, si la vérification de
|
||||||
|
l’identité des personnes peut être exigée pour l’accès à certains lieux, l’exigence de
|
||||||
|
divulgation d’autres informations relatives à la vie privée des personnes, a fortiori de
|
||||||
|
données sensibles, ne saurait être admise qu’au regard de la nature du lieu ou de
|
||||||
|
l’événement fréquenté et dans le cadre de la stricte application du principe de
|
||||||
|
minimisation de la collecte de ces données. La possibilité d’accéder aux lieux de
|
||||||
|
sociabilité sans avoir à prouver son état de santé fait partie des garanties apportées à
|
||||||
|
l’exercice des libertés et participe à dessiner une frontière raisonnable entre ce qui
|
||||||
|
relève de la responsabilité individuelle et du contrôle social.
|
||||||
|
|
||||||
|
En outre, principalement grâce à Gilbsgilbs, le peuple français a pu prendre conscience que TousAntiCovid Verif fuitait de manière systématique, à chaque scan d'un pass, le contenu de ce pass sanitaire (c'est-à-dire nos données privées et de santé) à un acteur américain (Akamai), puis à une société française : IN Groupe, l'imprimerie nationale. L'application TousAntiCovid Verif pouvait donc être transformée en une application de surveillance de masse, permettant de savoir qui se rendait où et quand. Cette fuite de données était, par ailleurs, parfaitement injustifiable d'un point de vue technique, d'après de nombreux spécialistes en cryptographie. D'ailleurs, [Olaf](https://github.com/ofa-/) et le créateur de sanipasse.fr l'ont prouvé en fournissant au public des vérificateurs de pass sanitaires hors-ligne, c'est-à-dire sans connexion à Internet nécessaire. En outre, TousAntiCovid Verif contenait des logiciels de tracking des utilisateurs provenant de Google.
|
||||||
|
|
||||||
|
De nombreux journaux ont relayé l’information, parmi lesquels [NextInpact](https://www.nextinpact.com/article/46153/pass-sanitaire-poudre-aux-yeux-pseudonymat-donnees-medicales-en-clair), [Numerama](https://www.numerama.com/tech/717219-tousanticovid-verif-pourquoi-lapp-verifiant-les-donnees-du-pass-sanitaire-fait-polemique.html), [01net](https://www.01net.com/actualites/tousanticovid-verif-le-panier-perce-du-pass-sanitaire-2044056.html), [Mediapart](https://www.mediapart.fr/journal/france/090621/donnees-personnelles-les-controles-du-passe-sanitaire-suscitent-linquietude), [Contrepoints](https://www.contrepoints.org/2021/06/09/399255-donnees-medicales-anonymat-surveillance-un-pass-sanitaire-passoire), [Le Monde Informatique](https://www.lemondeinformatique.fr/actualites/lire-tousanticovid-le-pass-sanitaire-en-quete-de-protection-83204.html). Il y eut aussi d'autres formes de relais dont [Developpez](https://www.developpez.com/actu/315816/Application-TousAntiCovid-et-Pass-sanitaire-les-informations-personnelles-et-medicales-sont-disponibles-en-clair-previent-Christian-Quest/) ou [iGeneration](https://www.igen.fr/ailleurs/2021/06/le-pass-sanitaire-souleve-des-craintes-en-matiere-de-confidentialite-123027).
|
||||||
|
Finalement, le gouvernement a annoncé, lors d'une [conférence de presse](https://www.nextinpact.com/article/46147/pass-sanitaire-donnees-en-clair-open-source-on-fait-au-plus-simple), l'élaboration dans l'urgence d'un mode "offline" (hors-ligne). Cette version avec un mode offline a été mise en ligne deux jours après. À ce jour, le code source de TousAntiCovid Verif n'a toujours pas été publié, contrairement aux annonces gouvernementales effectuées durant cette même conférence de presse.
|
||||||
|
|
||||||
|
Depuis, Olaf et Gilbsgilbs ont pu noter que la fonctionnalité de conversion des pass sanitaires français en pass sanitaires européens, par l'entremise de TousAntiCovid, avait recours à l'envoi du pass sanitaire dans son intégralité, toujours via les serveurs américains d'Akamai, puis via les serveurs d'IN Groupe.
|
||||||
|
|
||||||
|
Le gouvernement a annoncé une migration vers Orange, afin d’éviter la fuite des données en Amérique. À l’instar de la publication du code source de TousAntiCovid Verif, le cierge est allumé...
|
||||||
|
|
||||||
|
## Aspect juridique
|
||||||
|
|
||||||
|
Le pass sanitaire présente de nombreuses faiblesses juridiques.
|
||||||
|
|
||||||
|
Pour commencer, le pass sanitaire a failli être retoqué par le Parlement puisqu'il a été nécessaire de faire deux votes, le premier ayant eu pour résultat son rejet. C'est au prix d'amendements, à l'initiative du Modem, visant à restreindre la durée du pass, que celui-ci a été finalement accepté par le Parlement.
|
||||||
|
|
||||||
|
Parmi les dysfonctionnements persistants, on note ainsi dans la loi n° 2021-689 du 31 mai 2021 relative à la gestion de la sortie de crise sanitaire :
|
||||||
|
|
||||||
|
> La présentation, sur papier ou sous format numérique, des documents mentionnés au premier alinéa du présent B est réalisée sous une forme ne permettant pas aux personnes habilitées ou aux services autorisés à en assurer le contrôle de connaître la nature du document ni les données qu'il contient.
|
||||||
|
|
||||||
|
Or il se trouve que le pass sanitaire est librement lisible par tous, à l'aide d'un lecteur de code barre 2D, et qu'il contient les noms, prénoms, date de naissance, date de la dernière injection, agent prophylactique, état du cycle vaccinal, et nom et marque du vaccin, dans le cas d'un pass sanitaire de type vaccinal. Des informations différentes sont présentées dans le cas d'un pass sanitaire de type test. Le pass est donc contraire à cette loi en cela qu'il permet aux personnes habilitées ou aux services autorisés à en assurer le contrôle de connaître la nature du document et les données qu'il contient. Peu importe que l'application TousAntiCovid Verif dissimule certaines informations ; n'importe quel lecteur de code barres peut en révéler le contenu intégral.
|
||||||
|
|
||||||
|
En outre, il est attendu, pour qu'il soit efficace, que les personnes habilitées ou les services autorisés à en assurer le contrôle puissent vérifier l'identité du porteur du pass, notamment en comparant les noms, prénoms et date de naissance du pass avec ceux sur une pièce d'identité. Or, la vérification de l'identité d'une personne est encadrée par la loi. En l'occurrence, sur le site [Services Publics.fr](https://www.service-public.fr/particuliers/vosdroits/F1036), on peut lire que le contrôle d'identité est limité aux personnes suivantes :
|
||||||
|
|
||||||
|
> Les forces de l'ordre (police, gendarmerie) habilitées à faire un contrôle d'identité sont les suivantes :
|
||||||
|
> * Officier de police judiciaire (OPJ)
|
||||||
|
> * Agents de police judiciaire, sous la responsabilité de l'OPJ
|
||||||
|
> * Certains agents de police judiciaire adjoints, sous la responsabilité de l'OPJ
|
||||||
|
> Un douanier peut aussi faire un contrôle d'identité dans certains cas.
|
||||||
|
> A savoir : un agent de police municipale peut relever votre identité lorsqu'il constate une contravention. Par exemple, une contravention de stationnement. Toutefois, il n'est pas autorisé à contrôler votre identité.
|
||||||
|
|
||||||
|
Il convient donc de noter que ni les barmen, ni les restaurateurs, ni les agents de sécurité des boîtes de nuit, des centres commerciaux, des hôpitaux, des cinémas et théâtres, ou d'aucun lieu de loisirs ne sont habilités à demander aux citoyens français leurs papiers d'identité pour en faire le contrôle. Alexandre Horn informe également dans ce sens, dans [les colonnes de Libération](https://www.liberation.fr/checknews/pass-sanitaire-commercants-et-restaurateurs-peuvent-ils-controler-lidentite-de-leurs-clients-20210716_G7VJUUHV4RHLTABS4B64OWHTZU/).
|
||||||
|
|
||||||
|
Par ailleurs, la Quadrature du Net a saisi le Conseil d'État, le 9 juin, à propos du pass sanitaire, via la procédure de référé-liberté. Bien que le Conseil d'État soit censé rendre un verdict en deux jours, dans ce cadre de procédure, il a fallu attendre près d'un mois pour obtenir un jugement. Il s'agit là d'un délai noté comme anormalement long par de nombreux juristes, même dans les circonstances exceptionnelles de cette pandémie.
|
||||||
|
|
||||||
|
[Le verdict du Conseil d'État](https://www.conseil-etat.fr/actualites/actualites/le-conseil-d-etat-ne-suspend-pas-le-passe-sanitaire), rendu le 6 juillet a soigneusement évité d'adresser certains points cruciaux de la plainte, a énoncé des banalités sans rapport avec la plainte (avaient-ils perdu le dossier ?), et n'a finalement pas suspendu le pass sanitaire pour les raisons suivantes (liste non exhaustive) :
|
||||||
|
|
||||||
|
* "Il résulte de l'instruction et il n'est d'ailleurs pas contesté que le traitement TousAntiCovid Vérif effectivement mis en oeuvre par le ministre de solidarités et de la santé repose sur un contrôle local des données contenues par les justificatifs (" mode off-line "), et que le Gouvernement a renoncé à tout échange de données avec le serveur central de la société prestataire lors de la vérification des justificatifs présentés sur le téléphone mobile de la personne entendant se prévaloir du passe sanitaire". On notera que ce "renoncement" du gouvernement coïncide avec les révélations faites par le groupe de citoyens mentionnés dans la section "Aspects techniques" de ce billet.
|
||||||
|
|
||||||
|
* "Son usage a été restreint aux déplacements avec l'étranger, la Corse et l'outre-mer, d'une part, et à l'accès à des lieux de loisirs, d'autre part, sans que soient concernées les activités quotidiennes ou l'exercice des libertés de culte, de réunion ou de manifestation".
|
||||||
|
|
||||||
|
Or, avec l'extension du pass sanitaire annoncée par le Président de la République ce 12 juillet 2021, soit 6 jours après le verdict du Conseil d'État, il apparaît très clairement que le contexte sanitaire n'a pas changé du tout au tout en ces 6 jours, et que le pass concerne désormais les activités quotidiennes.
|
||||||
|
|
||||||
|
Sur le plan juridique, il y a également la question de la proportionnalité de l'atteinte à la liberté de circulation, et au principe d’égalité, au vu de l'objectif. Étant donné que la France est l'un des rares pays à avoir choisi de déployer un tel dispositif généralisé, il me semble raisonnable d'en douter. Hélas, comme l'explique [Dominique Bompoint dans son article dans Le Figaro](https://www.lefigaro.fr/vox/societe/passe-sanitaire-n-attendons-rien-du-conseil-constitutionnel-20210716), il n'y a rien à attendre du Conseil Constitutionnel.
|
||||||
|
|
||||||
|
Le recours déposé par Piotr Chmielnicki auprès de la CNIL, avec en copie le Défenseur des droits, n'a pas abouti. Le Défenseur des droits s'est défaussé en pointant vers [son article à ce sujet](https://www.defenseurdesdroits.fr/fr/communique-de-presse/2021/05/la-defenseure-des-droits-sinquiete-des-risques-datteintes-aux-droits-et), publié en mai, à une date antérieure à la plainte, et qui ne répond à aucun des éléments techniques avancés dans la plainte. On peut notamment y lire "Le projet de loi a fait l’objet de modifications par le Sénat, maintenues par la commission mixte paritaire, dont certaines vont dans le sens des recommandations de la Défenseure des droits, en particulier l’intégration dans le texte de garanties complémentaires concernant le « pass sanitaire », en vue de protéger les droits et libertés, notamment les données de santé". Or, il s'avère que même si la loi contient effectivement ces garanties, l'implémentation du pass sanitaire par le gouvernement français en fait fi. Le Défenseur des droits n’a donc visiblement pas jugé utile de vérifier.
|
||||||
|
La CNIL n’a fait qu’accuser réception, plus d’un mois après le dépôt de plainte. Elle n’a pas statué sur le fond pour le moment, près de deux mois après la réception du recommandé avec accusé de réception.
|
||||||
|
|
||||||
|
# Aspect éthique
|
||||||
|
|
||||||
|
Le pass sanitaire devient moralement un pass vaccinal. En effet, le déremboursement des tests sans prescription médicale revient à ce que les personnes ne souhaitant pas se faire vacciner, ou ne le pouvant pas pour raisons de santé, soient contraintes de se vacciner, ou à débourser plusieurs centaines d'euros par mois, pour vivre normalement. Il s'agit là d'une violence sociale, particulièrement grave, contre les personnes en situation de précarité, ou en situation de santé complexes. Criant est le manque d'humilité des instances dirigeantes sur leur capacité à capturer l'infinie complexité des situations individuelles et à asséner une solution universelle.
|
||||||
|
|
||||||
|
Le pass sanitaire va donc créer deux castes de citoyens : les vaccinés et les non-vaccinés. Le Président de la République soulignait d'ailleurs lors d'un [entretien](https://www.francetvinfo.fr/sante/maladie/coronavirus/pass-sanitaire/manifestations-contre-le-pass-sanitaire-vos-droits-ne-peuvent-pas-etre-les-memes-repond-emmanuel-macron_4704357.html) : "Vos droits ne peuvent pas être les mêmes parce qu'ils supposent des devoirs". Dès lors, la notion d'égalité, énoncée par la Constitution française, vole en éclat. Cette division participe, d’ailleurs, à créer des tensions entre citoyens, et détourne l’ire de la foule de la caste des dirigeants vers celle des non-vaccinés. Cette diversion participe activement à dédouaner et déresponsabiliser la caste politique de la situation actuelle, alors qu’ils en sont les premiers fautifs.
|
||||||
|
|
||||||
|
Hélas, les pauvres hères de la caste des non-vaccinés (ou devrait-on les appeler "intouchables" ?) ne seront pas tous relégués ainsi par choix. Même en admettant que tous souhaitent se faire vacciner, il n’y a pas assez de doses pour vacciner toute la population âgée de 12 ans et plus.
|
||||||
|
Avec [50 millions d’adultes âgés de 20 ans et plus](https://www.ined.fr/en/everything_about_population/data/france/population-structure/population-age/), il faudrait au minimum 100 millions de doses pour tous les vacciner. C’est faire l’hypothèse qu’aucune dose n’a été perdue, et qu’il faut deux doses pour chaque personne. Or, depuis le début de la pandémie, la France n’a reçu que [71 millions de doses](https://solidarites-sante.gouv.fr/grands-dossiers/vaccin-covid-19/article/le-tableau-de-bord-de-la-vaccination). Bien que nous soyons censés en recevoir 9.6 millions d’ici fin juillet, et 14 millions en août (source : JT de France 2 du 17 juillet), le chiffre de 100 millions ne sera pas atteint. Et c’est sans compter la vaccination des 12 à 20 ans. Le gouvernement a donc choisi délibérément, et en connaissance de ces chiffres publics, une stratégie qui plongera des millions de citoyens dans la difficulté, qu’ils soient pour ou contre le vaccin, et pour ou contre le pass sanitaire. Une stratégie antisociale, déshumanisante, déresponsabilisante, infantilisante.
|
||||||
|
|
||||||
|
Finalement, on peut s’interroger sur le caractère éthique d’un très haut taux de vaccination dans les pays développés, imposé par le gouvernement en France avec le pass désormais vaccinal, quand [87% de la population mondiale n’a pas reçu deux doses](https://ourworldindata.org/covid-vaccinations). Freinerait-on plus efficacement l’épidémie en diminuant la propagation du virus (et donc ses chances de mutation, qui affecteront également les vaccinés du premier monde) dans les pays du tiers monde ? Probablement.
|
||||||
|
|
||||||
|
# Conclusion
|
||||||
|
|
||||||
|
Le pass sanitaire, maintenant devenu pass vaccinal, représente à lui seul la déchéance de tous les principes énoncés par notre devise nationale. Séparation des citoyens en castes ; privation de libertés pour les non-vaccinés volontaires et involontaires ; obligation vaccinale pour certains corps de métiers, sous peine d’être licenciés de plein droit ; tensions accrues entre les citoyens menant à des actes de violences et de vandalismes. La défaite est totale.
|
||||||
|
L’individualisme est la règle, et dès lors que certains ont leur fameux sésame, ils se contrefichent, et pire, jugent, leurs compatriotes, au lieu de se montrer solidaires et de comprendre qu’ils seront peut-être les prochains exclus, au détour d’une nouvelle loi liberticide des gouvernants.
|
||||||
|
|
||||||
|
Un passeport n’a jamais été le symbole de la liberté. Un passeport est un passe-droit accordé à des personnes qui peuvent outrepasser une nouvelle règle restrictive, afin que leurs détenteurs puissent continuer de procéder comme avant. N’oublions jamais les laissés pour compte qui ne jouissent pas de ce passe-droit, et souvenons-nous que nous serons peut-être les prochains à être laissés sur le carreau.
|
||||||
|
|
||||||
|
Je souhaiterais terminer ce billet avec le cri du Général Kellermann lors de la bataille de Valmy, où les forces prussiennes ont été repoussées par l’armée des citoyens : “**Vive la Nation !**”. Faisons trembler la terre, et pourquoi pas l’Élysée !
|
||||||
|
|
180
posts/attestation-covid.md
Normal file
180
posts/attestation-covid.md
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
---
|
||||||
|
title: "Attestation COVID-19 en ligne : un outil de surveillance globale sur Internet ?"
|
||||||
|
slug: attestation-covid-19
|
||||||
|
authors: ["Florian Maury", "Piotr Chmielnicki"]
|
||||||
|
description: "Risques et recommandations relatives à l'application d'émission d'attestation de déplacements dérogatoires COVID-19 du Ministère de l'Intérieur"
|
||||||
|
date: 2020-11-25T22:00:00+00:00
|
||||||
|
type: posts
|
||||||
|
draft: false
|
||||||
|
categories:
|
||||||
|
- web
|
||||||
|
tags:
|
||||||
|
- web
|
||||||
|
- localStorage
|
||||||
|
- SOP
|
||||||
|
---
|
||||||
|
Cet article est sous licence CC-BY-NC-ND.
|
||||||
|
|
||||||
|
Les auteurs de cet article peuvent être contactés sur :
|
||||||
|
|
||||||
|
* le numéro Signal : +33687414960 (Florian Maury)
|
||||||
|
|
||||||
|
* le compte Twitter : [Piotr Chmielnicki - Twitter](https://twitter.com/piotrcki)
|
||||||
|
|
||||||
|
* le compte Twitter : [Florian Maury - Twitter](https://twitter.com/x_cli_public)
|
||||||
|
|
||||||
|
Synthèse non-technique
|
||||||
|
----------------------
|
||||||
|
|
||||||
|
Le Ministère de l'Intérieur, par l'intermédiaire du site permettant la génération d'attestations de déplacement dérogatoire COVID-19, est en mesure d'assurer un pistage nominatif des citoyens, en usant de procédés comparables à ceux qu'emploient les régies publicitaires et d'autres spécifiques aux services de renseignement. Il est impossible de prouver qu'ils ne le font ou ne le feront pas. Les auteurs de cet article ne détiennent pas, à ce jour, de preuve non plus qu'ils le font ou l'on fait. Cet article expose un risque et propose des contremesures. Les observations de cet article restent valables même après le 15 décembre 2020, lorsque les attestations ne seront potentiellement plus obligatoires, à cause de la persistence infinie des données de pistage.
|
||||||
|
|
||||||
|
Il n'existe pas de contremesure technique à certaines des méthodes de pistage évoquées dans cet article. Selon le principe de précaution, il est recommandé aux citoyens d'éviter le risque en ne faisant pas usage de la fonctionnalité "Mon téléphone se souvient de moi", ou en privilégiant les attestations au format papier. Si un citoyen a déjà utilisé la fonctionnalité "Mon téléphone se souvient de moi", il est recommandé qu'il efface son historique de navigation, y compris les données mémorisées par le site web https://media.interieur.gouv.fr.
|
||||||
|
|
||||||
|
Il est recommandé au Ministère de l'Intérieur de modifier le site web de génération des attestations de déplacement COVID-19 afin :
|
||||||
|
|
||||||
|
* de supprimer la fonctionnalité "Mon téléphone se souvient de moi" ;
|
||||||
|
|
||||||
|
* d'automatiser l'effacement des données mémorisées précedemment ;
|
||||||
|
|
||||||
|
* d'héberger l'application sur une autre adresse que https://media.interieur.gouv.fr ;
|
||||||
|
|
||||||
|
* dans le cas d'un maintien de la fonctionnalité de mémorisation, de supprimer la couche de chiffrement qui ne sert à rien d'autre qu'à induire un faux sentiment de sécurité ;
|
||||||
|
|
||||||
|
* de renforcer la sécurité de son site web (détail technique plus bas).
|
||||||
|
|
||||||
|
Une voie alternative pour le Ministère de l'Intérieur serait de fournir une application mobile en sources ouvertes dédiée à la génération d'attestations. TousAntiCovid ne répond pas à ce besoin, car elle effectue de nombreuses autres missions nécessitant des droits supplémentaires sur le téléphone.
|
||||||
|
|
||||||
|
|
||||||
|
Présentation succincte de l'application d'émission d'attestation
|
||||||
|
----------------------------------------------------------------
|
||||||
|
|
||||||
|
Pour le second confinement de 2020, le Ministère de l'Intérieur a publié une application en ligne pour l'émission d'attestation de déplacement dérogatoire COVID-19 à l'adresse : https://media.interieur.gouv.fr/deplacement-covid-19/
|
||||||
|
|
||||||
|
Cette application a subi notamment une mise à jour afin de permettre de mémoriser les informations saisies dans le formulaire, afin d'éviter d'avoir à retaper tous les renseignements à chaque nouvelle visite. Ce mécanisme est au coeur des risques évoqués dans cet article.
|
||||||
|
|
||||||
|
L'application fonctionne à l'aide d'un formulaire dans lequel les citoyens saississent leurs informations d'état civil, ainsi que le motif de sortie, et la date et heure. Lors du clic sur le bouton de génération, un fichier PDF est téléchargé. Ce fichier est un formulaire vierge. Il est completé par le navigateur (et le code téléchargé depuis le site du Ministère) grâce aux informations fournies dans le formulaire. Si la case "mon téléphone se souvient de moi" est cochée, alors les informations saisies dans le formulaire sont enregistrées dans le navigateur, grâce à la fonctionnalité localStorage. Elles ne sont pas transmises au Ministère lors de cette opération.
|
||||||
|
|
||||||
|
Lors du rechargement de la page, le localStorage est consulté afin d'auto-compléter le formulaire.
|
||||||
|
|
||||||
|
Les informations sont stockées sous une forme de document JSON chiffré à l'aide [secure-ls](https://www.npmjs.com/package/secure-ls). Secure-ls est un module Javascript permettant de chiffrer les données dans le localStorage afin d'en éviter le vol. Outre le fait que les algorithmes cryptographiques déployés par secure-ls sont pour la plupart obsolètes (MD5, SHA-1, 3DES, PBKDF2, RC4...), et que l'implémentation contienne des versions Javascript de ces algorithmes alors qu'il existe [WebCrypto](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API), le problème de l'implémentation du Ministère de l'Intérieur est le suivant : le secret permettant de chiffrer et de déchiffrer les informations dans le localStorage est hardcodé dans le code source de la page. Le voici :
|
||||||
|
|
||||||
|
```
|
||||||
|
s3cr3t$#@135^&*246
|
||||||
|
```
|
||||||
|
|
||||||
|
Autant dire que cette couche de chiffrement ne sert strictement à rien, et n'est là que pour donner une fausse impression de sécurité... ou pour cacher ce que contient le localStorage aux yeux des curieux.
|
||||||
|
|
||||||
|
Il se trouve que le localStorage ne contient rien de bien excitant : une entrée `_secure__ls__metadata` qui contient un document JSON listant les autres entrées "sécurisées" du localStorage et leurs clés de chiffrement :
|
||||||
|
|
||||||
|
```
|
||||||
|
{"keys":[{"k":"profile","s":"7bebe7af384395d2ec6d383ac5380c4f"}]}
|
||||||
|
```
|
||||||
|
|
||||||
|
La clé "profile" contient l'état civil et des informations étrangement redondantes :
|
||||||
|
|
||||||
|
```
|
||||||
|
{
|
||||||
|
"firstname": "Camille",
|
||||||
|
"lastname": "Dupont",
|
||||||
|
"birthday": "01/01/1970",
|
||||||
|
"placeofbirth": "Paris",
|
||||||
|
"address": "999 avenue de France",
|
||||||
|
"city": "Paris",
|
||||||
|
"zipcode": "75001",
|
||||||
|
"datesortie": "24/11/2020",
|
||||||
|
"heuresortie": "18:00",
|
||||||
|
"ox-travail": "travail",
|
||||||
|
"ox-achats": "achats",
|
||||||
|
"ox-sante": "sante",
|
||||||
|
"ox-famille": "famille",
|
||||||
|
"ox-handicap": "handicap",
|
||||||
|
"ox-sport_animaux": "sport_animaux",
|
||||||
|
"ox-convocation": "convocation",
|
||||||
|
"ox-missions": "missions",
|
||||||
|
"ox-enfants": "enfants"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
À première vue, l'application semble légitime et conforme à sa mission, faire de son mieux pour que les données ne fuitent pas vers le Ministère de l'Intérieur, et même ce qui semble "caché" sous du chiffrement médiocre est en fait sans surprise ni risque.
|
||||||
|
|
||||||
|
Quel est donc le problème décrit par cet article ?
|
||||||
|
|
||||||
|
|
||||||
|
Isolation des sites web
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Le web est un milieu dangereux ; s'y cotoient de nombreuses applications web (autrefois appelées sites, mais le terme de site web semble bien réducteur, compte tenu de la quantité de code que chaque site fait exécuter à un navigateur ; ce sont bien des applications à part entière), d'origines diverses et d'innocuité variable. Il est possible de consulter simultanément votre situation fiscale et Facebook, et il serait un scandale si Facebook pouvait décortiquer vos sources de revenus, ou si le site des Impôts pouvait analyser votre graph social.
|
||||||
|
|
||||||
|
Pour parrer à ce type de porosité, très tôt dans l'histoire du Web, une politique de sécurité appelée Same Origin Policy (SOP) a été introduite. Cette politique est automatiquement appliquée par tous les navigateurs web. Son principe est l'isolation des applications web en fonction du schéma/protocole (http ou https), du nom de domaine et du numéro de port depuis lequel elles ont été téléchargées. Ainsi http://example.com, http://exemple.net, http://broken-by-design.fr, https://broken-by-design.fr, https://www.broken-by-design.fr et https://www.broken-by-design.fr:8080 sont toutes des applications séparées.
|
||||||
|
|
||||||
|
Des applications web isolées par la SOP ne peuvent ni consulter ce qui est affiché par une autre application, ni consulter leurs localStorage respectifs.
|
||||||
|
|
||||||
|
Il est ainsi impossible pour Facebook de consulter l'état civil stocké par l'application de génération des attestations COVID-19, par exemple.
|
||||||
|
|
||||||
|
Techniques de pistage des utilisateurs
|
||||||
|
--------------------------------------
|
||||||
|
|
||||||
|
Les techniques de pistage des utilisateurs sont très nombreuses. Parmi ces dernières, les cookies sont un outil redoutable.
|
||||||
|
|
||||||
|
Visiter un site A, puis visiter un site B. Les deux sites utilisent les services d'un troisième site, C. Le site B peut alors afficher des publicités en rapport avec la visite du site A. Cela est rendu possible, car C a obtenu des informations sur la navigation de l'utilisateur lors de son passage sur le site A, et a utilisé des cookies pour en prendre note. Lors de la visite sur le site B, les cookies déposés lors de la visite du site A sont automatiquement envoyés à C, et C peut agit en conséquence en proposant de la publicité personnalisée.
|
||||||
|
|
||||||
|
Les cookies ne sont qu'un des nombreux moyens à la disposition des régies publicitaires. Ils sont les plus pratiques car les cookies usent d'une politique de sécurité moins forte que la Same Origin Policy discutée précedemment. Mais il est possible de faire virtuellement la même chose avec localStorage, l'outil de stockage également employé par le site de génération des attestations COVID-19 du Ministère de l'Intérieur. Le localStorage est légèrement plus sécurisé (c'est-à-dire moins pratique pour les régies), mais il a le mérite d'avoir une durée de vie potentiellement illimitée, et de passer un peu plus sous le radar juridique.
|
||||||
|
|
||||||
|
Pistage des citoyens
|
||||||
|
---------------------
|
||||||
|
|
||||||
|
Les lecteurs les plus perspicaces auront déjà fait 1 + 1.
|
||||||
|
|
||||||
|
Le citoyen soucieux de ne pas être sanctionné en cas d'absence d'attestation de déplacements dérogatoires COVID-19 voudra générer ses attestations quotidiennes le plus facilement et rapidement possible. Il va donc utiliser l'application web du Ministère de l'Intérieur sur son téléphone portable, et mémoriser ses informations. Ce faisant, il aura stocké son état civil durablement dans le localStorage associé à https://media.interieur.gouv.fr/.
|
||||||
|
|
||||||
|
Ce même utilisateur pourra ensuite continuer de naviguer sur ses sites habituels. Tout site contenant une sous-page (iframe) téléchargée depuis https://media.interieur.gouv.fr, qu'elle soit visible ou non, permettra potentiellement au Ministère de l'Intérieur d'associer cette visite à un état civil. Il s'agit d'un pistage nominatif, et non plus d'un pistage sur la base de pseudonymes (comme des identifiants numériques arbitraires ou des adresses IP).
|
||||||
|
|
||||||
|
Mettons que l'on veuille préremplir un formulaire sur service-public.fr ; il suffit d'inclure une sous-page téléchargée depuis https://media.interieur.gouv.fr et boom : service-public.fr peut collaborer avec media.interieur.gouv.fr pour obtenir les informations d'état civil ! (Par exemple, avec une technique d'affaiblissement de la SOP nommée [message passing](https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage), qui permet à des onglets de communiquer entre eux par messages.)
|
||||||
|
|
||||||
|
Mettons que l'on veuille identifier les bons citoyens qui se rendent régulièrement sur le site gouvernement.fr : boom, même technique.
|
||||||
|
|
||||||
|
Mettons qu'un service de renseignement peu scrupuleux des limites du droit français souhaite identifier les utilisateurs d'un site peu recommandable. Plusieurs options sont à sa disposition.
|
||||||
|
|
||||||
|
La première possibilité serait l'exploitation d'une vulnérabilité de XSS (Cross Site Scripting) qui serait présente sur ce site. Cette vulnérabilité permet d'ajouter du code illégitime dans une page d'un site web. Les XSS font partie des vulnérabilités [les plus communes du web](https://owasp.org/www-project-top-ten/) ; cela ne semble donc pas improbable. Grâce à cette faille, il est possible d'ajouter une sous-page de https://media.interieur.gouv.fr sur le site ciblé et boom : citoyen Camille Dupont a visité un site interdit !
|
||||||
|
|
||||||
|
Une seconde solution pour ce service de renseignement, si le site peu recommandable est servi en HTTP : pratiquer l'attaque de l'homme du milieu (MITM). Cette attaque permet également d'injecter du code dans une page du site web.
|
||||||
|
|
||||||
|
Finalement, il existe une technique pour associer un citoyen à une visite sur un site ciblé qui contourne toutes les contremesures (connues des auteurs de cet article) qui ont été déployées notamment à l'encontre des régies publicitaires, y compris [First Party Isolation de Mozilla](https://wiki.mozilla.org/Security/FirstPartyIsolation), et son équivalent dans le Tor Browser. Cette technique utilise la redirection HTTP et une page web qui pourrait être hébergée sur https://media.interieur.gouv.fr. Cette page web offrirait volontairement une vulnérabilité de type [Open Redirect](https://cwe.mitre.org/data/definitions/601.html) : en clair, dès que le Javascript de cette page est chargé, il enregistrerait les paramètres de la query string ainsi que l'état civil de l'utilisateur, puis effectuerait une redirection du navigateur (top level navigation) vers l'adresse du site web peu recommandable indiquée dans cette query string.
|
||||||
|
|
||||||
|
Délibérée ou accidentelle, cette fonctionnalité est dangereuse pour les libertés des citoyens français.
|
||||||
|
|
||||||
|
Pour les plus sceptiques, [un ensemble de sites de démonstration](https://covid.broken-by-design.fr/attestation.html) ont été mis en place, qui simulent ce que le Ministère de l'Intérieur peut faire.
|
||||||
|
|
||||||
|
Comment s'en protéger ?
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
Une application mobile en sources ouvertes dédiée à la génération d'attestation est une voie intéressante : elle serait immunisée aux attaques web. La recommandation n'est cependant pas d'utiliser TousAntiCovid. [Son modèle est voué à l'échec](https://www.franceculture.fr/emissions/superfail/stopcovid-encore-une-appli-qui-a-plante) et [présente des risques d'atteinte à la vie privée](https://risques-tracage.fr). De plus, TousAntiCovid accomplit de nombreuses autres missions qui nécessitent des privilèges supplémentaires, dont la connexion au réseau.
|
||||||
|
|
||||||
|
Pour se protéger des risques de dévoiement du site de génération de l'attestation COVID-19, il n'existe aucune panacée au meilleur de la connaissance des auteurs de cet article. Il est donc recommandé :
|
||||||
|
|
||||||
|
* de ne pas enregistrer ses données en laissant décoché "Mon téléphone se souvient de moi" ou
|
||||||
|
|
||||||
|
* d'éviter le risque en générant ses attestations autrement (sur papier ou avec des applications alternatives ne reposant par sur la sécurité des navigateurs web).
|
||||||
|
|
||||||
|
Enfin, si vous avez utilisé la fonction de mémorisation des données personnelles par le passé, il convient d'effacer toutes les données stockées par le site https://media.interieur.gouv.fr. Il est préférable de le faire en utilisant la fonctionnalité de nettoyage de l'historique du navigateur (en cochant bien la case d'effacement des données stockées par le site), plutôt que d'utiliser le bouton prévu à cet effet dans l'application web de génération des attestations (bien que celle-ci agisse correctement à l'heure de l'écriture de cet article).
|
||||||
|
|
||||||
|
Recommandations au Ministère de l'Intérieur
|
||||||
|
-------------------------------------------
|
||||||
|
|
||||||
|
Les recommandations principales sont de retirer la fonctionnalité de mémorisation de l'état civil, et d'ajouter un nettoyage automatique de ces données si elles ont été mémorisées par le passé.
|
||||||
|
|
||||||
|
L'usage de l'origine https://media.interieur.gouv.fr est un choix médiocre pour plusieurs raisons :
|
||||||
|
|
||||||
|
* de nombreuses ressources sont servies par cette origine ; la compromission d'une seule de ces ressources entraine la fuite des données personnelles (état civil) des citoyens français. L'isoler sur une origine dédié est une tâche prioritaire.
|
||||||
|
* media.interieur.gouv.fr peut trop facilement co-héberger une application délibérément utilisée pour traquer les utilisateurs, tout en prétendant avoir un objectif légitime ; si un site web tiers héberge une sous-page servie par https://media.interieur.gouv.fr, il ne fera pas immédiatement le rapport avec le risque de pistage des citoyens. En utilisant une autre origine dédiée (https://attestation-covid.media.interieur.gouv.fr, par exemple), vous faîtes preuve de transparence et vous permettez à vos partenaires et vos citoyens de vérifier/douter d'une sous-page que vous voudriez qu'ils ajoutent à votre site web.
|
||||||
|
|
||||||
|
En outre, les entêtes de sécurité HTTP employés pour servir le site web de génération des attestations COVID-19 sont strictement insuffisants. Vous employez à la date d'écriture de cet article `X-XSS-Protection` [qui est obsolète](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-XSS-Protection), HSTS (de manière adéquate, bravo), `x-content-type-options`. Les auteurs de cet article recommandent l'ajout des entêtes suivants :
|
||||||
|
|
||||||
|
* `Content-Security-Policy` (dont frame-ancestors 'none', script-src et default-src), afin de prévenir l'inclusion dans une sous-page et de charger du contenu malveillant en cas de XSS qui ne serait pas bloquée par `X-XSS-protection` qui est obsolète ;
|
||||||
|
* `X-Frame-Options: deny` afin de prévenir l'inclusion dans des sous-pages ;
|
||||||
|
* `Cross-Origin-Opener-Policy: same-origin` afin de prévenir la fuite d'information via l'ouverture de pop-ups par un attaquant (Spectre, etc.) ;
|
||||||
|
* `Cross-Origin-Resource-Policy: same-site` afin de prévenir l'inclusion de ressources de votre site sur un site tiers ; cela évitera que le secret hardcodé dans votre code source soit volable de manière automatisée ;
|
||||||
|
* `Cross-Origin-Embedder-Policy: require-corp` afin d'imposer l'usage de Cross-Origin-Resource-Policy
|
||||||
|
|
||||||
|
Enfin, si vous tenez absolument à maintenir la fonctionnalité de mémorisation, il est recommandable de retirer la couche de chiffrement ls-secure qui est parfaitement inutile et non conforme au [Référentiel Général de Sécurité (RGS) - Annexe B1](https://www.ssi.gouv.fr/uploads/2015/01/RGS_v-2-0_B1.pdf).
|
||||||
|
|
105
posts/gestes-barrieres.md
Normal file
105
posts/gestes-barrieres.md
Normal file
|
@ -0,0 +1,105 @@
|
||||||
|
---
|
||||||
|
title: "7 gestes barrières contre le pass sanitaire et pour préserver sa vie privée"
|
||||||
|
slug: gestes-barrieres-pass-sanitaire
|
||||||
|
authors: "Florian Maury"
|
||||||
|
description: "Une série de gestes barrières visant à lutter activement contre le pass sanitaire, et à préserver la confidentialité de ses données personnelles et médicales contenues dans le pass sanitaire"
|
||||||
|
date: 2021-07-29T00:00:00+02:00
|
||||||
|
type: posts
|
||||||
|
draft: false
|
||||||
|
layout: "singletoc"
|
||||||
|
categories:
|
||||||
|
- france
|
||||||
|
tags:
|
||||||
|
- privacy
|
||||||
|
- human rights
|
||||||
|
- pass sanitaire
|
||||||
|
- covid19
|
||||||
|
---
|
||||||
|
|
||||||
|
Avertissement : je ne suis pas juriste. Tous les points listés ci-dessous me semblent légaux, au meilleur de mes connaissances actuelles, mais vous prenez vos responsabilités personnelles en les appliquant. Ce document ne constitue pas un conseil juridique.
|
||||||
|
|
||||||
|
## 1. Stratégie d’évitement : boycotter les lieux qui exigent le pass sanitaire
|
||||||
|
|
||||||
|
Le premier geste barrière est d'adopter une stratégie d'évitemment de l'emploi du pass sanitaire. En boycottant les lieux qui l'exigent, on créé une pression que nos gouvernants, adeptes du néolibéralisme, comprennent. Boycott = pas de revenu. Alors évidemment, certains diront que la stratégie d'évitemment est la seule option des non-vaccinés. Ce n'est pas faux. Mais c'est aussi une stratégie qui peut, et à mon sens doit, être entreprise par les vaccinés, par solidarité, et pour rejeter cette société de controle permanent qui nous est proposée. Nous sommes des citoyens, et nous méritons la liberté de choix, en conscience, et l'égalité en droit.
|
||||||
|
|
||||||
|
## 2. Stratégie d'obstruction : maximiser le temps requis pour faire le contrôle du pass sanitaire, sans toutefois refuser
|
||||||
|
|
||||||
|
Le deuxième geste barrière est à appliquer lorsque la stratégie d'évitemment n'est pas possible ou n'est pas appropriée. L'objectif reste le même : maximiser les pertes de profit, seul argument compris par les néolibéraux. Ce geste consiste à maximiser le temps requis pour qu'un controleur vérifie le pass sanitaire.
|
||||||
|
|
||||||
|
Même 10 secondes, multipliées par le nombre de personnes qui ont à présenter le pass sanitaire, cela créé des retards significatifs et un impact sur l'activité économique.
|
||||||
|
|
||||||
|
N'hésitez pas à présenter des pass sanitaires de type tests (PCR ou antigéniques) périmés ("oh, je me suis trompé ; le voici"), à baisser la luminosité du téléphone pour gêner la lecture du code barre. Galérez à trouver le bouton pour l'augmenter. Présentez une photocopie chiffonnée.
|
||||||
|
|
||||||
|
Tout ce qui montre que vous êtes en train de vous plier au contrôle mais qui cause un délai est bon à appliquer. En revanche, ne soyez pas agressif, ou violent, et ne refusez pas de présenter le pass, à moins d'appliquer au final la stratégie d'évitemment. Les contrôleurs suivent les instructions du gouvernement et préfèreraient sûrement faire le vrai métier. Blamez les vrais coupables du pass sanitaire.
|
||||||
|
|
||||||
|
|
||||||
|
## 3. Ne jamais diffuser son pass sanitaire, même partiellement masqué à des personnes n'ayant pas le besoin d'en connaitre
|
||||||
|
|
||||||
|
Votre pass sanitaire contient de nombreuses données personnelles et de santé. Il contient notamment, vos noms, prénoms, date de naissance. En outre la nature des données médicales qu'il contient dépend du type de pass que vous détenez. Pour un pass de type vaccinal, il contient des informations générales comme le nombre de doses reçues, le nombre de doses requises pour être protégé et un indicateur de complétion de votre parcours vaccinal. En outre, il contient le nom, la marque, l'agent prophylactique et la date d'injection du dernier vaccin reçu.
|
||||||
|
|
||||||
|
Ces informations sont lisibles par n'importe qui qui peut visualiser votre code barre et qui dispose d'un logiciel dédié. Certains sont librement accessibles sur le net. Cette [petite vidéo](https://peertube.stream/w/chiP7CT37rwQJoyNuD3HMp) vous en convainvra.
|
||||||
|
|
||||||
|
En outre, les code barres 2D (datamatrix et qrcode) contiennent des codes correcteurs d'erreurs. Un code correcteur d'erreurs, c'est un outil mathématique qui vise à compenser la destruction partielle du code barre. Dans le cas des qrcodes, c'est le même code correcteur d'erreurs que celui qui est utilisé sur les CD, pour compenser les micro-rayures, par exemple. Dissimuler une partie de votre code barre peut ne pas être suffisant pour empecher la lecture complète du code, et des données qu'il contient.
|
||||||
|
|
||||||
|
Finalement, la diffusion de son pass sanitaire avec l'intention d'encourager son emploi par un tiers est puni selon le code pénal de 75 000 € d'amende et de 5 ans de prison.
|
||||||
|
|
||||||
|
Le troisième geste barrière est donc de ne pas diffuser son pass sanitaire.
|
||||||
|
|
||||||
|
## 4. Privilégier le papier
|
||||||
|
|
||||||
|
Ce quatrième geste barrière consiste à ne pas numériser son pass sanitaire afin de limiter les risques de sa diffusion. Utiliser le pass sanitaire au format papier limite sa diffusion aux moments où vous sortez le pass de votre poche.
|
||||||
|
|
||||||
|
Si votre poche n'est pas un endroit sûr (?), alors un téléphone avec un code d'accès fort peut éventuellement mieux le protéger. Notez bien que vous n'avez pas besoin d'installer TousAntiCovid sur votre téléphone pour numériser le pass sanitaire. TousAntiCovid ne présente aucun intérêt particulier pour la numérisation du pass sanitaire. Une simple photo suffit ! Attention cependant à ne pas la stocker dans un dossier synchronisé avec le cloud (e.g. Google Photos). Si vous êtes d'humeur très prudente, il existe des logiciels qui permettent de stocker les photos de manière chiffrée sur votre téléphone. Rien que s'envoyer à soi-même (pléonasme pour insister sur le fait de ne pas communiquer son pass à un tiers) la photo avec un logiciel de messagerie comme Signal peut faire l'affaire.
|
||||||
|
|
||||||
|
## 5. Vérifier le logiciel utilisé par le controleur
|
||||||
|
|
||||||
|
Comme détaillé plus haut, votre pass sanitaire contient de nombreuses données personnelles et médicales. Or, vous devez les présenter à des inconnus. Comment leur faire confiance pour qu'ils ne stockent pas ces données à des fins commerciales, marketing, ou statistiques. La loi les en défend, mais si personne ne vérifie, il y a fort à parier que cela sera fait. Contrôlez les controleurs !
|
||||||
|
|
||||||
|
Ce cinquième geste barrière consiste à exiger du controleur qu'il lance l'application de vérification (par exemple TousAntiCovid Verif) depuis l'app store, devant vos yeux. Si le controleur refuse, insistez (stratégie d'obstruction). Finalement, en cas de refus ferme, appliquez la stratégie d'évitemment : il en va de la sécurité de vos données personnelles et médicales.
|
||||||
|
|
||||||
|
## 6. Refuser la vérification d'identité par les personnes non habilitées
|
||||||
|
|
||||||
|
Le contrôle d'identité est un acte qui ne peut être effectué que par certains membres des forces de l'ordre. Aucun contrôleur de pass sanitaire employé par le secteur privé, ni même certains agents des forces de l'ordre ne sont habilités à contrôler votre identité.
|
||||||
|
|
||||||
|
En conséquence, ce sixième geste barrière consister à refuser de présenter sa carte d'identité ou tout autre justificatif d'identité à ces personnes. Appliquez la stratégie d'obstruction en cas de refus, et faites intervenir les forces de l'ordre.
|
||||||
|
|
||||||
|
Lors d'un contrôle d'identité par les forces de l'ordre, privilégiez la preuve d'identité par témoignage, si vous êtes dans un groupe d'amis, plutôt que de présenter vos papiers d'identité. C'est peu connu, mais des personnes attestant de votre identité sont suffisantes pour satisfaire un contrôle d'identité. Il n'y a pas besoin de présenter des titres émis par l'État.
|
||||||
|
|
||||||
|
## 7. Ne pas convertir son pass avec TousAntiCovid
|
||||||
|
|
||||||
|
**EDIT** du 4 août 2021 : ce geste barrière n'est plus nécessaire. La version 3.6.0 de TousAntiCovid introduit un mécanisme de protection des données pendant leur transfert, préservant leur confidentialité lors de leur passage via les serveurs américains.
|
||||||
|
|
||||||
|
----
|
||||||
|
L'application TousAntiCovid permet la conversion des pass sanitaires français, au format 2D-DOC (datamatrix) en pass européens, de type DCC (Digital Covid Certificate). Cette conversion s'effectue par l'envoi du pass sanitaire à un serveur central, via des serveurs américains, qui peuvent voir le contenu du pass sanitaire dans son intégralité.
|
||||||
|
|
||||||
|
Ce septième geste barrière consiste donc à ne pas utiliser TousAntiCovid pour effectuer cette conversion. À la place, rendez vous sur Ameli.fr pour télécharger votre pass au format européen, ou rendez vous dans un centre de vaccination ou une pharmacie.
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
Je doute qu'il y a ait un jour une chanson de Mc Fly et Carlito pour diffuser ces gestes barrières, mais je vous encourage à les faire connaitre. N'hésitez pas à redistribuer cet article. Vous pouvez le republier, le commenter, en faire des travaux dérivés. Sa licence est CC-0.
|
||||||
|
|
||||||
|
Si vous souhaitez me contacter, je suis disponible sur [Twitter](https://twitter.com/x_cli_public) ou le [Fédiverse](https://infosec.exchange/@x_cli).
|
||||||
|
|
||||||
|
Merci à toutse de m'avoir lu.
|
||||||
|
|
||||||
|
Vive la Nation !
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
## BONUS : gestes barrières relatifs à TousAntiCovid Signal
|
||||||
|
|
||||||
|
### Limiter la diffusion d'informations personnelles dans les carnets de rappel (TousAntiCovid Signal)
|
||||||
|
|
||||||
|
Lorsque l'on se rend dans certains lieux, il est requis de s'enregistrer soit avec l'application TousAntiCovid en scannant un QRCode de l'établissement, soit en ajoutant ses informations de contact dans un carnet de rappel papier.
|
||||||
|
|
||||||
|
Dans le carnet de rappel papier, il est demandé de renseigner son nom, prénom, et une information de contact (généralement le numéro de téléphone).
|
||||||
|
|
||||||
|
Ce geste barrière consiste à donner de faux noms et prénoms, car ils ne sont pas utiles pour la finalité du traitement, et une adresse email à la place du numéro de téléphone. Cette adresse email peut être une adresse email temporaire, comme celles proposées par le service Yopmail. Ainsi, vous restez contactables en cas de cluster, mais vous ne diffusez aucune information personnelle durable. Si la personne en charge de l'enregistrement dans le carnet de rappel refuse l'usage d'une adresse email, prétexter que vous n'avez pas de téléphone, ou que vous êtes sur un numéro temporaire car vous changez d'opérateur.
|
||||||
|
|
||||||
|
Utiliser ou non TousAntiCovid Signal avec prise de photo d'un QRCode est un sujet complexe en matière de respect de la vie privée ; il y a du pour et du contre. Il n'est donc pas possible de donner une recommandation universelle quant à son emploi ou non.
|
||||||
|
|
||||||
|
### Régénérer très fréquemment les QRCodes TousAntiCovid Signal
|
||||||
|
|
||||||
|
Si vous êtes gestionnaire de lieux devant afficher des QRCode TousAntiCovidSignal, ce geste barrière consiste à renouveller le plus fréquemment possible le QRCode que votre clientèle peut scanner. Un bon rythme pourrait être une fois par jour, à intégrer dans les gestes du quotidien lors de l'ouverture de l'établissement.
|
||||||
|
|
||||||
|
Changer le code régulièrement aide vos clients à préserver leur vie privée lorsqu'ils se déclarent malades. Cela limite la possibilité de désanonymiser le malade, et de tracer ses lieux de fréquentation d'une manière disproportionnée par rapport à la finalité de détection de clusters.
|
135
posts/large-file-decrypt-en.md
Normal file
135
posts/large-file-decrypt-en.md
Normal file
|
@ -0,0 +1,135 @@
|
||||||
|
---
|
||||||
|
title: "Secure large file decryption using Linux, Go and Nacl"
|
||||||
|
slug: large-file-decrypt-en
|
||||||
|
description: "This article describes strategies than can be used to decrypt securely large files that do not fit in RAM, thanks to Linux."
|
||||||
|
date: 2022-01-11T00:00:00+00:00
|
||||||
|
author: Florian Maury
|
||||||
|
type: posts
|
||||||
|
draft: false
|
||||||
|
categories:
|
||||||
|
- security
|
||||||
|
tags:
|
||||||
|
- Cryptography
|
||||||
|
- Linux
|
||||||
|
---
|
||||||
|
|
||||||
|
It is often said that you should not create your own cryptographic algorithm. That in cryptography, one should not be original; that security is in the beaten track, and in particular those which are beaten by cryptographers. What is often forgotten is that using cryptography is also perilous. In this article we will discuss how to handle large files when decrypting, and how to do it securely on a Linux system. This article will be illustrated with Go code.
|
||||||
|
|
||||||
|
## What is a "large file" and how is it different from "small files"?
|
||||||
|
|
||||||
|
A small file is anything that is reasonable to store in RAM, in its entirety. RAM is a sanctuary where a program can store information. This sanctuary offers a relatively strong guarantee of isolation from interference from other tasks/processes.
|
||||||
|
|
||||||
|
Conversely, a large file is a file that cannot be stored in its entirety in the RAM of the system that will decrypt it. Therefore, interferences can occur. They are of several kinds.
|
||||||
|
|
||||||
|
### Modifications of the encrypted data
|
||||||
|
|
||||||
|
The first problem is that the encrypted data can be modified by an external program. Indeed, as soon as a file is visible in a directory, any program running with the necessary privileges can open a file descriptor on this file. Thanks to this file descriptor, it is then possible to modify the file. This includes programs running with the same privileges as the program that is in charge of decryption.
|
||||||
|
|
||||||
|
This is not necessarily a problem. The state of the art in applied cryptography is to use authenticated encryption. This type of encryption verifies the cryptographic integrity of the encrypted content, while it decrypts it. Some algorithms and encryption modes, such as AES-GCM or AES-OCB, perform these operations in "one pass". That is to say that the authenticity check is performed as the decryption proceeds and an authenticity verdict is given at the end of the decryption. Alas, not all encryption modes are one-pass; thus AES-CCM, for example, will first perform an integrity check (first pass) and then perform decryption (second pass) if the data was initially determined to be authentic. Unfortunately, an attacker is then able to alter the data between the first and second pass. The decrypted content is then declared authentic, even though it has been altered.
|
||||||
|
|
||||||
|
Consequently, when a one-pass decryption mode is not used, it is necessary to use a private copy of the data, either in RAM or by other system tricks that will be detailed later in this document.
|
||||||
|
|
||||||
|
### Early use of data whose authenticity is not yet assured
|
||||||
|
|
||||||
|
When the decrypted data is too large to be stored in RAM, it is necessary to write it to disk. Unfortunately, security problems can occur during this passage on disk.
|
||||||
|
|
||||||
|
The list of these problems cannot be exhaustive, but it is possible to think of certain software that would use the data in an anticipated way. Indeed, some software monitors the contents of a folder with [inotify(7)](https://man7.org/linux/man-pages/man7/inotify.7.html) ; this is the case of most file explorers.
|
||||||
|
|
||||||
|
This early reading can result in incorrect interpretations of the file. This is not the most unfortunate consequence, however. In the case of one-pass encryption, or integrity checking after decryption (see the OpenPGP section of this document), it is possible that the file containing the decrypted file contains malicious code added by an attacker who corrupted the encrypted document. It is indeed very important to understand that encryption alone is not sufficient to guarantee the integrity of a data, and that it is necessary to use an integrity pattern, and to verify it, before using the decrypted file. If the decrypted data is used before the authenticity verdict is given, then it is possible to exploit a vulnerability with a non-authentic document.
|
||||||
|
|
||||||
|
Therefore, it is essential that the decrypted data remains private until the authenticity verdict is received. When it is stored in RAM, this is easy, but when it must be stored on disk, in the case of large files, then it is necessary to exploit some defensive strategies, discussed later in this document.
|
||||||
|
|
||||||
|
## Size reduction by fragmentation
|
||||||
|
|
||||||
|
One possible strategy for encrypting/decrypting a large file may be to break it up into chunks that will fit in RAM.
|
||||||
|
|
||||||
|
It might be tempting to look at encryption modes used for encrypting large amounts of data, like XTS. In a nutshell, XTS performs encryption/decryption using a single key, but it uses an encryption process that is "tweaked" to each logical unit. XTS is often used for disk encryption, and in this case the logical unit is the disk "sector". Under Linux, it is possible to use XTS with dm-crypt and its LUKS overlay.
|
||||||
|
|
||||||
|
XTS presents a rather interesting advantage for disk encryption. Indeed, the tweak for each logical unit is an intrinsic information about the disk: the position of the sector on the disk. This subtlety means that it is not necessary to store additional data (the tweak) for each sector. So there is no storage expense for encryption related data!
|
||||||
|
|
||||||
|
Unfortunately, XTS offers limited integrity protection. Of course, if you take an encrypted sector and move it to another sector on the disk, the tweek of the algorithm will be incorrect. Indeed, if a sector is to be decrypted as sector X, and we try to decrypt it as sector Y, then the tweak will be incorrect, and the decryption will be meaningless. However, XTS does not protect against replacing sector X with an earlier version of sector X, for example.
|
||||||
|
|
||||||
|
Moreover, by splitting the disk into encrypted sectors, each with a different tweak, there is no protection against truncation. If one takes a disk of size X and copies it to a disk of size Y, with Y < X, the cryptographic algorithm will not detect that data is missing.
|
||||||
|
|
||||||
|
From the errors or constraints of disk encryption modes, several lessons can be learned. If the data to be encrypted/decrypted is too large to be stored in RAM, and the solution is to split it into chunks, then care must be taken to ensure that the integrity of the chunks is strong.
|
||||||
|
|
||||||
|
This integrity must ensure that :
|
||||||
|
|
||||||
|
* each section cannot be modified individually, even by replacement with a section of another encrypted message of comparable size;
|
||||||
|
* the order of the sections cannot be modified;
|
||||||
|
* it is not possible to add or remove sections without the entire large file being considered invalid.
|
||||||
|
|
||||||
|
Problem number 1 can be easily solved by using a separate encryption/decryption key per encrypted file, in combination with an algorithm and encryption mode that results in an authenticated encryption, such as AES-GCM.
|
||||||
|
|
||||||
|
Problem 2 can be easily solved by adding a counter to each encrypted data block. This represents a storage overhead that can be paid for when we are talking about file encryption and not disk encryption.
|
||||||
|
|
||||||
|
It might be possible to create a tweakable encryption mode that is also authenticated, for example by combining XTS and an HMAC. Alas, the consequence would be that the cryptographic operations would be in two passes (XTS then HMAC), which is a potentially unnecessary computational overhead if a better solution is available (and it is; see below :)).
|
||||||
|
|
||||||
|
Furthermore, XTS + HMAC would not protect against issue 3). Indeed, to counter 3), one method is to add the expected amount of chunks in the metadata of the encrypted file. This amount should be protected in integrity. This method is not original; it is used in the [Merkle-Damgård cryptographic construction](https://en.wikipedia.org/wiki/Merkle%E2%80%93Damg%C3%A5rd_construction), and is used in particular by the SHA hash algorithms.
|
||||||
|
|
||||||
|
All these additions are as many ways to make mistakes when performing the encryption and decryption steps. However, as stated in the chapter of this article, going off the beaten track is often synonymous with vulnerability.
|
||||||
|
|
||||||
|
Therefore, it would be better not to reinvent the wheel, and to use well-known cryptographic mechanisms and libraries to solve our large file problem.
|
||||||
|
|
||||||
|
## Cryptographic libraries
|
||||||
|
|
||||||
|
In Go, there are various high-level cryptographic libraries that are frequently used. Here I will talk about OpenPGP, which is problematic, and NACL, which is to be preferred.
|
||||||
|
|
||||||
|
### OpenPGP
|
||||||
|
|
||||||
|
OpenPGP is a fairly [old encryption standard](https://www.rfc-editor.org/rfc/rfc4880.txt). Its main implementation is GnuPG, and it continues to be the hobby of some misguided technicians. Yes, I'm thinking in particular of you Linux distributions.
|
||||||
|
|
||||||
|
These harsh words against this format are however deserved. OpenPGP is a museum of horrors, full of antiquated mechanisms, and cryptographic constructs from the infancy of authenticated encryption. Also, and not least, its implementers seem to have a passion for bad API ideas. In fact, the author of this article discovered [problems in most OpenPGP implementations in 2015](https://www.ssi.gouv.fr/uploads/2015/05/format-Oracles-on-OpenPGP.pdf), and some, in 2022, are still vulnerable to these findings... including GnuPG.
|
||||||
|
|
||||||
|
In Go, unsurprisingly, the OpenPGP implementation also contains some bad ideas. The package has even been [frozen and deprecated](https://github.com/golang/go/issues/44226), with the comment that it is not desirable for Go developers to use OpenPGP, as this format is "complex, fragile, and unsafe, and using it exposes applications to a dangerous ecosystem". To make the point, we will study one of its problems.
|
||||||
|
|
||||||
|
While it is true that it is fairly universal for data sources to implement [io.Reader](https://pkg.go.dev/io#Reader), it is possible to question the relevance of this choice for an encrypted data source whose integrity can only be verified after a complete pass.
|
||||||
|
|
||||||
|
One might expect the OpenPGP container [openpgp.MessageDetails](https://pkg.go.dev/golang.org/x/crypto/openpgp#MessageDetails) to perform this check on its own when instantiated with [openpgp.ReadMessage](https://pkg.go.dev/golang.org/x/crypto/openpgp#ReadMessage). This would be quite consistent with the `encoding/gzip` API whose [NewReader](https://pkg.go.dev/compress/gzip#NewReader) function returns an error if there are no "magic" bytes at the beginning of the read. Alas, as said before, OpenPGP is a museum of horrors, and it is not possible to check the integrity of the encrypted document; it is necessary to decrypt the entire encrypted document first, to finally recover an integrity tag. Indeed, with the OpenPGP standard, the integrity tab (a simple SHA-1 of the cleartext) is part of the encrypted data, and is suffixed to the cleartext. This approach is called [MAC-then-encrypt](https://en.wikipedia.org/wiki/Authenticated_encryption#MAC-then-Encrypt_(MtE)) and is decried by the cryptographic community.
|
||||||
|
|
||||||
|
Although the `io.Reader` of `openpgp.MessageDetails` is stored in the aptly named `UnverifiedBody` field, it is extremely tempting for a developer to plug it into another `io.Reader`, like a series of decorators, and forget or discover too late that the message was not genuine!
|
||||||
|
|
||||||
|
### NACL
|
||||||
|
|
||||||
|
[NACL](https://pkg.go.dev/golang.org/x/crypto/nacl) is an excellent cryptographic library, whose well-designed API allows only the most stubborn of idiots to make mistakes in its use. There are some command line tools to exploit it or its fork [libsodium](https://github.com/jedisct1/libsodium). One of them is the excellent [minisign](https://github.com/jedisct1/minisign) utility, by Frank Denis. The author of this article highly recommends minisign as a replacement for OpenPGP for signing documents!
|
||||||
|
|
||||||
|
There are implementations of minisign in Go, such as [go-minisign](https://github.com/jedisct1/go-minisign), which unfortunately suffers from the same problem of handling large files that we are dealing with in this article. Fortunately it is possible to use go-minisign even for large files by using the tricks presented in this article, below.
|
||||||
|
|
||||||
|
Coming back to NACL, the [box.Seal](https://pkg.go.dev/golang.org/x/crypto@v0.0.0-20211215153901-e495a2d5b3d3/nacl/box#Seal) and [box.Open](https://pkg.go.dev/golang.org/x/crypto@v0.0.0-20211215153901-e495a2d5b3d3/nacl/box#Open) functions have the particularity of not reading from an `io.Reader` and not writing to an `io.Writer`. So they do not fall into the crude trap at the bottom of which we find OpenPGP. These functions use byte slices. This could look like a blocking point. This article aims precisely at proposing a solution to circumvent this particularity, while offering a correct level of security.
|
||||||
|
|
||||||
|
## System tips and tricks to the rescue
|
||||||
|
|
||||||
|
### Control the release of data
|
||||||
|
|
||||||
|
As seen at the beginning of this article, it is important to control when the decrypted data is released; until the data is complete and verified, the working copy of the data must remain private. Since we are dealing with large files, which do not fit in RAM, it is necessary to store the working copy on the file system, while ensuring that no other process or task can access it. To do this, it is possible to use anonymous files.
|
||||||
|
|
||||||
|
Anonymous files are files that are stored on the file system without any links to them. By link, here, it is necessary to understand link in the sense "entry in a directory": a hardlink. These files are created by specifying the `O_TMPFILE` option to syscall [open(2)](https://man7.org/linux/man-pages/man2/open.2.html). Any byte written to such a file is actually stored on the file system, via the file descriptor returned by open(2) and known only to the program that created it (and to the processes that will be poking around in /proc... but they are looking for trouble ;)). It is therefore a private copy of the decrypted file. When the file is complete and its content verified, it is then possible to publish it through different ways.
|
||||||
|
|
||||||
|
One way to publish the file in a not very elegant way is simply to create a new file, without the `O_TMPFILE` option, and then to copy the contents of the decrypted file into this new file which is accessible by the other processes. The file descriptor can then be closed, and the anonymous file will be automatically freed. This method is expensive and has the drawback of doubling the disk size needed to store the decrypted file, at least temporarily, until the file descriptor of the anonymous file is closed.
|
||||||
|
|
||||||
|
A more elegant way, which takes advantage of a feature that is not always available, is to use [FICLONE](https://man7.org/linux/man-pages/man2/ioctl_ficlone.2.html) of the syscall [ioctl(2)](https://man7.org/linux/man-pages/man2/ioctl.2.html). `FICLONE` uses the copy-on-write (COW) functionality of some file systems, such as [btrfs](https://btrfs.wiki.kernel.org/index.php/Main_Page). With this syscall, it is possible to open a file with a hardlink and then request that the named file be a snapshot of the anonymous file. The two files will then share the same blocks of data on the file system, until one of them changes a block. But in this case, there will be no subsequent writing to the anonymous file after this call to ioctl(2). So this is simply a trick to link to the contents of the anonymous file, and thus publish it. The only drawback of this approach is that you have to use a file system that is compatible with `FICLONE`, and this is not the case with ext4, which is usually the default file system of Linux distrubutions.
|
||||||
|
|
||||||
|
Finally, there is a third method, also elegant, which does not take advantage of a particular feature of some file systems. Unfortunately, it requires certain system privileges to do so: [CAP_DAC_READ_SEARCH](https://man7.org/linux/man-pages/man7/capabilities.7.html). `CAP_DAC_READ_SEARCH` bypasses this file system protection, which is unfortunate, because it is also the privilege required to call the [linkat(2)](https://man7.org/linux/man-pages/man2/linkat.2.html) syscall, with the `AT_EMPTY_PATH` option. This syscall together with this option allows the creation of a link from a file descriptor. It allows giving a name to our anonymous file, once it is complete. It may be acceptable to give `CAP_DAC_READ_SEARCH` to our process, if it is running in a chroot in which this permission does not allow the program to gain or keep undue access to system resources. This solution is therefore probably acceptable under certain conditions, which must however be well controlled.
|
||||||
|
|
||||||
|
## Decrypting a large file in virtual memory
|
||||||
|
|
||||||
|
Not everything that is in virtual memory is necessarily physical memory. Thus, it is possible to obtain a Go slice containing the contents of a file, without it being copied into RAM. In the same way, it is possible to write in a slice, which is not stored in RAM, thanks to a syscall: [mmap(2)](https://man7.org/linux/man-pages/man2/mmap.2.html). So we can call `box.Seal` and `box.Open` on such slices, and the result will have been computed without the content of the files being stored in RAM!
|
||||||
|
|
||||||
|
Unfortunately, things are never that "simple". There are some additional subtleties required when performing this write operation to a slice pointing to a file placed in virtual memory with mmap(2). Firstly, it is necessary that the destination file is the right size before calling mmap(2). To do this, a sparse file can be created, using the [fallocate(2)](https://man7.org/linux/man-pages/man1/fallocate.1.html) syscall. Then, once the writing to the slice is done, it is necessary to call the [msync(2)](https://man7.org/linux/man-pages/man2/msync.2.html) syscall in order to force the transfer of data from the virtual memory to the file, before making the call to munmap(2), to free the slice created by mmap(2).
|
||||||
|
|
||||||
|
Similarly, mmap(2) should used for the encrypted file, but there are some subtleties here as well. In particular, it is better to work on a private copy of the file, rather than one that can be altered by an external source. Indeed, the behavior is not specified if an external program truncates the file after it has been passed to mmap(2).
|
||||||
|
|
||||||
|
Finally, when passing the slice receiving the decrypted data to `box.Open`, one must pass this slice with [:0], in order to keep the capacity of the slice equal to the size of the file, but to force its length to 0. By doing this, `box.Open` will not proceed to reallocations of the array underlying the slice. It is indeed very important to use this trick, in order to continue working in the virtual memory returned by mmap(2) and not to end up working accidentally in RAM.
|
||||||
|
|
||||||
|
## Bringing it all together in a coherent program
|
||||||
|
|
||||||
|
To summarize everything that has been discussed in this article:
|
||||||
|
|
||||||
|
* when dealing with a large file, it is better to use Linux efficiently than to risk creating vulnerabilities by trying to truncate the file;
|
||||||
|
* it is important to always work with private copies of the data, both the encrypted and decrypted content;
|
||||||
|
* it is necessary to have control over the publication of the decrypted content, in particular to ensure that the content has integrity and is complete before making it available to third-party applications.
|
||||||
|
|
||||||
|
To accomplish these goals, it is possible to use the Linux syscalls mmap(2), fallocate(2), msync(2), and ioctl(2) or linkat(2).
|
||||||
|
|
||||||
|
[This git repository](https://github.com/X-Cli/large-file-decrypt) contains a library using all these elements to encrypt and decrypt a large file in a secure way.
|
||||||
|
|
141
posts/large-file-decrypt.md
Normal file
141
posts/large-file-decrypt.md
Normal file
|
@ -0,0 +1,141 @@
|
||||||
|
---
|
||||||
|
title: "Déchiffrement sécurisé de gros fichiers en Go"
|
||||||
|
slug: large-file-decrypt
|
||||||
|
description: "Cet article détaille comment déchiffrer de manière sécurisée de gros fichiers, ne tenant pas en RAM, grâce à Linux."
|
||||||
|
date: 2022-01-10T00:00:00+00:00
|
||||||
|
type: posts
|
||||||
|
draft: false
|
||||||
|
categories:
|
||||||
|
- security
|
||||||
|
tags:
|
||||||
|
- Cryptography
|
||||||
|
- Linux
|
||||||
|
---
|
||||||
|
|
||||||
|
L'on dit souvent qu'il ne faut pas créer son propre algorithme cryptographique. Qu'en cryptographie, il ne faut pas faire preuve d'originalité ; que la sécurité est dans les sentiers battus, et en particulier ceux qui le sont par les cryptographes. Ce qu'on oublie souvent de dire, c'est qu'utiliser la cryptographie est aussi périlleux. Dans cet article, nous allons discuter du traitement des gros fichiers lors du déchiffrement, et de comment le faire de manière sécurisée sur un système Linux. Cet article sera illustré par du code Go.
|
||||||
|
|
||||||
|
## Qu'est ce qu'un "gros fichier" et en quoi cela est-il différent des "petits fichiers" ?
|
||||||
|
|
||||||
|
Un petit fichier est tout ce qu'il est raisonnable de stocker en RAM, en intégralité. La RAM est un espace sanctuarisé où un programme peut stocker de l'information. Cette sanctuarisation offre avec une garantie relativement forte d'isolation contre les interférences provenant d'autres tâches/processus.
|
||||||
|
|
||||||
|
À l'inverse, un gros fichier est un fichier qui ne peut être stocké dans son intégralité dans la RAM du système qui va le déchiffrer. Dès lors des interférences peuvent survenir. Elles sont de plusieurs ordres.
|
||||||
|
|
||||||
|
### Les modifications de la donnée chiffrée
|
||||||
|
|
||||||
|
Le premier problème est que la donnée chiffrée peut être modifiée par un programme externe. En effet, dès lors qu'un fichier est présent dans un répertoire, tout programme tournant avec les privilèges nécessaires peut ouvrir un descripteur de fichiers (*file descriptor*) sur ce fichier. Grâce à ce descripteur de fichiers, il est alors possible de modifier le fichier. Cela inclut les programmes tournant avec le même niveau de privilèges que le programme qui est en charge du déchiffrement.
|
||||||
|
|
||||||
|
Ce n'est pas forcément un problème. L'état de l'art en matière de cryptographie appliquée est d'utiliser du chiffrement authentifié. Ce type de chiffrement vérifie l'intégrité cryptographique du contenu chiffré, en même temps qu'il le déchiffre. Certains algorithmes et modes de chiffrement, comme AES-GCM ou AES-OCB, effectuent ces opérations en "une passe". C'est-à-dire que la vérification de l'authenticité est effectuée au fur et à mesure du déchiffrement et un verdict d'authenticité est donné à la fin du déchiffrement. Hélas, tous les modes de chiffrement ne sont pas en une passe ; ainsi AES-CCM, par exemple, va d'abord effectuer une vérification en intégrité, puis effectuer le déchiffrement si la donnée a initialement été déterminée comme authentique. Hélas, un attaquant est alors en mesure d'altérer la donnée entre la première et la deuxième passe. Le contenu déchiffré est alors déclaré authentique, alors que ce dernier a été modifié.
|
||||||
|
|
||||||
|
En conséquence, lorsqu'un mode de déchiffrement en une passe n'est pas employé, il est nécessaire d'utiliser une copie privée de la donnée, que ce soit en RAM ou par d'autres astuces systèmes qui seront détaillées ultérieurement dans ce document.
|
||||||
|
|
||||||
|
### Utilisation anticipée d'une donnée dont l'authenticité n'est pas encore assurée
|
||||||
|
|
||||||
|
Lorsque les données déchiffrées sont trop grandes pour être contenues en RAM, il est nécessaire de les écrire sur disque. Hélas, des problèmes de sécurité peuvent survenir lors de ce passage sur disque.
|
||||||
|
|
||||||
|
La liste de ces problèmes ne saurait être exhaustive, mais il est possible de penser notamment à certains logiciels qui utiliseraient la donnée de manière anticipée. En effet, certains logiciels surveillent le contenu d'un dossier avec [inotify(7)](https://man7.org/linux/man-pages/man7/inotify.7.html) ; c'est le cas de la plupart des explorateurs de fichiers.
|
||||||
|
|
||||||
|
Cette lecture anticipée peut résulter en des interprétations incorrectes du fichier. Ce n'est cependant pas la conséquence la plus funeste. Dans le cas d'un chiffrement en une passe, ou d'une vérification d'intégrité effectuée après le déchiffrement (c.f. la section de ce document relative à OpenPGP), il est possible que le fichier contenant le déchiffré intègre du code malveillant ajouté par un attaquant ayant corrompu le document chiffré. Il est, en effet, capital de comprendre que le chiffrement seul n'est pas suffisant pour garantir l'intégrité d'une donnée, et qu'il est nécessaire d'utiliser un motif d'intégrité, et de le vérifier, avant de faire usage du déchiffré.
|
||||||
|
S'il est fait usage de la donnée déchiffrée avant que le verdict d'authenticité ne soit rendu, alors il est possible d'exploiter une vulnérabilité avec un document non-authentique.
|
||||||
|
|
||||||
|
En conséquence, il est indispensable que le déchiffré reste privé, jusqu'à réception du verdict d'authenticité. Lorsqu'il est stocké en RAM, c'est chose aisée, mais lorsqu'il doit être stocké sur le disque, dans le cas des gros fichiers, alors il est nécessaire d'exploiter quelques stratégies défensives, discutées plus bas dans ce document.
|
||||||
|
|
||||||
|
## Réduction de la taille par fragmentation
|
||||||
|
|
||||||
|
Une stratégie possible pour chiffrer/déchiffrer un gros fichier peut être de le découper en tronçons qui tiendront en RAM.
|
||||||
|
|
||||||
|
Il pourrait être tentant de regarder du côté des modes de chiffrement utilisés pour le chiffrement de vastes quantité de données, comme XTS. En quelques mots, XTS effectue un chiffrement/déchiffrement à l'aide d'une unique clé, mais d'un procédé de chiffrement qui est ajusté ("tweaked") à chaque unité logique. XTS est souvent employé pour le chiffrement de disques, et dans ce cas, l'unité logique est le "secteur" du disque. Sous Linux, il est possible de faire usage de XTS notamment avec dm-crypt et sa surcouche LUKS.
|
||||||
|
|
||||||
|
XTS présente un avantage assez intéressant pour le chiffrement de disques. En effet, l'élément qui permet de paramétrer l'ajustement (tweak) pour chaque unité logique est une donnée extérieure et intrinsèque du disque : la position du secteur sur le disque. Cette subtilité fait qu'il n'est pas nécessaire de stocker une donnée supplémentaire (les paramètres d'ajustement) pour chaque secteur. Il n'y a donc pas de dépense de stockage pour des données liées au chiffrement !
|
||||||
|
|
||||||
|
Hélas, XTS offre une protection en intégrité limitée. Certes, si l'on prend un secteur chiffré et qu'on le déplace dans un autre secteur du disque, les paramètres d'ajustement de l'algorithme vont être incorrects. En effet, si un secteur doit être déchiffré en tant que secteur X, et qu'on tente de le déchiffrer en tant que secteur Y, alors les paramètres de l'algorithme seront incorrects, et le déchiffré n'aura aucun sens. Cependant XTS ne protège pas contre le remplacement du secteur X par une version antérieure du secteur X, par exemple.
|
||||||
|
|
||||||
|
En outre, en découpant le disque en secteurs chiffrés, chacun avec un paramètre différent, il n'existe pas de protection contre la troncature. Si l'on prend un disque de taille X et qu'on le copie sur un disque de taille Y, avec Y < X, l'algorithme cryptographique ne détectera pas qu'il manque de la donnée.
|
||||||
|
|
||||||
|
Des erreurs ou contraintes des modes de chiffrement de disques, plusieurs leçons peuvent être tirées. Si l'on dispose d'une donnée à chiffrer/déchiffrer qui est trop grande pour être stockée en RAM, et que la solution envisagée est de découper cette donnée en tronçon, alors il faut veiller à mettre en place une intégrité forte des tronçons.
|
||||||
|
|
||||||
|
Cette intégrité doit assurer que :
|
||||||
|
|
||||||
|
1) chaque tronçon ne peut être modifié individuellement, même par remplacement avec un tronçon d'un autre message chiffré de taille comparable ;
|
||||||
|
2) l'ordre des tronçons ne peut être modifié ;
|
||||||
|
3) il n'est pas possible d'ajouter ou de retirer des tronçons sans que l'ensemble du gros fichier ne soit considéré comme invalide.
|
||||||
|
|
||||||
|
La problématique numéro 1 peut être aisément résolue en utilisant une clé de chiffrement/déchiffrement distincte par fichier chiffré, en combinaison avec un algorithme et un mode de chiffrement qui permette d'obtenir un chiffré authentifié, comme AES-GCM.
|
||||||
|
|
||||||
|
La problématique 2 peut être aisément résolue en ajoutant un compteur à chaque bloc de données chiffré. Cela représente un surcoût de stockage qu'il est possible de payer quand on parle de chiffrement de fichiers et non de chiffrement de disques.
|
||||||
|
|
||||||
|
Il pourrait être envisageable de créer un mode de chiffrement ajustable qui soit également authentifié, par exemple en combinant XTS et un HMAC. Hélas, la conséquence serait que les opérations cryptographiques seraient en deux passes (XTS puis HMAC), ce qui représente un surcoût potentiellement inutile si une meilleure solution est disponible (et c'est le cas ; voir ci-dessous :)).
|
||||||
|
|
||||||
|
En outre, XTS + HMAC ne protègerait pas contre la problématique 3). En effet, pour protéger contre cette dernière, une méthode est d'ajouter la quantité de tronçons attendue en meta-données du fichier. Cette quantité devrait être protégée en intégrité. Cette méthode n'est pas originale ; elle est utilisée dans la construction cryptographique [Merkle-Damgård](https://en.wikipedia.org/wiki/Merkle%E2%80%93Damg%C3%A5rd_construction), et est employée notamment par les algorithmes de hachage SHA.
|
||||||
|
|
||||||
|
Tous ces ajouts sont autant de manière de se tromper lorsqu'on réalise les étapes de chiffrement et de déchiffrement. Or, comme dit en chapô de cet article, sortir des sentiers battus est souvent synonyme de vulnérabilité.
|
||||||
|
|
||||||
|
Dès lors, il serait préférable de ne pas réinventer la roue, et d'employer des mécanismes et des bibliothèques cryptographiques bien connues pour résoudre notre problématique de gros fichiers.
|
||||||
|
|
||||||
|
## Les bibliothèques cryptographiques
|
||||||
|
|
||||||
|
En Go, il existe diverses bibliothèques cryptographiques haut-niveau qui sont fréquemment employées. Je vais ici parler d'OpenPGP, qui est problématique, et de NACL, qu'il convient de préférer.
|
||||||
|
|
||||||
|
### OpenPGP
|
||||||
|
|
||||||
|
OpenPGP est un [standard de chiffrement assez ancien](https://www.rfc-editor.org/rfc/rfc4880.txt). Son implémentation principale est GnuPG, et il continue d'être la marotte de certains techniciens bien mal avisés. Oui, je pense notamment à vous, les distributions Linux.
|
||||||
|
|
||||||
|
Ces mots durs contre ce format sont cependant mérités. OpenPGP est un musée des horreurs, remplis de mécanismes vétustes, et de constructions cryptographiques datant des balbutiements du chiffrement authentifié. Également, et non des moindres, ses implémenteurs semblent avoir une passion pour les mauvaises idées en matière d'API. L'auteur de cet article a d'ailleurs découvert [en 2015 des problèmes dans la plupart des implémentations d'OpenPGP](https://www.ssi.gouv.fr/uploads/2015/05/format-Oracles-on-OpenPGP.pdf), et certaines, en 2022, sont toujours vulnérables à ces découvertes... dont GnuPG.
|
||||||
|
|
||||||
|
En Go, sans surprise, l'implémentation d'OpenPGP contient elle aussi de mauvaises idées. Le package a même été [gêlé et déprécié](https://github.com/golang/go/issues/44226), avec le commentaire qu'il n'est pas souhaitable que les développeurs Go emploient OpenPGP, ce format étant "fragile, complexe, non sûr, et [...] son usage expose les développeurs à un écosystème dangereux". Pour enfoncer le clou, nous allons procéder à une étude de l'une de ses problématiques.
|
||||||
|
|
||||||
|
S'il est vrai qu'il est assez universel que des sources de données implémentent [io.Reader](https://pkg.go.dev/io#Reader), il est possible de s'interroger sur la pertinence de ce choix pour une source de données chiffrée dont l'intégrité ne peut être vérifiée qu'après une passe complète.
|
||||||
|
|
||||||
|
L'on pourrait alors s'attendre à ce que le containeur OpenPGP [openpgp.MessageDetails](https://pkg.go.dev/golang.org/x/crypto/openpgp#MessageDetails) effectue cette vérification de lui-même lors de son instanciation avec [openpgp.ReadMessage](https://pkg.go.dev/golang.org/x/crypto/openpgp#ReadMessage). Cela serait assez cohérent avec l'API de encoding/gzip dont la fonction [NewReader](https://pkg.go.dev/compress/gzip#NewReader) renvoie une erreur en l'absence d'octets "magiques" en début de lecture. Hélas, comme dit précemment, OpenPGP est un musée des horreurs, et il n'est pas possible de vérifier l'intégrité du chiffré ; il est nécessaire de déchiffrer l'intégralité du document chiffré, pour récupérer enfin un motif d'intégrité. En effet, avec le standard OpenPGP, le motif d'intégrité (un simple SHA-1 du clair) fait partie des données chiffrées, et est suffixé au clair. Cette approche est appelée [MAC-then-encrypt](https://en.wikipedia.org/wiki/Authenticated_encryption#MAC-then-Encrypt_(MtE)) et est décriée par la communauté cryptographique.
|
||||||
|
|
||||||
|
Bien que le io.Reader de openpgp.MessageDetails soit stocké dans le bien-nommé champ UnverifiedBody, il est extrêmement tentant pour un développeur de le "brancher" dans un autre io.Reader, à la manière d'une série de décorateurs, et d'oublier ou de découvrir trop tard que le message n'était pas intègre !
|
||||||
|
|
||||||
|
### NACL
|
||||||
|
|
||||||
|
[NACL](https://pkg.go.dev/golang.org/x/crypto/nacl) est une excellente bibliothèque cryptographique, dont l'API, bien conçue, ne permet qu'aux plus entêtés des idiots de se tromper dans son emploi. Il existe quelques outils en ligne de commande pour l'exploiter, elle ou sa variante (*fork*) [libsodium](https://github.com/jedisct1/libsodium). On peut notamment citer l'excellent utilitaire [minisign](https://github.com/jedisct1/minisign), par Frank Denis. L'auteur de cet article recommande vivement minisign comme remplacement à OpenPGP pour la signature de documents !
|
||||||
|
|
||||||
|
Il existe des implémentations de minisign en Go, dont [go-minisign](https://github.com/jedisct1/go-minisign), qui hélas souffre du même problème de gestion des gros fichiers qui nous occupe dans cet article. Fort heureusement il est possible d'exploiter go-minisign y compris pour des gros fichiers en utilisant les astuces qui sont présentées dans le présent article, plus bas.
|
||||||
|
|
||||||
|
Pour en revenir à NACL, les fonctions [box.Seal](https://pkg.go.dev/golang.org/x/crypto@v0.0.0-20211215153901-e495a2d5b3d3/nacl/box#Seal) et [box.Open](https://pkg.go.dev/golang.org/x/crypto@v0.0.0-20211215153901-e495a2d5b3d3/nacl/box#Open) ont pour particularité de ne pas recevoir d'io.Reader et d'écrire dans des io.Writer. Elles ne tombent donc pas dans le piège grossier au fond duquel on trouve OpenPGP. Ces fonctions utilisent des *slices* de *bytes*. Cela pourrait ressembler à un point bloquant. Cet article vise précisément à proposer une solution pour contourner cette particularité, tout en offrant un niveau de sécurité correct.
|
||||||
|
|
||||||
|
## Astuces systèmes à la rescousse
|
||||||
|
|
||||||
|
### Contrôler la mise à disposition des données
|
||||||
|
|
||||||
|
Comme vu en début d'article, il est important de maitriser le moment où les données déchiffrées sont publiées ; tant que ces dernières ne sont pas complètes ou pas vérifiées, la copie de travail de ces données doit rester privée. Vu que nous traitons des gros fichiers, qui ne tiennent pas en RAM, il est nécessaire de stocker la copie de travail sur le système de fichiers, tout en s'assurant que nul autre processus ou tâche ne puisse y avoir accès. Pour ce faire, il est possible d'utiliser les fichiers anonymes.
|
||||||
|
|
||||||
|
Les fichiers anonymes sont des fichiers qui sont stockés sur le système de fichiers, sans qu'aucun lien n'existe vers ces derniers. Par lien, ici, il faut comprendre lien dans le sens "entrée dans un répertoire" (*hardlink*). Ces fichiers sont créés en spécifiant l'option O_TMPFILE au *syscall* [open(2)](https://man7.org/linux/man-pages/man2/open.2.html). Tout octet écrit dans un tel fichier est effectivement stocké sur le système de fichiers, par l'intermédiaire du descripteur de fichier renvoyé par open(2) et connu uniquement du programme qui l'a créé (et des processus qui vont farfouiller dans /proc... mais ils cherchent les problèmes ;)). Il s'agit donc d'une copie privée du déchiffré. Lorsque le fichier est complet et son contenu vérifié, il est alors possible de le publier de différentes manières.
|
||||||
|
|
||||||
|
Une manière de publier le fichier d'une manière peu élégante est simplement de créer un nouveau fichier, sans l'option O_TMPFILE, puis de recopier le contenu du fichier déchiffré dans ce nouveau fichier qui est accessible par les autres processus. Le descripteur de fichiers peut alors être fermé, et le fichier anonyme sera automatiquement libéré. Cette méthode est couteuse et présente le défaut de doubler la taille disque nécessaire pour stocker le déchiffré, au moins temporairement, jusqu'à la fermeture du descripteur de fichier du fichier anonyme.
|
||||||
|
|
||||||
|
Une manière plus élégante, qui tire cependant partie d'une fonctionnalité qui n'est pas toujours disponible, est d'utiliser [FICLONE](https://man7.org/linux/man-pages/man2/ioctl_ficlone.2.html) du *syscall* [ioctl(2)](https://man7.org/linux/man-pages/man2/ioctl.2.html). FICLONE utilise la fonctionnalité de copy-on-write (COW) de certains systèmes de fichiers, comme [btrfs](https://btrfs.wiki.kernel.org/index.php/Main_Page). Avec ce *syscall*, il est possible d'ouvrir un fichier ayant un lien (*hardlink*) puis de demander que le fichier nommé soit une copie instantanée du fichier anonyme. Les deux fichiers partageront alors les mêmes blocs de données sur le système de fichiers, jusqu'à ce que l'un d'entre eux modifie un bloc. Mais en l'espèce, il n'y aura pas d'écriture ultérieure dans le fichier anonyme, après cet appel à ioctl(2). Il s'agit donc simplement d'une astuce qui permet de créer un lien vers le contenu du fichier anonyme, et donc de le publier.
|
||||||
|
Le seul défaut de cette approche est qu'il faut utiliser un système de fichier compatible avec FICLONE, et ce n'est notamment pas le cas de ext4, qui est généralement le système de fichiers par défaut des distrubutions Linux.
|
||||||
|
|
||||||
|
Finalement, il existe une troisième méthode, elle aussi élégante, qui ne tire par partie d'une fonctionnalité particulière de certains systèmes de fichiers. Hélas, il est nécessaire de disposer de certains privilèges système pour ce faire : [CAP_DAC_READ_SEARCH](https://man7.org/linux/man-pages/man7/capabilities.7.html). `CAP_DAC_READ_SEARCH` permet de contourner cette protection du système de fichiers, ce qui est regrettable, car il s'agit aussi du privilège requis pour appeler le syscall [linkat(2)](https://man7.org/linux/man-pages/man2/linkat.2.html), avec l'option `AT_EMPTY_PATH`. Ce syscall cumulé à cette option permet de créer un lien à partir d'un descripteur de fichiers. Il permet donc de donner un nom à notre fichier anonyme, une fois celui-ci complet. Il peut être acceptable de donner `CAP_DAC_READ_SEARCH` à notre processus, si ce dernier est exécuté dans un [chroot](https://man7.org/linux/man-pages/man2/chroot.2.html) dans lequel cette permission ne permet pas au programme de gagner ou conserver des accès indûs à des ressources du système. Cette solution est donc probablement acceptable dans certaines conditions, qui doivent cependant être bien maitrisées.
|
||||||
|
|
||||||
|
### Travailler sur une copie inaltérable du chiffré
|
||||||
|
|
||||||
|
Dans le cas d'un procédé de déchiffrement en deux passes, une pour la vérification d'intégrité, et une pour le déchiffrement en lui-même, il est possible d'utiliser les mêmes mécanismes que détaillés dans la section précédente de cet article pour obtenir une copie privée : créer un fichier anonyme dont le contenu sera alimenté par recopie (avec un surcoût en espace disque) ou le syscall ioctl(2) avec FICLONE pour obtenir une copie instantanée et qui ne pourra être modifiée par des processus ou taches tiers.
|
||||||
|
|
||||||
|
### Déchiffrer un gros fichier en mémoire virtuelle
|
||||||
|
|
||||||
|
Tout ce qui est dans la mémoire virtuelle n'est pas nécessairement de la mémoire physique. Ainsi, il est possible d'obtenir une *slice* Go comportant le contenu d'un fichier, sans que celui-ci ne soit lu et recopié en RAM. De même, il est possible d'écrire dans une *slice*, qui n'est pas stocké en RAM, grâce à un *syscall* : [mmap(2)](https://man7.org/linux/man-pages/man2/mmap.2.html). Il nous est donc possible d'appeler `box.Seal` et `box.Open` sur de telles *slices*, et le résultat aura été calculé sans que le contenu des fichiers ne soit stocké en intégralité en RAM !
|
||||||
|
|
||||||
|
Hélas, les choses ne sont jamais aussi "simples". Il a quelques petites subtilités supplémentaires, requises lorsqu'on effectue cette opération d'écriture dans une *slice* pointant vers un fichier placé en mémoire virtuelle avec mmap(2). D'une part, il est nécessaire que le fichier de destination soit de la bonne taille avant l'appel à mmap(2). Pour cela, on peut créer un fichier creux (*sparse file*), à l'aide de l'appel système [fallocate(2)](https://man7.org/linux/man-pages/man1/fallocate.1.html). Ensuite, une fois l'écriture dans la *slice* accomplie, il est nécessaire d'appeler le *syscall* [msync(2)](https://man7.org/linux/man-pages/man2/msync.2.html) afin de forcer le transfert de données depuis la mémoire virtuelle vers le fichier, avant d'effectuer l'appel à munmap(2), pour "défaire" la *slice* créée par mmap(2).
|
||||||
|
|
||||||
|
De même, mmap(2) est utilisé pour le fichier chiffré, mais il y a là aussi quelques subtilités. Il est notamment préférable de travailler sur une copie privée du fichier, plutôt qu'un fichier altérable par une source externe. En effet, le comportement n'est pas spécifié si un programme externe tronquait le fichier après que ce dernier ait été passé à mmap(2).
|
||||||
|
|
||||||
|
Enfin, lorsqu'on passe la *slice* recevant les données déchiffrées à `box.Open`, il convient de passer cette *slice* découpée avec [:0], afin de garder la capacité de la *slice* égale à la taille du fichier, mais de forcer sa longueur à 0. Ce faisant, `box.Open` ne procèdera pas à des réallocations du tableau qui sous-tend la *slice*. Il est, en effet, capital d'user de cette astuce, afin de continuer de travailler dans la mémoire virtuelle retournée par mmap(2) et ne pas se retrouver à travailler accidentellement en RAM.
|
||||||
|
|
||||||
|
## Rassembler tout en un programme cohérent
|
||||||
|
|
||||||
|
Pour résumer tout ce qui a été abordé dans cet article :
|
||||||
|
|
||||||
|
* pour traiter un gros fichier, il est préférable d'utiliser efficacement Linux que de risquer de créer des vulnérabilités en essayant de tronçonner le fichier ;
|
||||||
|
* il est important de toujours travailler avec des copies privées des données, que ce soit le contenu chiffré et le contenu déchiffré ;
|
||||||
|
* il est nécessaire d'avoir un contrôle sur la publication du déchiffré, notamment pour s'assurer que le contenu est intègre, et complet avant de le rendre accessible à des applications tierces.
|
||||||
|
|
||||||
|
Pour accomplir ces objectifs, il est possible d'exploiter les syscall Linux mmap(2), fallocate(2), msync(2), et ioctl(2) ou linkat(2).
|
||||||
|
|
||||||
|
[Ce dépôt git](https://github.com/X-Cli/large-file-decrypt) contient un exemple d'algorithme mettant tous ces éléments en oeuvre, pour déchiffrer un gros fichier, de manière sécurisée.
|
||||||
|
|
||||||
|
|
192
posts/pass-sanitaire.md
Normal file
192
posts/pass-sanitaire.md
Normal file
|
@ -0,0 +1,192 @@
|
||||||
|
---
|
||||||
|
title: "Pass sanitaire et vie privée : quels sont les risques ?"
|
||||||
|
slug: pass-sanitaire
|
||||||
|
authors: ["Florian Maury", "Piotr Chmielnicki"]
|
||||||
|
description: "Le gouvernement français et l'Europe ont sorti leurs pass sanitaires dans le cadre de la crise sanitaire du COVID-19. Nous avons analysé le pass sanitaire français. Quels sont ses risques pour la vie privée ?"
|
||||||
|
date: 2021-06-03T18:00:00+02:00
|
||||||
|
type: posts
|
||||||
|
draft: false
|
||||||
|
categories:
|
||||||
|
- privacy
|
||||||
|
tags:
|
||||||
|
- privacy
|
||||||
|
- cryptography
|
||||||
|
- covid19
|
||||||
|
- pass sanitaire
|
||||||
|
---
|
||||||
|
Cet article est sous licence CC-BY-ND.
|
||||||
|
|
||||||
|
Auteurs :
|
||||||
|
|
||||||
|
* [Florian Maury](https://infosec.exchange/@X_Cli)
|
||||||
|
|
||||||
|
* [Piotr Chmielnicki](https://twitter.com/piotrcki)
|
||||||
|
|
||||||
|
Les auteurs de cet article peuvent être contactés :
|
||||||
|
|
||||||
|
* par email : florian.maury-pass-sanitaire VOUS-SAVEZ-QUOI broken-by-design.fr
|
||||||
|
|
||||||
|
* par chat, avec Matrix : #pass-sanitaire:matrix.piotr.paris
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Mise à jour importante du 10 juin 2021** : [l'application TousAntiCovid Verif](https://twitter.com/gilbsgilbs/status/1402689482193879045), dans sa dernière version à la date d'écriture de cette ligne, ne fait plus appel à un serveur centralisé pour valider les pass sanitaires. Elle n'envoie plus de données à un prestataire américain. Elle ne contient plus de services Google Firebase. Hélas, le reste de l'article et de la vidéo continuent d'être pertinents pour l'heure. La Quadrature du Net effectue [une action en justice](https://www.laquadrature.net/2021/06/09/passe-sanitaire-attaquons-lobligation-didentification/) en ce sens.
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Il existe une vidéo compagnon, pour ceux qui préfèrent regarder que lire.
|
||||||
|
|
||||||
|
Sur Peertube :
|
||||||
|
|
||||||
|
{{< rawhtml >}}
|
||||||
|
<iframe width="560" height="315" sandbox="allow-same-origin allow-scripts allow-popups" title="Pass sanitaire et vie privée : quels sont les risques ?" src="https://peertube.stream/videos/embed/a1b37a25-fa83-4350-8da9-db3f89c662b0" frameborder="0" allowfullscreen></iframe>
|
||||||
|
{{< /rawhtml >}}
|
||||||
|
|
||||||
|
En téléchargement : [Haute qualité (1300MB)](https://data.piotr.paris/pass-sanitaire.mp4) / [Faible qualité (90MB)](https://data.piotr.paris/pass-sanitaire-br.mp4)
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Ce document porte sur le pass sanitaire, qui est en train d'être mis en place par le gouvernement français et qui entrera en vigueur le 9 juin 2021. Il vise à mettre au jour de fausses informations diffusées par certains membres du gouvernement, à expliquer et à illustrer pourquoi le pass sanitaire, tel qu'il est conçu, met en danger la vie privée, mais aussi des données médicales des citoyens. En outre, il accroit le risque de vol d'identité.
|
||||||
|
|
||||||
|
Le pass sanitaire est présenté sous la forme d'un code barre en deux dimensions, appelé datamatrix. Ce code barre, comme son nom l'indique, encode des informations. Il est en cela similaire aux codes barres des produits que vous achetez en grande surface, et que vous passez à la caisse. Il est juste en deux dimensions et contient plus d'information. Au lieu d'un numéro qui sert à indiquer à la caisse enregistreuse la nature du produit que vous achetez, ce qui lui sert à connaitre le prix à imputer, le code barre du pass sanitaire contient vos informations personnelles et des informations relatives à la vaccination. L'encodage de ces informations ne constitue pas une mesure de protection des données puisque n'importe qui équipé d'un dispositif de lecture de code-barres peut acquérir les données qui ont été encodées. Le pass sanitaire ne fait pas exception.
|
||||||
|
|
||||||
|
D'après le site [Service Public.fr](https://www.service-public.fr/particuliers/actualites/A14896), le pass sanitaire contient les informations suivantes :
|
||||||
|
|
||||||
|
> * nom, prénom ;
|
||||||
|
> * date de naissance ;
|
||||||
|
> * type de certificat et résultat éventuel (test PCR ou antigénique ou vaccination première et seconde dose) ;
|
||||||
|
> * type de vaccin le cas échéant ;
|
||||||
|
> * date et heure du certificat.
|
||||||
|
|
||||||
|
Le site [gouvernement.fr](https://www.gouvernement.fr/pass-sanitaire-toutes-les-reponses-a-vos-questions) indique la même liste.
|
||||||
|
|
||||||
|
Nous avons analysé le contenu du pass sanitaire, à l'aide d'outils grands publics, trouvables sur n'importe quel Store d'applications, comme le Google Play Store ou l'Apple Store. Par exemple, Barcode Scanner de ZXing Team sur le Google Play Store.
|
||||||
|
|
||||||
|
Nous affirmons que la liste dressée par les sites gouvernmentaux est incomplète.
|
||||||
|
|
||||||
|
Le pass est composé de 3 types d'informations :
|
||||||
|
|
||||||
|
* des informations techniques, qui permettent de vérifier l'authenticité du pass sanitaire ; on y retrouve des informations sur l'émetteur du pass sanitaire, ainsi que la date d'émission, et le sceau d'authenticité (une signature numérique) ;
|
||||||
|
* des informations personnelles : nom, prénom et date de naissance ;
|
||||||
|
* des informations de santé : le type de molécule injectée, le nom du vaccin reçu, le nombre de doses reçues, la date de vaccination et si ce nombre est suffisant pour être protégé de manière optimale pour la personne vaccinée.
|
||||||
|
|
||||||
|
![Décomposition du pass sanitaire](/pass_content.png)
|
||||||
|
|
||||||
|
Au-delà de ces informations de santé, il est également possible d'inférer des informations de santé encore plus privées sur certains citoyens : ont-il déjà été infectés par la COVID-19 (besoin que d'une seule dose) ? Sont-ils immunodéprimés (besoin de trois doses) ? Sont-ils parmis les citoyens prioritaires pour recevoir des injections tôt dans le calendrier vaccinal ?
|
||||||
|
|
||||||
|
Ces informations dépassent largement le cadre et la finalité du pass sanitaire.
|
||||||
|
|
||||||
|
Les sites gouvernementaux (comme gouvernement.fr et servic public.fr) se veulent cependant rassurants. Ils précisent :
|
||||||
|
|
||||||
|
> Pour accéder à un lieu, un établissement ou un événement, seuls les ouvreurs engagés par les organisateurs pourront lire :
|
||||||
|
|
||||||
|
> * nom et prénom ;
|
||||||
|
> * date de naissance ;
|
||||||
|
> * accès autorisé ou accès refusé, en fonction des règles sanitaires imposées pour accéder au lieu (les ouvreurs ne pourront pas connaître le détail du type de certificat sanitaire présenté).
|
||||||
|
|
||||||
|
De même, M. Cédric O, secrétaire d'État, chargé de la transition numérique, indique dans son interview exclusive donnée au journal [Le Parisien](https://www.leparisien.fr/societe/pass-sanitaire-carnet-de-rappel-numerique-cedric-o-devoile-les-nouveautes-de-tousanticovid-24-05-2021-BGOIIX3GSRH7XAUPCDZDMNZO3Y.php) :
|
||||||
|
|
||||||
|
> Comment les professionnels vérifieront-ils le pass sanitaire numérique ?
|
||||||
|
>
|
||||||
|
> D’ici le 9 juin, nous aurons déployé l’application de lecture appelée TousAntiCovid Verif. Pour les compagnies aériennes, il y aura une version spécifique car elles ont obligation d’avoir accès au contenu détaillé, avec la date de vaccination, le type de vaccination etc. Elles pourront la télécharger sur les stores, avec un contrôle d’accès par identifiant. En revanche, les organisateurs d’évènements ou les lieux concernés par le pass sanitaire en France, ne connaîtront pas ces informations. Ils ne sauront que le nom, le prénom et la date de naissance de la personne concernée et ne verront apparaître que « vert » ou « rouge » pour valider ou non l’accès. Pour eux, l’application sera en accès libre sur les stores.
|
||||||
|
|
||||||
|
Comme nous l'avons démontré plus tôt dans ce document, il n'existe aucune protection contre l'obtention de l'ensemble des données contenues dans le pass sanitaire. Tout lecteur de code barre grand public est suffisant. Il n'est nul besoin d'être membre d'une compagnie aérienne pour obtenir une application aux pouvoirs supérieurs permettant d'acquérir des informations de santé sensibles sur une personne qui exposerait volontairement ou par mégarde son pass sanitaire, sur Internet, dans une file d'attente, ou à un personnel de sécurité à l'entrée d'un événement.
|
||||||
|
|
||||||
|
Il existe également d'autres parties qui pourraient être mises au courant du contenu de votre pass sanitaire. D'après un [tweet du compte TousAntiCovid du 20 mai 2021](https://twitter.com/TousAntiCovid/status/1395317625161408512):
|
||||||
|
|
||||||
|
> #COVID19 | Les autorités compétentes peuvent lire vos certificats de tests avec l’application #TousAntiCovid Verif. Seule la signature du certificat est vérifiée par un serveur dédié d'@IN_Groupe respectant toutes les règles de sécurité des systèmes d'information.
|
||||||
|
|
||||||
|
Cette assertion est également corroborée par la demande de l'application TousAntiCovid Verif d'avoir un accès complet au réseau, lors de son installation. De même, on trouve dans les entrailles de l'application TousAntiCovid Verif, une URL (`https://portail.tacv.myservices-ingroupe.com`), et ainsi qu'un fichier comportant une fonction `call2dDoc`, qui fait une requête HTTP avec des paramètres `2ddoc`, `latitude` et `longitude`. Enfin, lorsque l'on scanne un pass sanitaire en mode avion, TousAntiCovid Verif affiche un message d'erreur "Erreur de connexion" et n'affiche pas de résultat.
|
||||||
|
|
||||||
|
Il n'est pas aisé de déterminer clairement ce que fait cette fonction, car l'application TousAntiCovid Verif, contrairement à l'application TousAntiCovid, n'est pas en sources ouvertes. Néanmoins, [gilbsgilbs](https://twitter.com/gilbsgilbs) a su faire de l'ingénierie inverse et [il confirme nos craintes et observations](https://twitter.com/gilbsgilbs/status/1401129388234657794).
|
||||||
|
|
||||||
|
Il convient de noter qu'une telle communication réseau génère des meta-données de communication avec le serveur ; il y a notamment l'adresse IP de l'équipement faisant tourner l'application TousAntiCovid Verif. Cette adresse IP permet la géolocalisation de l'équipement faisant tourner TousAntiCovid Verif, par l'entremise des opérateurs de télécommunication, comme Orange.
|
||||||
|
|
||||||
|
En croisant ces données, l'État serait donc en mesure de dresser un listing des citoyens et de leurs lieux de fréquentation, grâce au pass sanitaire.
|
||||||
|
|
||||||
|
Il convient de noter que l'envoi des données (complètes ou sous la forme d'empreintes cryptographiques) n'est nullement nécessaire pour la vérification de la signature numérique du pass sanitaire. Toutes les informations nécessaires à cette vérification sont publiques. La validation du pass sanitaire peut donc être accomplie sans problème directement par l'application de lecture du code barre. Si un lecteur de code barre est jugé par le gouvernement comme étant suffisamment de confiance pour lire les données médicales et afficher un verdict, il l'est aussi pour la vérification de la signature.
|
||||||
|
|
||||||
|
Nous ne sommes pas les seuls à dénoncer le pass sanitaire, et la quantité d'informations qu'il recèle. La CNIL, la Commission Nationale Informatique et Liberté, a été saisie et a rendu un avis le 12 mai 2021 :
|
||||||
|
|
||||||
|
> 36. La Commission considère qu’un dispositif visant à ne permettre la vérification que sur la base d’un résultat de conformité réduirait considérablement les données accessibles aux personnes habilitées à vérifier le statut des personnes concernées, et notamment de ne pas indiquer si elle a été vaccinée, a fait un test ou s’est rétablie d’une infection antérieure à la COVID-19, conformément au principe de minimisation des données
|
||||||
|
>
|
||||||
|
> 37. Un tel dispositif implique le téléchargement, du côté des vérificateurs, d’une application permettant de décoder les signaux, probablement sous forme de code-QR, qui contiendront l’information permettant de faire apparaître un résultat vert ou rouge et d’en vérifier l’authenticité. Dans l’hypothèse où ce code-QR correspondrait aux codes actuellement disponibles dans la fonctionnalité « TousAntiCovid Carnet », la Commission relève que celui-ci contient plus d’informations (nom, prénom, date de 8 naissance, date d’examen, type d’examen, résultat). Il est donc possible, dans ce cas, qu’un tel dispositif soit détourné de façon à ce que le lecteur (téléphone ou lecteur dédié) lisant le code-QR puisse accéder à davantage d’informations qu’un simple résultat de conformité (couleur verte ou rouge). Elle invite le Gouvernement à s’assurer de la mise en œuvre des mesures opérationnelles et à fournir, aux personnes gérant les lieux, événements et établissements toute documentation nécessaire (communication sur les lieux, établissements ou évènements soumis au dispositif, mise en place d’une signalétique visible sur place, etc.) permettant de se prémunir de ce risque.
|
||||||
|
|
||||||
|
Hélas, aucune "mesure opérationnelle" significative n'a été mise en place par le gouvernement. Il aurait, par exemple, été possible d'émettre plusieurs pass, contenant plus ou moins d'information, en fonction du type de lieu (e.g. salles de spectacle ou aéroports). Cela aurait été, de surcroit, conforme au principe de minimisation des données en regard de la finalité, comme indiqué par la CNIL ou par le Règlement Général de la Protection des Données (RGPD).
|
||||||
|
|
||||||
|
Nous affirmons donc, que la mise en oeuvre du pass sanitaire, en l'état, constitue un risque significatif pour la vie privée, pour les données personnelles (risque de vol d'identité accru) et pour les données médicales des citoyens.
|
||||||
|
|
||||||
|
Nous affirmons qu'il contient des informations sensibles sans aucun rapport avec la finalité énoncée.
|
||||||
|
|
||||||
|
Nous affirmons qu'il peut être détourné pour pister les citoyens.
|
||||||
|
|
||||||
|
Nous demandons le retrait du pass sanitaire dans sa forme actuelle.
|
||||||
|
|
||||||
|
Nous invitons les citoyens français à rejoindre cet appel et à déposer une plainte auprès de la CNIL et du défenseur des droits contre le pass sanitaire dans sa forme actuelle.
|
||||||
|
|
||||||
|
Nous invitons les citoyens européens et les responsables politiques à s'opposer au pass sanitaire européen qui est peu ou prou calqué sur le pass sanitaire français, avec les mêmes informations, les mêmes dérives et les mêmes risques.
|
||||||
|
|
||||||
|
Si un nouveau pass sanitaire français est créé, nous exigeons le retrait des informations qui sont sans rapport avec la finalité. Si certaines informations sensibles doivent figurer dans le pass sanitaire, plusieurs pass doivent être remis en fonction du besoin d'en connaitre des employés de sécurité filtrant l'accès à un lieu.
|
||||||
|
|
||||||
|
Finalement, nous exigeons que la vérification de l'authencité du pass sanitaire s'effectue localement par une application de vérification en source ouverte, sans avoir besoin de la permission d'accès au réseau.
|
||||||
|
|
||||||
|
M. Cédric O s'indigne, dans son interview au Parisien :
|
||||||
|
|
||||||
|
> Il y a une forme d’aberration dans la crispation sur ces sujets-là. Comme si nous avions si peur de la solidité de notre démocratie et de notre état de droit, qu’on ne puisse pas se doter de ces outils.
|
||||||
|
|
||||||
|
Nous affirmons que la confiance ne s'exige pas, mais qu'elle s'acquiert. Nous affirmons que son acquisition passe par la vérité, la transparence, et des actes en accord avec les paroles et les engagements. Sur ce point, le pass sanitaire est un échec.
|
||||||
|
|
||||||
|
De surcroit, les risques de détournement ou de mésusage évoquées dans ce document devraient au minimum avoir été considérés avec circonspection par les responsables politiques. S'ils l'avaient été, le pass sanitaire dans sa forme actuelle aurait été rejeté selon le principe de prudence, au nom de la protection des citoyens.
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Autres documents :
|
||||||
|
|
||||||
|
* un [excellent article](https://cq94.medium.com/la-fin-du-pseudonymat-dans-tousanticovid-932d50de11ee) de Christian Quest, sur ce même sujet, qui confirme nos observations ;
|
||||||
|
* un [fil de tweets de Mathis Hammel](https://twitter.com/MathisHammel/status/1397902091067265026), qui faisait une analyse similaire à la nôtre, au même moment où nous tournions les rushs de notre vidéo ;
|
||||||
|
* une [application Android en preuve de concept](https://twitter.com/BLeQuerrec/status/1401290053687644169), qui collecte et extrait les informations de pass sanitaires scannés, a été développée par Bastien Le Querrec ;
|
||||||
|
* [une version alternative de TousAntiCovid](https://github.com/ofa-/stopcovid-android) maintenue par Olaf, qui contient notamment un lecteur/vérificateur du pass sanitaire au format 2D-DOC, prouvant la faisabilité d'une lecture et d'une vérification en local, sans avoir besoin des serveurs d'IN Groupe ;
|
||||||
|
* un [fil de tweets de Pixel de Tracking](https://twitter.com/pixeldetracking/status/1401626317053992963), qui indique des trouvailles similaires à celles sur Android : le pass sanitaire est envoyé aux serveurs d'IN Groupe en totalité ;
|
||||||
|
|
||||||
|
* [une application web qui vérifie les pass sanitaires de manière autonome](https://github.com/lovasoa/sanipasse) ;
|
||||||
|
|
||||||
|
* [l'article de presse de NextInpact](https://www.nextinpact.com/article/46153/pass-sanitaire-poudre-aux-yeux-pseudonymat-donnees-medicales-en-clair)
|
||||||
|
|
||||||
|
* [l'article de press de Numerama](https://www.numerama.com/tech/717219-tousanticovid-verif-pourquoi-lapp-verifiant-les-donnees-du-pass-sanitaire-fait-polemique.html)
|
||||||
|
|
||||||
|
* [l'article de Developpez](https://www.developpez.com/actu/315816/Application-TousAntiCovid-et-Pass-sanitaire-les-informations-personnelles-et-medicales-sont-disponibles-en-clair-previent-Christian-Quest/)
|
||||||
|
|
||||||
|
* [l'article d'igen](https://www.igen.fr/ailleurs/2021/06/le-pass-sanitaire-souleve-des-craintes-en-matiere-de-confidentialite-123027)
|
||||||
|
|
||||||
|
* [l'article de 01net](https://www.01net.com/actualites/tousanticovid-verif-le-panier-perce-du-pass-sanitaire-2044056.html)
|
||||||
|
|
||||||
|
* [l'article de lemondeinformatique.fr](https://www.lemondeinformatique.fr/actualites/lire-tousanticovid-le-pass-sanitaire-en-quete-de-protection-83204.html)
|
||||||
|
|
||||||
|
* [l'article de Mediapart](https://www.mediapart.fr/journal/france/090621/donnees-personnelles-les-controles-du-passe-sanitaire-suscitent-linquietude)
|
||||||
|
|
||||||
|
* [l'article de Contrepoints](https://www.contrepoints.org/2021/06/09/399255-donnees-medicales-anonymat-surveillance-un-pass-sanitaire-passoire)
|
||||||
|
|
||||||
|
* [l'article de NextInpact sur la réaction du gouvernement le 8 juin, en conférence de presse](https://www.nextinpact.com/article/46147/pass-sanitaire-donnees-en-clair-open-source-on-fait-au-plus-simple)
|
||||||
|
|
||||||
|
* [la délibération de la CNIL du 7 juin (PDF)](https://www.cnil.fr/sites/default/files/atoms/files/deliberation_2021-067_du_7_juin_2021_portant_avis_sur_le_projet_de_decret_portant_application_du_ii_de_larticle_1er_de_la_loi_du_31_mai_2021.pdf)
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Remerciements : Aurélien Hugues, Émilie Gill, Stéphane Bortzmeyer
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
Historique d'édition :
|
||||||
|
|
||||||
|
* 5 juin 2021 à 13h55 : ajout des références aux travaux de gilbsgilbs et de Christian Quest
|
||||||
|
* 5 juin 2021 à 14h35 : ajout du fil de tweets de Mathis Hammel
|
||||||
|
* 5 juin 2021 à 16h20 : ajout des remerciements
|
||||||
|
* 6 juin 2021 à 8h45 : ajout de l'application en preuve de concept
|
||||||
|
* 6 juin 2021 à 11h50 : ajout de la version alternative de TousAntiCovid par Olaf
|
||||||
|
* 7 juin 2021 à 12h40 : corrections de coquilles, précisions sur les auteurs, rajout d'une réference à NextInpact, reformulation de la contribution d'Olaf
|
||||||
|
* 7 juin 2021 à 13h30 : ajout du fil de tweets de Pixel de Tracking
|
||||||
|
* 8 juin 2021 à 8h50 : ajout des articles de Numerama, de Developpez, et du code source de sanipasse
|
||||||
|
* 8 juin à 19h15 : ajout des articles d'igen, de 01net et de lemondeinformatique.fr
|
||||||
|
* 9 juin à 13h35 : ajout des articles de Mediapart, de Contrepoints, de Nextinpact, et la délibération de la CNIL du 7 juin ; remerciements à Stéphane Bortzmeyer sans lequel la diffusion de cet article n'aurait pas été celle qu'elle a connue
|
||||||
|
* 10 juin à 8h25 : ajout du bandeau de mise à jour en tête d'article concernant TousAntiCovid Verif
|
||||||
|
|
43
posts/pki_rant.md
Normal file
43
posts/pki_rant.md
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
---
|
||||||
|
title: "A PKI Rant: the Free (as in free beer) Certificate Problem"
|
||||||
|
slug: pki-rant
|
||||||
|
description: null
|
||||||
|
date: 2018-10-24T00:00:00+00:00
|
||||||
|
type: posts
|
||||||
|
draft: false
|
||||||
|
categories:
|
||||||
|
- PKI
|
||||||
|
tags:
|
||||||
|
- PKI
|
||||||
|
- LE
|
||||||
|
---
|
||||||
|
|
||||||
|
# Intro
|
||||||
|
|
||||||
|
Someone asked me what I had to object to [this article](https://scotthelme.co.uk/debunking-the-fallacy-that-paid-certificates-are-better-than-free-certificates-and-other-related-nonsense/) about free certificates versus paying ones.
|
||||||
|
|
||||||
|
# The competing interests problem
|
||||||
|
First of all, I agree that many CAs (Certificate Authorities) are overselling their certificates, granting them properties or responsibilities that are not factual or even accurate on the technical level. The examples provided in the article are excellent. Where my opinion starts to diverge is near the conclusion. Most people will say that the following is pure FUD, and they would not be absolutely wrong. I have nothing to prove my statements; these are hypotheses, and things to look out for. Truth is I don't feel like I am not FUDing, or I would not speak up. I think I am just being extra precautious.
|
||||||
|
|
||||||
|
First, I find amusing that many people like to quote "if that is free, then you are the product", but when it comes to Let's Encrypt, they forget this statement and enjoy their certificates carelessly. So, you might ask: "How am I a product, when I am a client of Let's Encrypt? They are not selling my information, and even if they were, all they have on me and my website are public data". True. Your information is not a good for Let's Encrypt, as far as I know. What you are, however, is a wallet, who is no longer opening to give money to any CAs. You see, operating a CA is not free, nor even cheap (especially when you consider that [Let's Encrypt staffing budget](https://letsencrypt.org/2016/09/20/what-it-costs-to-run-lets-encrypt.html) was a whooping $2.06M for 10 people in 2017 ). So if you get your certificates for free, someone is paying for them. Let's see, "platinum sponsors" (over $300K annually):
|
||||||
|
|
||||||
|
- EFF: OK.
|
||||||
|
- OVH: they make a heavy usage of Let's Encrypt for their shared hosting; OK.
|
||||||
|
- Cisco and Akamai: hmm, OK.
|
||||||
|
- Mozilla and Chrome? Uh-oh.
|
||||||
|
|
||||||
|
You see, if Let's Encrypt is giving certificates for free, other CAs won't sell any, because people know that there is no difference between a paying DV certificate and a Let's encrypt DV certificate (or now, they know, thanks to the aforementioned article). Since browsers are increasingly lowering the value of EV certificates, people have less and less incentive to pay for them. (Did you notice there is no longer any green bar (or indicator for that matter) in Chrome?) Thus, people won't buy DV or EV certificates from commercial CAs. What will happen to them? They will either shut down their business or become increasingly less secure, because of the lack of funding. Good riddance will say those that consider they have been swindled enough by these CAs. But what the sponsors of Let's Encrypt are doing, really, is leading a war of attrition on commercial CAs by sponsoring an organization that is losing money with each emitted certificate. That's called unfair competition and that is the road to monopolies, too-big-to-fail entities, and single point of failure. That is also the road to an entity whose main sponsors are consumers of the product, and who may have competing interests.
|
||||||
|
|
||||||
|
What do you think will happen if, say, Google requires drastic changes in CA policies at the CA/B Forum and the main CA is also dependent on Google's funding to exist and operate properly?
|
||||||
|
|
||||||
|
# The Certificate Transparency problem
|
||||||
|
Also, you may consider that free certificates are a bane because people act more carelessly with what is literally worthless, than if they had paid $1 for it. Did you ever come across people who are registering certificates during their boot-up procedures and throw them away during the shutdown procedure (docker containers, hello!)? Well, these certificates are ephemeral only if you consider the registrant.
|
||||||
|
|
||||||
|
You see, Let's Encrypt is not all bad; they have implemented ACME, and they started registering all their certificates to Certificate Transparency (CT) logs long before April 2018, when it became required by some browsers. For those not familiar with CT, it is a collection of append-only logs where certificates emitted by ("public") CAs are registered, for them to be publicly auditable. The important word in the previous sentence is "append-only". That means that the so-called ephemeral certificates are logged forever in these CT logs. This causes massive scaling issues on the CT ecosystem, because log operation requires a lot of memory and the CT logs are literally spammed with free certificates. This, in turn, causes CT log sharding (certificates are logged into different CT logs based on some criteria), which increases the difficulty for website owners to monitor these logs and use them properly.
|
||||||
|
|
||||||
|
# Conclusion
|
||||||
|
If DV certificate price was low but non-null, commercial CAs would still be able to exist, and Let's Encrypt would be less dependent on their sponsor and less influenced by their agenda and pressures. So I dare say that free certificates are actively harming the web PKI.
|
||||||
|
|
||||||
|
|
||||||
|
This content was originally posted by myself on [https://fediverse.blog/~/InfiniteTypingPlatypuses/a-pki-rant-the-free-as-in-beer-certificate-problem](Fediverse.blog).
|
||||||
|
|
467
posts/python-string-emptiness.md
Normal file
467
posts/python-string-emptiness.md
Normal file
|
@ -0,0 +1,467 @@
|
||||||
|
---
|
||||||
|
title: "Python String Emptiness Test"
|
||||||
|
date: 2019-03-25T00:00:00+00:00
|
||||||
|
draft: false
|
||||||
|
categories: ["python"]
|
||||||
|
tags: [ "python", "secureprogramming" ]
|
||||||
|
---
|
||||||
|
|
||||||
|
Security specialists have this tendency to focus on the most mundane things,
|
||||||
|
and overthink them to the point where they may actually find something
|
||||||
|
interesting to say about them.
|
||||||
|
|
||||||
|
Is that a useful use of their time? That question is open to debate ;p
|
||||||
|
|
||||||
|
But I believe writing about them IS a useful use of my time on a gray Sunday
|
||||||
|
morning!
|
||||||
|
|
||||||
|
# Python String Emptiness Test
|
||||||
|
|
||||||
|
A while back, I entered a lengthy debate about condition expressions in Python
|
||||||
|
with one of my colleagues during a merge conflict. We both wrote a utility
|
||||||
|
function expressing the exact same condition, but we had written it in a
|
||||||
|
different way.
|
||||||
|
|
||||||
|
He wrote this:
|
||||||
|
|
||||||
|
{{< highlight python >}}
|
||||||
|
def should_i_do_smth():
|
||||||
|
# ...
|
||||||
|
return bool(some_string) and some_int == 0
|
||||||
|
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
I wrote that:
|
||||||
|
|
||||||
|
{{< highlight python >}}
|
||||||
|
def should_i_do_smth():
|
||||||
|
# ...
|
||||||
|
return len(some_string) != 0 and some_int == 0
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
The only use case of that function was in an if condition:
|
||||||
|
|
||||||
|
{{< highlight python >}}
|
||||||
|
if should_i_do_smth():
|
||||||
|
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
Whose code was merged is not really relevant, but I decided to create a Twitter
|
||||||
|
poll to probe my followers on the topic of writing emptiness tests of Python
|
||||||
|
strings.
|
||||||
|
|
||||||
|
The poll was:
|
||||||
|
|
||||||
|
> According to you, which of the four following lines of #Python code (poll) is
|
||||||
|
> the most *self-explanatory* and the *safest* to express that the string
|
||||||
|
> variable "value" is not empty? All answers are valid Python.
|
||||||
|
>
|
||||||
|
>* `if value:`
|
||||||
|
>* `if bool(value):`
|
||||||
|
>* `if len(value) != 0:`
|
||||||
|
>* `if value != '':`
|
||||||
|
|
||||||
|
## Regarding the poll itself
|
||||||
|
|
||||||
|
The wording of the poll is vague on purpose. Depending on the reader, some of
|
||||||
|
the words have different meanings. That was a deliberate choice. It is very
|
||||||
|
hard to express formal things in a natural language. So, even if you have clear
|
||||||
|
instructions, and documentation, and code comments, in the end, what really
|
||||||
|
matters is what was on the mind of the dev when they wrote that line of code.
|
||||||
|
|
||||||
|
People who answered the poll may have read differently:
|
||||||
|
|
||||||
|
* Safest: did I mean that the code must be robust (i.e. not crash) or secure
|
||||||
|
(i.e. act consistently and fail during static analysis)?
|
||||||
|
|
||||||
|
* String variable: did I mean `str` or `Optional[str]`, only Unicode string
|
||||||
|
(`''`) or also bytestring (`b''`)? Was there a test to control the type? Or
|
||||||
|
static typing analysis?
|
||||||
|
|
||||||
|
* Not empty: did I mean that we should refuse the empty string only, or should
|
||||||
|
we also refuse None?
|
||||||
|
|
||||||
|
* Self-explanatory: did I mean that the code should be readable by Python
|
||||||
|
developers abiding to PEP8 and the Zen, or should the code be readable by most
|
||||||
|
people, including security auditors or junior developers that may not be
|
||||||
|
knowledgeable of PEP8 best current practices. Is readable equivalent to
|
||||||
|
explicit and unambiguous?
|
||||||
|
|
||||||
|
Of course, the poll results are heavily biased. Most of my followers are
|
||||||
|
security specialists, not software engineers, and certainly not full-time
|
||||||
|
Python developers.
|
||||||
|
|
||||||
|
At any rate, I find the results quite interesting, because a majority of people
|
||||||
|
did NOT follow the PEP-8 recommendation when asked this question.
|
||||||
|
|
||||||
|
At the time of writing, with 54 voters, the poll results is:
|
||||||
|
|
||||||
|
* `if value:` 38%
|
||||||
|
* `if bool(value):` 4%
|
||||||
|
* `if len(value) != 0:` 34%
|
||||||
|
* `if value != '':` 24%
|
||||||
|
|
||||||
|
## Answer analysis
|
||||||
|
Each of the poll answers is valid Python, but they all also have merits.
|
||||||
|
|
||||||
|
### <a name="first"></a>First Answer
|
||||||
|
|
||||||
|
`if value:` might be readable by most full-time Python developers because it is
|
||||||
|
the Python way, recommended by PEP-8. It works because the empty string `''` is
|
||||||
|
evaluated to False in the context of a condition expression.
|
||||||
|
|
||||||
|
To some extent, it is comparable to the following C code :
|
||||||
|
|
||||||
|
{{< highlight c >}}
|
||||||
|
char * value;
|
||||||
|
... instructions ...
|
||||||
|
if (value && *value) {
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
This code plays on the fact that a `NULL` pointer values 0, which is evaluated
|
||||||
|
to `False` in C. It also plays on the fact that if the string is empty, the
|
||||||
|
first character will be the `NULL` byte (`\0`), which is also evaluated to
|
||||||
|
`False`. This type of syntax requires your reader to be knowledgeable about the
|
||||||
|
language. Thus, if they are, it is very readable. If they are not, then the
|
||||||
|
jury is out.
|
||||||
|
|
||||||
|
But the real issue of this syntax is that there is absolutely no type checking
|
||||||
|
whatsoever. That's not really a problem if you embrace Python's duck typing and
|
||||||
|
you are well prepared for the consequences of that choice. If you only consider
|
||||||
|
the poll question as it is formulated, and the code is properly guarded so that
|
||||||
|
you are certain that the `value` variable is a Unicode string, this approach
|
||||||
|
bares no risks.
|
||||||
|
|
||||||
|
But what if I lied and the value variable is not a string? What if it is a
|
||||||
|
bytestring? The None value? An integer? An arbitrary object? What if the code
|
||||||
|
is not properly guarded?
|
||||||
|
|
||||||
|
I mean, this can happen easily when you refactor your code or when you meddle
|
||||||
|
with some code you don't fully understand. Sure, unit tests and static analysis
|
||||||
|
are valid answers to refactoring problems... but let's be honest here:
|
||||||
|
correctly written unit tests are not frequent in the startup industry.
|
||||||
|
|
||||||
|
And mistakes happen. One of my followers told me a "war story":
|
||||||
|
|
||||||
|
{{< highlight python >}}
|
||||||
|
if i:
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
Yeah, that's it ^_^ A colleague of them wrote this test to guard against the
|
||||||
|
`None` value... but `i` was an integer. And some day, that integer equaled 0.
|
||||||
|
If you don't know, 0, in Python, is evaluated as `False` in a condition
|
||||||
|
expression. Oops.
|
||||||
|
|
||||||
|
But let's say that this integer war story is a corner case. What would happen
|
||||||
|
if the value variable was in fact an instance of a type implementing `__bool__`
|
||||||
|
or `__len__`?
|
||||||
|
|
||||||
|
In Python, truth evaluation on arbitrary object is done by trying to call
|
||||||
|
`__bool__` that should return the truth value for this object. If `__bool__` is
|
||||||
|
not implemented, `__len__` is tried. If `__len__` returns 0, then the
|
||||||
|
evaluation result is `False`, else `True`. Finally, if `__len__` is not
|
||||||
|
implemented, then it returns `True` for variable that are not `None` nor a
|
||||||
|
scalar that evaluates to `None`.
|
||||||
|
|
||||||
|
{{< highlight python >}}
|
||||||
|
class Test:
|
||||||
|
def __bool__(self):
|
||||||
|
print('>>Bool!')
|
||||||
|
return True
|
||||||
|
|
||||||
|
value = Test()
|
||||||
|
if value():
|
||||||
|
print('>>Hey!')
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
```
|
||||||
|
>>Bool!
|
||||||
|
>>Hey!
|
||||||
|
```
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
{{< highlight python >}}
|
||||||
|
class Test:
|
||||||
|
def __len__(self):
|
||||||
|
print('>>Len!')
|
||||||
|
return 1
|
||||||
|
|
||||||
|
value = Test()
|
||||||
|
if value:
|
||||||
|
print('>>Hey!')
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
```
|
||||||
|
>>Len!
|
||||||
|
>>Hey!
|
||||||
|
```
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
{{< highlight python >}}
|
||||||
|
class Test:
|
||||||
|
def __len__(self):
|
||||||
|
print('>>Len!')
|
||||||
|
return 0
|
||||||
|
|
||||||
|
value = Test()
|
||||||
|
if value():
|
||||||
|
print('>>Hey!')
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
```
|
||||||
|
>>Len!
|
||||||
|
```
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
{{< highlight python >}}
|
||||||
|
class Test: pass
|
||||||
|
|
||||||
|
value = Test()
|
||||||
|
if value:
|
||||||
|
print('>>Hey!')
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
```
|
||||||
|
>>Hey!
|
||||||
|
```
|
||||||
|
|
||||||
|
With such a high risk of confusion on the actual type of the object, I would
|
||||||
|
personally bet that there is a very high probability of having some unstable
|
||||||
|
code that will assume something about what the `value` variable is capable of,
|
||||||
|
resulting in the infamous `AttributeError: 'Foo' object has no attribute
|
||||||
|
'bar'`. Or worse: no crash and a very stupid result.
|
||||||
|
|
||||||
|
You understood it, this all boils down to the discussion about typing, and the
|
||||||
|
fact that the more checks (notably typing checks) are done statically, the more
|
||||||
|
robust the code. And that is a fact that many languages have caught on. PHP,
|
||||||
|
Python, Javascript, Perl, almost all interpreted languages (with the exception
|
||||||
|
of Ruby 😂) have adopted a type hinting syntax to help static analyzers
|
||||||
|
verify the code sanity. In Python, PEP-484 defines the syntax and you can use
|
||||||
|
MyPy to do the checking.
|
||||||
|
|
||||||
|
So, if you hinted that the type of value was `str` before the `if value:`
|
||||||
|
statement, then your code is probably safe and robust if you use a static
|
||||||
|
analyzer.
|
||||||
|
|
||||||
|
{{< highlight python >}}
|
||||||
|
def foo(value: str):
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
If you do not have a type hint and you don't use a static analyzer, then your
|
||||||
|
code is not readable because your intention about what is acceptable is not
|
||||||
|
explicit. Thus, and your code is not robust/safe because you will probably end
|
||||||
|
up crashing.
|
||||||
|
|
||||||
|
### <a name="second"></a>Second Answer
|
||||||
|
|
||||||
|
The second answer is nonsensical and I admittedly did a poor job at
|
||||||
|
representing the opinion of my colleague when writing this poll. Let's put it
|
||||||
|
on the fact that I was forced to use their solution.
|
||||||
|
|
||||||
|
`if bool(value):` is stupid because we already are in the context of a boolean
|
||||||
|
evaluation, so the bool casting is redundant. However, their opinion is not
|
||||||
|
without ground in other contexts.
|
||||||
|
|
||||||
|
For instance, in the following code, what would you replace **XXX** with?
|
||||||
|
|
||||||
|
{{< highlight python >}}
|
||||||
|
def foo() -> Tuple[str, int]:
|
||||||
|
"""some instructions"""
|
||||||
|
# ...
|
||||||
|
|
||||||
|
def bar() -> XXX:
|
||||||
|
s, i = foo()
|
||||||
|
return s and i
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
If you answered `bool`, you are wrong. **XXX** must be replaced by `Union[str,
|
||||||
|
int]`.
|
||||||
|
```
|
||||||
|
error: Incompatible return value type (got "Union[str, int]", expected "bool")
|
||||||
|
```
|
||||||
|
|
||||||
|
That's because Python logical operators do not return booleans! In the case of
|
||||||
|
the `and` operator, it returns the first value that cannot be evaluated to
|
||||||
|
`True`.
|
||||||
|
|
||||||
|
Thus, if you meant the bar function to return a boolean, you need to cast the
|
||||||
|
whole expression as bool before returning it.
|
||||||
|
|
||||||
|
{{< highlight python >}}
|
||||||
|
def bar() -> bool:
|
||||||
|
s, i = foo()
|
||||||
|
return bool(s and i)
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
You can also only cast elements that are not comparisons, because, at least,
|
||||||
|
comparisons do return booleans:
|
||||||
|
|
||||||
|
{{< highlight python >}}
|
||||||
|
def bar() -> bool:
|
||||||
|
s, i = foo
|
||||||
|
return bool(s) and i != 0
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
The fact that Python logical operators return arbitrary values is very
|
||||||
|
surprising to people that come from some other languages, such as C or even
|
||||||
|
PHP, where the logical operators return a truth value. For people coming from
|
||||||
|
very strongly typed languages such as Ocaml or Rust, this is difficult to even
|
||||||
|
consider, because logical operators are typed and would only accept booleans as
|
||||||
|
operands in the first place.
|
||||||
|
|
||||||
|
{{< highlight bash >}}
|
||||||
|
$ echo '''#include <stdio.h>
|
||||||
|
|
||||||
|
int main(){
|
||||||
|
printf("> %d\n", 2 && 3);
|
||||||
|
return 0;
|
||||||
|
}''' | gcc -o /tmp/condition -x c -
|
||||||
|
|
||||||
|
$ /tmp/condition
|
||||||
|
>1
|
||||||
|
|
||||||
|
$ php -r 'echo 2 && 3;'
|
||||||
|
1
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
Once you consider that you need to explicitly cast to bool all of your values
|
||||||
|
in a logical expression to output a boolean, the readability and
|
||||||
|
robustness/safety arguments are identical to those of the first answer.
|
||||||
|
|
||||||
|
### <a name="third"></a>Third Answer
|
||||||
|
|
||||||
|
The third answer is the first that is not relying on the Python language
|
||||||
|
specification to express a condition. It explicitly states which particular
|
||||||
|
characteristic of the content of the `value` variable is studied and must not
|
||||||
|
be equal to 0 and it returns an unambiguous result: a truth value: True or
|
||||||
|
False.
|
||||||
|
|
||||||
|
Regarding code safety, the result is mixed. It is the only answer out of the
|
||||||
|
four that will crash the program during runtime if value is the None value.
|
||||||
|
|
||||||
|
```
|
||||||
|
TypeError: object of type 'NoneType' has no len()
|
||||||
|
```
|
||||||
|
|
||||||
|
Depending on the purpose of the program, crashing early may or may not be
|
||||||
|
desirable. A ex-colleague working on machine learning told me that they would
|
||||||
|
prefer to have a program crash early because of a type bug like this, than to
|
||||||
|
have the program run for hours only to crash later with a `AttributeError:
|
||||||
|
'NoneType' object has no attribute 'foo'`.
|
||||||
|
|
||||||
|
Of course, the best option is to test the program, and check the parameters
|
||||||
|
against the API contract so that this situation never occurs. But what if the
|
||||||
|
test is broken (for instance, because it was written with the syntax from the
|
||||||
|
first answer) and it let through a value that was not intended?
|
||||||
|
|
||||||
|
Unfortunately, this answer is also susceptible to cause late crashes because of
|
||||||
|
duck typing. The `len()` function is only a keyword that calls on the `__len__`
|
||||||
|
implementation of the receiver (that is the object provided as `len()`'s
|
||||||
|
argument. This means that this test won't guard against objects implementing
|
||||||
|
`__len__`, just like `str`, but not being a string. The perfect builtin example
|
||||||
|
would be the bytestring.
|
||||||
|
|
||||||
|
{{< highlight python >}}
|
||||||
|
value = b'bonjour'
|
||||||
|
if len(value) != 0:
|
||||||
|
l = a.split('o')
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
```
|
||||||
|
TypeError: a bytes-like object is required, not 'str'
|
||||||
|
```
|
||||||
|
|
||||||
|
All in all, this answer is relatively explicit in what is tested and what's the
|
||||||
|
intended result. But even if an early crash might be desirable, a crash is not
|
||||||
|
certain because duck typing will let invalid objects go through.
|
||||||
|
|
||||||
|
### <a name="fourth"></a>Fourth Answer
|
||||||
|
|
||||||
|
The fourth answer is pretty straightforward. The meaning is crystal clear: it
|
||||||
|
evaluates to `True` as long as `value` is not the empty Unicode string. Anybody
|
||||||
|
gifted with eyes can read this expression and understand what is the intent of
|
||||||
|
the author.
|
||||||
|
|
||||||
|
It won't be abused by the empty bytestring. It won't be abused by the `None`
|
||||||
|
value. It won't be abused by objects implementing `__str__`.
|
||||||
|
|
||||||
|
{{< highlight python >}}
|
||||||
|
class Test:
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
value = Test()
|
||||||
|
if value != "":
|
||||||
|
print("Foo")
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
```
|
||||||
|
Foo
|
||||||
|
```
|
||||||
|
|
||||||
|
Wait, what? It printed `Foo`?! Oh, yeah. Right. All objects of a different type
|
||||||
|
than str, and even objects of type str if they are not the empty string will
|
||||||
|
make this condition evaluate to True. They are not equal to the empty string.
|
||||||
|
That is exactly what is expressed by the condition.
|
||||||
|
|
||||||
|
If what you really mean is that the value must be a string and not the empty
|
||||||
|
string, then, you need to write an even more explicit version of this:
|
||||||
|
|
||||||
|
{{< highlight python >}}
|
||||||
|
if isinstance(value, str) and value != "":
|
||||||
|
{{< /highlight >}}
|
||||||
|
|
||||||
|
(note: using `isinstance()` is prefered to `type(value) is str` just in case
|
||||||
|
someone got the extra-weird idea to subclass `str`; don't do that.
|
||||||
|
Unfortunately, `type(value) is str` would have been so much more readable)
|
||||||
|
|
||||||
|
## A Word About Efficiency
|
||||||
|
|
||||||
|
To my very own surprise, there is a significant difference of performance
|
||||||
|
between the four answers. The slowest take twice as much time as the fastest
|
||||||
|
(2.19 times more, to be precise).
|
||||||
|
|
||||||
|
It is in the realm of micro-optimizations but since the difference is so large,
|
||||||
|
I think the result is worth including in this post.
|
||||||
|
|
||||||
|
For 10 million iterations, run 100 times to clear out statistical noise, the
|
||||||
|
average results are:
|
||||||
|
|
||||||
|
Answer | Time | Ratio
|
||||||
|
--------|-------|-------
|
||||||
|
First | 1.28s | 1
|
||||||
|
Second | 2.81s | 2.19
|
||||||
|
Third | 1.92s | 1.5
|
||||||
|
Fourth | 1.52s | 1.19
|
||||||
|
|
||||||
|
# Conclusion
|
||||||
|
|
||||||
|
As I stated in the poll itself, all four of the answers are totally valid
|
||||||
|
Python code. However some answers will make full-time Python developers abiding
|
||||||
|
by the PEP-8 recommendations cringe. Some even implemented tools that would
|
||||||
|
whine specifically on one of the answers. That's the case of the third answer
|
||||||
|
that will make pylint, a Python code linter, complain about the use of `len()`
|
||||||
|
to test if an sequence is empty (because a `str` is also an character
|
||||||
|
sequence):
|
||||||
|
|
||||||
|
```
|
||||||
|
Do not use `len(SEQUENCE)` to determine if a sequence is empty (len-as-condition)
|
||||||
|
```
|
||||||
|
|
||||||
|
We also saw during the discussion about the advantages and drawbacks of the
|
||||||
|
first answer that if one makes a proper use of type hinting and static code
|
||||||
|
analysis (e.g. mypy), there may be little code safety problems with it; just a
|
||||||
|
readability issue because the intent of the developers about which use cases
|
||||||
|
are covered remains obscure.
|
||||||
|
|
||||||
|
Overall, my personal belief is that the fourth answer (especially when guarded
|
||||||
|
with the `isinstance()` call) should be prefered because the developer's intent
|
||||||
|
is explicit and one gets exactly what they asked for, without any of the magic
|
||||||
|
offered by the language.
|
||||||
|
|
||||||
|
---
|
||||||
|
Please post your comments as answers on [Twitter](https://twitter.com/X_Cli_Public/status/1110275080221806593) or on the [Fediverse](https://infosec.exchange/@x_cli/101813137227224905)!
|
||||||
|
|
102
posts/tacv-dev-defensif.md
Normal file
102
posts/tacv-dev-defensif.md
Normal file
|
@ -0,0 +1,102 @@
|
||||||
|
---
|
||||||
|
title: "TousAntiCovid Verif : vulnérabilité et développement défensif"
|
||||||
|
slug: tacv-vuln-dev-defensif
|
||||||
|
authors: "Florian Maury"
|
||||||
|
description: "Un article détaillant les manquements des développeurs de TousAntiCovid Verif et une liste de bonnes pratiques de développement défensif pour les éviter"
|
||||||
|
date: 2021-08-05T00:00:00+02:00
|
||||||
|
type: posts
|
||||||
|
draft: false
|
||||||
|
layout: "singletoc"
|
||||||
|
categories:
|
||||||
|
- france
|
||||||
|
tags:
|
||||||
|
- software development
|
||||||
|
- pass sanitaire
|
||||||
|
- covid19
|
||||||
|
---
|
||||||
|
|
||||||
|
L'application TousAntiCovid Verif sur Android nous a récemment régalé d'une vulnérabilité permettant d'afficher comme valides des pass sanitaires au format 2D-DOC manifestement invalides. Le bug n'intervient pas dans la vérification cryptographique de l'authenticité du pass sanitaire. Il se situe dans le reste du code métier qui vérifie la validité du pass, ne se déclenche que si l'appareil faisant tourner TousAntiCovid Verif utilise la locale anglaise.
|
||||||
|
|
||||||
|
Une [vidéo de démonstration](https://twitter.com/X_Cli_Public/status/1422240038377644038) a été publiée par l'auteur de cet article. La démonstration a été effectuée avec la version 1.6.1 de TousAntiCovid Verif. La version 1.6.2 publiée le 4 août n'a pas corrigé la vulnérabilité.
|
||||||
|
|
||||||
|
Cet article propose d'étudier le bug, ses causes, et comment se protéger de tels bugs lorsqu'on pratique le développement défensif.
|
||||||
|
|
||||||
|
## La vulnérabilité
|
||||||
|
|
||||||
|
Comme le code source de TousAntiCovid Verif n'a été [publié que très tardivement](https://gitlab.inria.fr/tousanticovid-verif/tousanticovid-verif-android/-/commit/accf4cce921b0c078c35ba8c4ae7244a1c834b17), et que la vulnérabilité était présente dès cette publication initiale, il n'est pas possible de déterminer la date d'introduction du bug. En outre, tous les commits étant fusionné sous un unique commit à l'intitulé laconique "initial commit", il n'est pas possible de faire une revue de code des changements dans le détail et de déterminer si l'auteur de ce bug a également commis d'autres impairs dans le code source de l'application. Bref, nous avons affaire à une parodie de code en sources ouvertes.
|
||||||
|
|
||||||
|
Toujours est-il que l'excellent [@Gilbsgilbs](https://twitter.com/gilbsgilbs) a détecté, puis identifié la vulnérabilité. Elle se situe à aux lignes suivantes :
|
||||||
|
|
||||||
|
* https://gitlab.inria.fr/tousanticovid-verif/tousanticovid-verif-android/-/blob/master/app/src/main/java/com/ingroupe/verify/anticovid/ui/result/ResultScanPresenterImpl.kt#L151
|
||||||
|
* https://gitlab.inria.fr/tousanticovid-verif/tousanticovid-verif-android/-/blob/master/app/src/main/java/com/ingroupe/verify/anticovid/ui/result/ResultScanPresenterImpl.kt#L261
|
||||||
|
|
||||||
|
(oui, parce que non contents de faire un bug, ils font des copier-coller du bug pour être sûrs de l'avoir dans plusieurs branches de code...)
|
||||||
|
|
||||||
|
Au niveau le plus basique de l'analyse, le hic se situe sur le fait que `status`, en anglais, ne vaut pas `Not Valid` mais `Invalid`.
|
||||||
|
|
||||||
|
Cette variable est assignée ici : https://gitlab.inria.fr/tousanticovid-verif/tousanticovid-verif-android/-/blob/master/app/src/main/java/com/ingroupe/verify/anticovid/ui/result/ResultScanPresenterImpl.kt#L590
|
||||||
|
|
||||||
|
La valeur assignée à cette variable est, elle-même, assignée ici : https://gitlab.inria.fr/tousanticovid-verif/tousanticovid-verif-android/-/blob/master/app/src/main/java/com/ingroupe/verify/anticovid/service/document/StaticDataService.kt#L68
|
||||||
|
|
||||||
|
On peut noter que la valeur est issue d'un fichier de traduction : [en français](https://gitlab.inria.fr/tousanticovid-verif/tousanticovid-verif-android/-/blob/master/app/src/main/res/values/strings.xml#L174) ou [en anglais](https://gitlab.inria.fr/tousanticovid-verif/tousanticovid-verif-android/-/blob/master/app/src/main/res/values-b+en/strings.xml#L156).
|
||||||
|
|
||||||
|
Le niveau zéro de la correction de cette vulnérabilité serait donc de remplacer `Not Valid` par `Invalid`... Mais il y a bien plus d'enseignements à tirer de cette erreur criante. Le reste de cet article s'attache à expliquer les stratégies de développement défensif visant à prévenir de ce genre de bugs.
|
||||||
|
|
||||||
|
## Tester
|
||||||
|
|
||||||
|
Le code de TousAntiCovid Verif ne semble manifestement pas faire l'objet de tests unitaires (à l'exception [de](https://gitlab.inria.fr/tousanticovid-verif/tousanticovid-verif-android/-/blob/master/decoder/src/test/java/dgca/verifier/app/decoder/X509Tests.kt) [trois](https://gitlab.inria.fr/tousanticovid-verif/tousanticovid-verif-android/-/blob/master/decoder/src/test/java/dgca/verifier/app/decoder/CertificateCheckTest.kt) [fichiers](https://gitlab.inria.fr/tousanticovid-verif/tousanticovid-verif-android/-/blob/master/decoder/src/test/java/dgca/verifier/app/decoder/QrCodeTests.kt)). Plusieurs indices le laissent penser. D'une part, il n'y a aucune trace de ces tests dans le [dépôt de code source](https://gitlab.inria.fr/tousanticovid-verif/tousanticovid-verif-android/). Pour autant, on trouve des fichiers qui sont peu pertinents comme les fichiers de proguard pour rendre le code compilé moins facilement lisible par rétro-ingénierie. Cela pourrait donc laisser à penser qu'ils ont commité tout ce qu'ils avaient (à l'exception des fichiers exclus par .gitignore, et qui ne font pas mention des tests unitaires).
|
||||||
|
|
||||||
|
Ensuite, on peut voir que le code fautif était déjà présent dans le [commit initial](https://gitlab.inria.fr/tousanticovid-verif/tousanticovid-verif-android/-/tree/accf4cce921b0c078c35ba8c4ae7244a1c834b17) tandis que le fichier de langue contenant la chaine de caractères a été introduit dans un [commit ultérieur](https://gitlab.inria.fr/tousanticovid-verif/tousanticovid-verif-android/-/commit/1a0abc923f30a25c2d50398af903473ab12bc835). Il est donc probable que le code a été poussé en production, sans même avoir de fichier de langue de test pour vérifier que le code soit fonctionnel.
|
||||||
|
|
||||||
|
La première stratégie de développement défensif, qui constitue une bonne pratique standard de l'industrie, est de tester son code.
|
||||||
|
|
||||||
|
## Utiliser les bons types (booléens) et les bonnes variables
|
||||||
|
|
||||||
|
Le code fautif effectue des comparaisons de chaines de caractères pour déterminer le statut de validité de la vérification cryptographique. La comparaison de chaines de caractère est fragile, en cela qu'elle repose sur la comparaison de nombreux octets qui doivent être tous justes pour que le test réussisse. À l'inverse, la comparaison d'une valeur booléenne ou même numérique (nulle ou non nulle, par exemple), aurait permis de discriminer de manière bien moins ambiguë et surtout moins susceptible à des erreurs comme celle vu dans TousAntiCovid Verif.
|
||||||
|
|
||||||
|
Le plus navrant avec TousAntiCovid Verif, c'est qu'il y a [dans la même structure](https://gitlab.inria.fr/tousanticovid-verif/tousanticovid-verif-android/-/blob/master/app/src/main/java/com/ingroupe/verify/anticovid/service/document/StaticDataService.kt#L67) que celle dont est extraite cette chaine de caractères, un attribut `isValid` de type booléen et qui aurait pu être avantageusement utilisé pour ce test. C'est à se demander s'il y a eu une revue de code !
|
||||||
|
|
||||||
|
## Utiliser des symboles plutôt que des littéraux
|
||||||
|
|
||||||
|
Comme discuté dans le point précédent, les chaines de caractères sont souvent des ennemis du code rigoureux. En effet, leur usage est bien souvent la cause de bugs lorsqu'elles contiennent de manière aléatoire des coquilles. Il est donc généralement considéré que leur usage sous la forme de valeurs hardcodées est une mauvaise pratique de développement. Il est généralement préférable d'utiliser des symboles de langages, comme des constantes nommées ou des [symboles](http://rubylearning.com/satishtalim/ruby_hashes.html). L'usage de symboles présente l'avantage qu'en cas de coquille dans le nom du symbole : une erreur sera levée au moment de la compilation ou de l'interprétation, suivant le langage, et la coquille ne se traduira pas par une erreur de logique métier.
|
||||||
|
Même Javascript, qui est un langage très souple, a un mode strict pour forcer la déclaration des variables, ce qui permet de détecter des coquilles dans les noms des variables.
|
||||||
|
|
||||||
|
## Utiliser l'approche liste autorisée ("blanche"), plutôt que liste interdite ("noire")
|
||||||
|
|
||||||
|
L'approche utilisée par le code fautif de TousAntiCovid Verif est celle de la liste interdite. En effet, le test vérifie que le status de validité du pass est "Non valide". Si ce test échoue, alors le statut du pass est réputé valide. Cette approche a montré ses limites en de multiples occasions lors du développement d'applications sécurisées, car il est en général bien plus difficile d'envisager l'infinité des valeurs susceptibles d'être invalides, alors que les valeurs valides sont bien souvent en quantité très limitée.
|
||||||
|
|
||||||
|
Une manière de programmer de manière défensive le test fautif aurait été d'inverser sa nature : plutôt que de tester si le statut du pass est "Non valide", il aurait été préférable de tester s'il n'était pas "Valide". Ainsi, en cas d'oubli, de coquille, ou de toute autre raison inattendue, le résultat de la validation aurait été un échec et non un succès.
|
||||||
|
|
||||||
|
## Émuler le pattern matching complet
|
||||||
|
|
||||||
|
Une méthode employée par des langages très rigoureux pour prévenir ce genre de bugs est également de proposer des instructions de contrôles exhaustives.
|
||||||
|
|
||||||
|
Par exemple, en Caml ou en Rust (parmi tant d'autres ; je ne fais que citer ceux que j'ai pratiqués), il existe du [pattern matching](https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html) dont l'exhaustivité des cas est vérifiée à la compilation. Si vous ne connaissez pas ce mécanisme, imaginez une sorte d'instruction `switch` qui refuse de compiler si tous les `case` possibles n'ont pas été énumérés.
|
||||||
|
|
||||||
|
Typiquement, ici, il aurait pu être pertinent d'utiliser une telle instruction de contrôle pour énumérer de façon exhaustive les cas "Valide", "Non valide", "Valid" et "Invalid" à l'exclusion de tout autre, sauf à encourir une erreur de compilation.
|
||||||
|
|
||||||
|
Même quand le langage ne dispose pas de telles instructions de pattern matching (ou même de switch !), il est possible de l'émuler.
|
||||||
|
|
||||||
|
|
||||||
|
Par exemple, en Python :
|
||||||
|
```
|
||||||
|
if status in ["Non valide", "Invalid"]:
|
||||||
|
handle_invalid_case()
|
||||||
|
else if status in ["Valide", "Valid"]:
|
||||||
|
handle_valid_case()
|
||||||
|
else:
|
||||||
|
raise NotImplementedError()
|
||||||
|
```
|
||||||
|
|
||||||
|
## Employer les design patterns lorsqu'ils sont pertinents
|
||||||
|
|
||||||
|
Finalement, on peut noter que les développeurs de TousAntiCovid Verif n'ont absolument pas respecté les patrons de conception ("design patterns") pourtant standard dans l'industrie. Je pense notamment à Model View Controler (MVC), [introduit dans les années 80](http://dl.acm.org/citation.cfm?id=50757.50759). En effet, les développeurs de TousAntiCovid Verif ont [initialisé l'attribut status de type string de la structure DocumentSignatureResult avec le résultat d'une interrogation du module d'internationalisation](https://gitlab.inria.fr/tousanticovid-verif/tousanticovid-verif-android/-/blob/master/app/src/main/java/com/ingroupe/verify/anticovid/service/document/StaticDataService.kt#L68). Cette initialisation est faite dès l'acquisition du code-barre 2D-DOC. Il s'agit donc d'une violation du patron MVC qui réserve l'affichage (éventuellement internationalisé) à la View, alors que l'initialisation du résultat de l'acquisition du code barre est clairement du code métier, devant être développé dans le Model.
|
||||||
|
|
||||||
|
Il est de bon aloi pour tout développeur, débutant ou moins débutant de se familiariser avec les patrons de conception classiques. Il est notamment chaudement recommandé de lire "Design Patterns: Elements of Reusable Object-Oriented Software".
|
||||||
|
|
||||||
|
## Conclusion
|
||||||
|
|
||||||
|
L'application TousAntiCovid Verif présente de nombreux signes d'une application développée sans respect des bonnes pratiques et des standards de l'industrie. L'absence de tests unitaires, et le manque de vigilance lors des revues de code (s'il y en a eu), indiquent également une très faible qualité code. Il est également permis de douter qu'un tel code ait été validé par l'ANSSI ou ait même subi un audit externe indépendant.
|
||||||
|
|
||||||
|
Compte tenu de la criticité de cette application dans la stratégie gouvernementale de déploiement universalisé du pass sanitaire comme panacée contre la COVID-19, et son installation sur les téléphones de centaines de milliers de citoyens/contrôleurs, il est regrettable de constater la faible qualité de son développement et l'incompétence manifeste de ses développeurs.
|
||||||
|
|
Loading…
Reference in a new issue