- 35,8 % der von der EEF CNA veröffentlichten CVEs betreffen unkontrollierten Ressourcenverbrauch, und im BEAM-Ökosystem macht wiederkehrende Atom-Erschöpfung einen großen Anteil aus
- Atom-Erschöpfung ist eine Denial-of-Service-Schwachstelle: Atoms werden nicht per Garbage Collection entfernt, sammeln sich in einer globalen Tabelle, und wenn diese Tabelle voll ist, stürzt die VM ab
- Wenn aus Daten wie Benutzereingaben, bei denen nicht garantiert ist, dass die Menge möglicher Werte endlich ist, Atoms erzeugt werden, entsteht ein DoS-Risiko; auch URI-Schemes sind keine Ausnahme
- Das Risiko besteht nicht nur bei expliziten Aufrufen wie
binary_to_atom/1oderString.to_atom/1, sondern auch bei der Atom-Dekodierung von JSON-Schlüsseln und dynamischer Erzeugung auf Basis von String-Interpolation - Sichere Verarbeitung bedeutet, die Erzeugung neuer Atoms zur Laufzeit zu vermeiden, bekannte Werte auf explizite Lookup-Tabellen oder die
to_existing_atom-Familie zu beschränken und dies mit einem Linter zu prüfen
Denial-of-Service-Schwachstellen durch Atom-Erschöpfung
- Von den durch die EEF CNA veröffentlichten CVEs entfallen 35,8 % auf unkontrollierten Ressourcenverbrauch; im BEAM-Ökosystem machen wiederkehrende Probleme mit Atom-Erschöpfung einen großen Anteil aus {p:36}
- Die aktuelle Verteilung ist auf der Seite Common Weaknesses der EEF CNA zu sehen
- Atom-Erschöpfung ist eine Denial-of-Service-(DoS)-Schwachstelle
- Atoms werden nicht per Garbage Collection entfernt
- Sie werden in einer globalen Atom-Tabelle gespeichert
- Wenn die Tabelle voll ist, stürzt die VM ab
- Werden Atoms aus nicht endlichen Werten erzeugt, insbesondere aus Benutzereingaben, entsteht ein potenzieller DoS
- Das Risiko ist nicht auf offensichtliche Aufrufe beschränkt
- In Erlang:
binary_to_atom/1,list_to_atom/1 - In Elixir:
String.to_atom/1,List.to_atom/1
- In Erlang:
- Es gibt auch weniger auffällige Risikomuster
- Dynamische Atom-Erzeugung per Interpolation in Erlang:
% Erlang: 보간을 통한 동적 atom 생성 list_to_atom("field_" ++ UserInput) - Dekodierung von JSON-Schlüsseln als Atoms in Elixir:
# Elixir: JSON을 atom 키로 디코딩 Jason.decode(json, keys: :atoms) - Dynamische Atom-Erzeugung per Interpolation in Elixir:
# Elixir: 보간을 통한 동적 atom 생성 :"field_#{user_input}"
- Dynamische Atom-Erzeugung per Interpolation in Erlang:
Sichere Vorgehensweisen und Prüfpunkte
- Schwachstellen durch Atom-Erschöpfung entstehen nicht nur durch bloße Unachtsamkeit, sondern häufig in Code, der annimmt, Eingaben seien kontrolliert oder endlich
- URI-Schemes sind ein typisches Beispiel
- Es kann sich so anfühlen, als gäbe es nur wenige zu verarbeitende Schemes
- Kommt der Wert aus externer Eingabe, ist nicht mehr garantiert, dass die mögliche Menge endlich ist
- Code, der aus Eingaben Atoms erzeugt, ist nur dann sicher, wenn die Menge möglicher Werte endlich, bekannt und durchgesetzt ist
- Der sicherste Ansatz ist, zur Laufzeit keine neuen Atoms zu erzeugen
- Wenn zulässige Werte bekannt sind, ist die Verwendung einer expliziten Lookup-Tabelle sicherer
% Erlang case Scheme of <<"http">> -> http; <<"https">> -> https; _ -> error end - Wenn eine Lookup-Tabelle nicht praktikabel ist, sollten Varianten verwendet werden, die keine neuen Atoms erzeugen, sondern nur bestehende Atoms nutzen
- Diese Funktionen erzeugen keine neuen Atoms, sondern werfen einen Fehler
% Erlang binary_to_existing_atom(Value) list_to_existing_atom(Value)# Elixir String.to_existing_atom(value) List.to_existing_atom(value) - Ein Linter hilft dabei, Risikomuster zu erkennen, bevor sie zu Schwachstellen werden
- In Elixir-Projekten kann man erwägen, Credo.Check.Warning.UnsafeToAtom von Credo zu aktivieren
- Diese Prüfung markiert unsichere Aufrufe von
String.to_atom/1,List.to_atom/1,Module.concat/1,2sowieJason.decode/2mitkeys: :atoms - Die Prüfung ist standardmäßig deaktiviert
- Maintainer von Erlang- oder Elixir-Projekten sollten nach Code suchen, der Atoms aus Binärdaten, Strings, JSON-Schlüsseln, URI-Bestandteilen, Headern und Konfigurationswerten erzeugt
- Diese Schwachstellenklasse gehört zu den Typen, die sich oft leicht beheben lassen, bevor sie zu einer CVE werden
- Ausführlichere Hinweise sind im Leitfaden zur Vermeidung von Atom-Erschöpfung der EEF Security Working Group zusammengefasst
1 Kommentare
Lobste.rs-Kommentare
Klingt ähnlich wie die Situation in Ruby, bevor
Symbolgarbage-collected wurdeIch verstehe den Titel nicht. Das sieht definitiv nach einer footgun aus
Wenn man denkt: „Hat Ruby nicht Symbole wie Erlang-Atoms?“, dann stimmt das zwar, aber Ruby sammelt Symbole per Garbage Collection ein
Außerdem erlaubt die Lookup-Tabelle, in der Erlang-Atoms standardmäßig gespeichert werden, maximal 1.048.576 Einträge
Wenn man Atoms dynamisch aus Benutzereingaben wie Formularen erzeugt, ist das sehr riskant, und die Software wird anfällig für Denial-of-Service-Angriffe
Allerdings ist „footgun“ meiner Erfahrung nach selbst schon ein ziemlich weiter Begriff, daher wirkt die Formulierung im Titel so oder so etwas seltsam
Es überrascht mich, weil es so klingt, als sei irgendein zentraler Teil des Designs oder der Implementierung schlecht. Bei einer Sprache, die im Internet ständig gelobt wird, ist das umso unerwarteter
Dieser Tabelle Referenzzählung hinzuzufügen wäre teuer und würde die Skalierungseigenschaften von Code verändern, der seit Jahrzehnten existiert
Die maximale Anzahl von Atoms liegt standardmäßig bei 1 Million und wird beim Start der VM festgelegt
Es ist eine Falle, aber keine, die schwer zu vermeiden wäre. Seit Langem lautet die Empfehlung: „Erzeuge keine Atoms aus Benutzereingaben.“
Wenn man zum Beispiel JSON parst, wandelt man Schlüssel üblicherweise entweder nicht in Atoms um oder nur dann, wenn das Atom bereits existiert. So kann man mit Atom-Schlüsseln Pattern Matching machen, diese Atoms werden bereits beim Laden des Codes erzeugt, und ein Catch-all-Zweig kann statt eines Atoms einen String annehmen
Elixir ist deutlich massentauglicher, daher ist es gut möglich, dass Erlang-Entwickler das wissen, Elixir-Entwickler aber nicht
Persönlich finde ich es ohnehin merkwürdig, Atoms so zu verwenden. Ich verstehe Erlang-Atoms grob als etwas Ähnliches wie den Enum-Typ in C
Als eine Komfortfunktion, bei der man ein Wort auf bestimmte Weise schreibt und intern ein Enum daraus wird
Im Artikel werden Benutzereingaben erwähnt, aber ich verstehe nicht, warum es überhaupt einen Anwendungsfall geben sollte, in dem man aus Benutzereingaben neue Enum-Werte erzeugen möchte. Das scheint mir extrem eingeschränkt zu sein
In den Nachbarkommentaren geht es ums Parsen, aber idealerweise parst man doch vorab bekannte Datenstrukturen, oder? Ich habe das Gefühl, mir entgeht etwas
In dieser Sprache hat es mindestens zwei Vorteile, Symbole als eigenen Typ zu haben und nicht nur als schnelle Gleichheitsvergleiche. Symbole sind atomar, also keine listenartigen Sequenzen von Zeichen, und deshalb behandeln verschiedene Operatoren sie anders; außerdem sind Symbole vektorisiert und können dicht in Listen eines einzigen Typs gespeichert werden
In K und Q ist es sehr wünschenswert, Spalten von Datenbanktabellen als vektorisierte Typen darzustellen. Das verbessert die Lokalität, nutzt Speicher effizienter und eröffnet für viele Operatoren schnelle Pfade. Wegen der Einschränkungen der Symboltabelle muss man aber vorsichtig sein, wenn man Symbole für Spalten mit hoher Kardinalität verwendet
Wenn man JSON mit bekanntem Schema parst, sind Symbole hervorragende Dictionary-Schlüssel und in k2/k3 praktisch unverzichtbar. Aber bei JSON unbekannter Herkunft dürfen sie nicht aus Benutzereingaben kommen
In einigen K-Dialekten ist die Länge von Symbolen kurz begrenzt, sodass sie in einen 64-Bit-Wert gepackt und transportiert werden können. Dadurch gibt man Allgemeinheit auf, braucht aber überhaupt keine Symboltabelle mehr
Diese Unterscheidung zwischen „kontrollierter Eingabe“ und „unkontrollierter Eingabe“ fühlt sich in der Sicherheit ungefähr so an wie die Frage, ob etwas
nullist oder nichtWenn ich Einträge sehe wie „
webpack-plugin-less-cssverursacht einen Denial of Service, wenn es eine nicht vertrauenswürdige CSS-Datei erhält“, spüre ich schon eine deutliche CVE-MüdigkeitTrotzdem wäre hier eine bessere Kennzeichnung der Grenzen hilfreich. Zum Beispiel wäre es gut, wenn man auch Kompositionsregeln besser behandeln könnte, etwa welche Sicherheitseigenschaften beim Verketten von Strings erhalten bleiben
Und wenn jemand eine ganze Menge Dinge aus einem HTTP-POST als
SafeStringmarkiert hat, dann ist das zu einem gewissen Grad auch die eigene Verantwortung