L'épuisement des atomes : un problème sous-estimé
Dans le vaste univers de la sécurité informatique, certaines vulnérabilités passent souvent sous le radar, jusqu'à ce qu'elles deviennent une menace manifeste. L'épuisement des atomes en est un parfait exemple. Dans l'écosystème BEAM, qui inclut des langages comme Erlang et Elixir, cette vulnérabilité se manifeste comme une consommation incontrôlée des ressources, conduisant à environ 35,8% des CVEs recensés par la Fondation de l'écosystème Erlang (EEF).
Comprendre l'épuisement des atomes
Dans BEAM, un atome est une constante littérale, stockée dans une table d'atomes globale qui n'est pas collectée par le garbage collector. Cette table a une capacité limitée. Une fois pleine, elle provoque le crash de la machine virtuelle, rendant le système vulnérable à des attaques de déni de service (DoS).
La création dynamique d'atomes à partir de valeurs non finies, notamment des entrées utilisateur, est une porte ouverte à ces vulnérabilités. Par exemple, des fonctions telles que binary_to_atom/1 ou list_to_atom/1 en Erlang, et String.to_atom/1 en Elixir, peuvent transformer des entrées utilisateur imprévisibles en atomes, avec des conséquences potentiellement désastreuses.
Exemples concrets de vulnérabilités
Prenons un exemple simple en Elixir :
``elixir decoded_json = Jason.decode!(json_data, keys: :atoms) ``
Ici, si json_data provient d'une source externe non fiable, chaque clé JSON est convertie en un atome, risquant de remplir la table d'atomes si ces clés sont nombreuses et imprévisibles.
Stratégies pour éviter l'épuisement
La meilleure pratique consiste à éviter de créer de nouveaux atomes à l'exécution. Préfère l'utilisation de tables de correspondance explicites lorsque les valeurs acceptées sont connues :
``erlang case Scheme of <<"http">> -> http; <<"https">> -> https; _ -> error end ``
Quand une table de correspondance n'est pas pratique, utilise des variantes d'atomes existants qui génèrent une erreur au lieu de créer un nouvel atome si celui-ci n'existe pas déjà :
``erlang binary_to_existing_atom(Value) ``
En Elixir, utilise String.to_existing_atom(value) pour garantir la sécurité.
Linting et meilleures pratiques
Pour les projets Elixir, le linter Credo propose un check pour détecter les appels dangereux à String.to_atom/1 et autres. Active le check Credo.Check.Warning.UnsafeToAtom pour prévenir les vulnérabilités avant qu'elles ne deviennent des CVEs.
Conclusion
L'épuisement des atomes est une vulnérabilité facile à éviter avec les bonnes pratiques et outils. Ne te laisse pas surprendre par ce piège, souvent insidieux mais aisément contournable.
Discutons de ton projet en 15 minutes.