Attestation COVID-19 en ligne : un outil de surveillance globale sur Internet ?

Cet article est sous licence CC-BY-NC-ND.

Les auteurs de cet article peuvent être contactés sur :

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. 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, 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, 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 ; 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, 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 : 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 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 et présente des risques d’atteinte à la vie privée. 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, 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.

Python String Emptiness Test

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:

def should_i_do_smth():
    # ...
    return bool(some_string) and some_int == 0

I wrote that:

def should_i_do_smth():
    # ...
    return len(some_string) != 0 and some_int == 0

The only use case of that function was in an if condition:

if should_i_do_smth():

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.

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 :

char * value;
... instructions ...
if (value && *value) {

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”:

if i:

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.

class Test:
    def __bool__(self):
        print('>>Bool!')
        return True

value = Test()
if value():
    print('>>Hey!')
>>Bool!
>>Hey!

class Test:
    def __len__(self):
        print('>>Len!')
        return 1

value = Test()
if value:
    print('>>Hey!')
>>Len!
>>Hey!

class Test:
    def __len__(self):
        print('>>Len!')
        return 0

value = Test()
if value():
    print('>>Hey!')
>>Len!

class Test: pass

value = Test()
if value:
    print('>>Hey!')
>>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.

def foo(value: str):

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.

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?

def foo() -> Tuple[str, int]:
    """some instructions"""
    # ...
 
def bar() -> XXX:
    s, i = foo()
    return s and i

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.

def bar() -> bool:
    s, i = foo()
    return bool(s and i)

You can also only cast elements that are not comparisons, because, at least, comparisons do return booleans:

def bar() -> bool:
    s, i = foo
    return bool(s) and i != 0

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.

$ 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

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.

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.

value = b'bonjour'
if len(value) != 0:
    l = a.split('o')
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.

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__.

class Test:
    def __str__(self) -> str:
        return ""

value = Test()
if value != "":
    print("Foo")
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:

if isinstance(value, str) and value != "":

(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 or on the Fediverse!

About

My name is Florian Maury. I am a network and protocol security specialist.

My resume

My social media links:

A PKI Rant: the Free (as in free beer) Certificate Problem

Intro

Someone asked me what I had to object to this article 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 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.