Skip to main content

SnakeYaml 2.0: Die Schwachstelle durch unsichere Deserialisierung beheben

Artikel von
blog feature supply chain sbom

21. Juni 2023

0 Min. Lesezeit

Im Dezember letzten Jahres haben wir Ihnen CVE-2022-1471 gemeldet. Dieses Problem mit unsicherer Deserialisierung konnte unter den richtigen Umständen leicht zur Ausführung beliebigen Codes führen.

Im ausführlichen Blogbeitrag „Schwachstelle durch unsichere Deserialisierung in SnakeYaml (CVE-2022-1471)“ habe ich die Probleme in dieser Bibliothek und ihre Ausnutzung erläutert. Das Problem bestand im Wesentlichen darin, dass SnakeYaml eingehendes YAML standardmäßig in den generischen Objekttyp einlas. Dadurch konnten andere Klassen des Klassenpfads deserialisiert werden. Unabhängig von der ausgelösten ClassCastException ist der Schaden bereits angerichtet, sobald das Objekt geladen wurde.

Die Auswirkungen hängen stark davon ab, wie Sie die Bibliothek verwenden. Viele Entwicklerinnen und Entwickler nutzen YAML beispielsweise nur, um ihre Apps zu konfigurieren. Diese Schwachstelle lässt sich nur ausnutzen, wenn Sie YAML aus unbekannten Quellen akzeptieren – etwa von Endnutzern. Dennoch können wir nicht vorhersehen, wie eine Bibliothek wie diese verwendet wird, und die Standardeinstellung sollte sicher sein.

SnakeYaml mit Snyk Open Source aktualisieren

Verwenden wir Snyk Open Source, um einen Ersatz für die alte SnakeYaml-Bibliothek zu finden. Wenn ich lokal mit der Snyk CLI snyk test ausführe, sehe ich, dass ein Ersatz verfügbar ist.

Terminalausgabe mit Warnung vor beliebiger Codeausführung in org.yaml:snakeyaml 1.33 und dem Hinweis, dass das Problem in Version 2.0 behoben wurde

Auch die Weboberfläche zeigt mir, dass eine Version von SnykYaml 2.0 verfügbar ist, mit der sich das Problem beheben lässt. Das einzige Problem: Spring Boot 3 enthält weiterhin die Version 1.x. Daher muss ich die Version manuell in meiner Maven- oder Gradle-Manifestdatei ersetzen.

Snyk-Schwachstellendetails für org.yaml:snakeyaml mit Hinweisen auf beliebige Codeausführung, CVE-2022-1471, mittlerem Schweregrad und einem Score von 437.

Beachten Sie, dass die manuelle Aktualisierung auf eine neue Hauptversion zu Problemen führen kann. Nehmen Sie diese Änderungen nicht unüberlegt vor, und berücksichtigen Sie die möglichen Auswirkungen auf das Innenleben Ihrer Anwendung.

Deserialisierung mit SnakeYaml 2.0 entschärfen

SnakeYaml 2.0 wurde Anfang 2023 veröffentlicht, um das Standardverhalten zu ändern, das zur Ausführung beliebigen Codes führen kann. In dieser Version erweitert der Konstruktor, den jedes neue yaml() verwendet, nun SafeConstructor. Dadurch können wir nur noch eine begrenzte Auswahl an Typen parsen. SnakeYamls SafeConstrutor kann Standard-Java-Klassen wie primitive Typen und grundlegende Klassen wie String und Map erstellen.

Spezifische Typen lassen sich standardmäßig nicht mehr parsen. Daher führt die folgende YAML-Datei zu einer Ausnahme:

!!Gadget ["env"]
Exception in thread "main" Global tag is not allowed: tag:yaml.org,2002:Gadget
 in 'reader', line 1, column 1:
    !!Gadget ["env"]
    ^

Die Standardimplementierung ist nun nicht mehr anfällig. Allerdings handelt es sich um eine Änderung, die nicht abwärtskompatibel ist. Daher funktioniert Ihr bisheriger Code vermutlich nicht mehr.

So beheben Sie Ihre YAML-Parsing-Logik mit SnakeYaml 2.x

Zunächst müssen wir sicherstellen, dass wir SnakeYaml 1.x nicht mehr verwenden. Selbst die aktuelle Version von Spring Boot 3.1 enthält derzeit noch kein SnakeYaml 2.x. Das bedeutet, dass wir es selbst in unserer Manifestdatei aktualisieren müssen. Bei Maven-Projekten können wir dafür unter anderem den Abschnitt <dependencyManagement> der pom-Datei verwenden – neben weiteren Möglichkeiten. In Gradle lassen sich transitive Abhängigkeiten ebenfalls mithilfe von Abhängigkeitsbedingungen aktualisieren.

Beachten Sie, dass SnakeYaml 2.x im Vergleich zu früheren Versionen API-Änderungen enthält. Damit die YAML-Parsing-Implementierung wieder funktioniert, müssen wir sie an die neuen sicheren Standardeinstellungen anpassen.

Betrachten wir eine sehr einfache Domäne mit zwei Klassen:

  • Person

  • Kommentar

Person.java:

public class Person {
    private String name;
    private int age;

    private Comment comment;

    public Person() {
    }

    public Person(String name, int age, Comment class2) {
        this.name = name;
        this.age = age;
        this.comment = class2;
    }
    //getters and setters
}

Comment.java:

public class Comment {

    private String text;
    private String dateTime;

    public Comment() {}

    public Comment(String text) {
        this.text = text;
        this.dateTime = LocalDateTime.now().toString();
    }
    //getters and setters
}

Wenn Sie aus einer Entität wie der oben gezeigten eine YAML-Datei erstellen möchten, haben Sie mit SnakeYaml 1.x vermutlich etwas Ähnliches wie den folgenden Code geschrieben:

var john = new Person("John", 31, new Comment("This is a comment"));
dumpYaml(john, "file.yaml");

public static void dumpYaml(Person pojo, String filename) throws IOException {
    Yaml yaml = new Yaml();

    try (FileWriter writer = new FileWriter(filename)) {
        yaml.dump(pojo, writer);
    }
}

Das ergibt die folgende YAML-Datei:

!!mypackage.Person
age: 31
comment: {dateTime: '2023-06-15T16:53:23.175989', text: This is a comment}
name: John

Mit SnakeYaml 2.x wird !!mypackage.Person nicht mehr akzeptiert. Beim Parsen des Objekts in eine YAML-Datei können wir nun die Objektreferenz entfernen. Bei YAML-Dateien, die vor dieser Migration exportiert wurden, besteht jedoch weiterhin ein Problem.

Zum Glück lässt sich auch dieses Problem lösen! Wenn Sie in einen bestimmten Objekttyp parsen, können Sie den Konstruktor festlegen, den der Parser verwenden soll. Außerdem können wir den TagInspector zu den LoaderOptions hinzufügen, um unser Paket-Tag zuzulassen. So können Sie nur YAML-Dateien zulassen, die Ihrem Objekt entsprechen und zugleich abwärtskompatibel mit den zuvor mit Version 1.x erstellten YAML-Dateien sind.

public static Person parseYaml(String filename) throws IOException {
        var loaderoptions = new LoaderOptions();
        TagInspector taginspector = 
                tag -> tag.getClassName().equals(Person.class.getName());
        loaderoptions.setTagInspector(taginspector);

        Yaml yaml = new Yaml(new Constructor(Person.class, loaderoptions));

        try (InputStream in = new FileInputStream(filename)) {
            // Parse the YAML file into a mypackage.MyYamlClass object
            Person obj = yaml.load(in);
            return obj;
        }
    }

Noch besser wäre es, die Referenz oder das Tag des eigentlichen Objekts vollständig aus Ihrer YAML-Datei zu entfernen. Das war bereits in früheren Versionen von SnakeYaml möglich: Dazu wird dem YAML-Objekt ein Representer hinzugefügt, der das Tag des obersten Objekts auf Map abbildet. Im Folgenden sehen Sie ein Beispiel, das mit SnakeYaml 2.x kompatibel ist:

    public static void dumpYaml(Person pojo, String filename) throws IOException {
        Representer customRepresenter = new Representer(new DumperOptions());
        customRepresenter.addClassTag(Person.class, Tag.MAP);

        Yaml yaml = new Yaml(new Constructor(Person.class, new LoaderOptions()), 
                customRepresenter);

        try (FileWriter writer = new FileWriter(filename)) {
            yaml.dump(pojo, writer);
        }
    }

Die YAML-Datei beginnt dann nicht mehr mit !!mypackage.Person (oder einem ähnlichen Eintrag). Wenn alle Ihre YAML-Dateien sauber sind, können Sie den TagInspector aus Ihrem Parser entfernen.

Mit Snyk auf dem neuesten Stand bleiben

Für die Open-Source-Sicherheit ist es entscheidend, alle Versionen Ihrer Bibliotheken auf dem neuesten Stand zu halten. Die Verwendung von SnakeYaml 1.x kann zu unnötigen Sicherheitsproblemen führen, wenn Sie YAML-Dateien direkt oder indirekt aus externen Quellen akzeptieren.

Snyk Open Source hilft Ihnen, diese Probleme zu finden und zu beheben oder bei Bedarf auf eine alternative Version hinzuweisen – wie im folgenden Beispiel, das zeigt, dass ein Update auf mindestens SnakeYaml 2.0 die Schwachstelle zur Ausführung beliebigen Codes beseitigt.

Snyk-Schwachstellendetails zu org.yaml:snakeyaml mit Informationen zu beliebiger Codeausführung, CVE-2022-1471, mittlerem Schweregrad und betroffenen Versionen.

Wenn Sie außerdem Ihr Git-Repository mit Snyk verbinden, können wir Ihnen Pull Requests bereitstellen, damit Ihre Abhängigkeiten auf dem neuesten Stand bleiben – auch wenn kein kritisches Sicherheitsproblem vorliegt. Vorbeugen ist besser als heilen. Indem Sie Ihre Bibliotheken aktuell halten, senken Sie das Risiko von Schwachstellen und vermeiden den zusätzlichen Aufwand, der bei Updates aufgrund eines Sicherheitsproblems entsteht.

Kommentar eines Snyk-Bots zu einem Pull Request mit der Empfehlung, ein Upgrade von Version 1.7.0 auf 1.8.1 durchzuführen, damit die Abhängigkeiten aktuell bleiben.

Starten Sie mit Capture-the-Flag

Erfahren Sie in unserem virtuellen On-Demand-Workshop für Einsteiger, wie Sie Capture-the-Flag-Challenges lösen.

Weiterlesen

feature insights context
Blog

Autonome Angriffe sind bereits Realität. Die Verteidigung muss Schritt halten.

Autonome Angreifer verkürzen das Zeitfenster für die Verteidigung. Erfahren Sie, wie kontinuierliches Erkennen, Beheben, Validieren und Verhindern Sicherheitsteams helfen kann, Schritt zu halten.

feature insights context
Blog

Ist Prävention im Grunde ein gelöstes Problem?

Die Prävention in von Agenten generiertem Code ist architektonisch gelöst – die Herausforderung besteht jedoch weiterhin darin, Kontrollen zu wählen, die die Sicherheit schützen, ohne die Entwicklung zu verlangsamen.

Live Stream

Remediation-Agenten verständlich erklärt: Warum Beheben besser ist als Finden

Erfahren Sie, wie der Remediation Agent von Snyk Sicherheitsinformationen, Analysen zur Ausnutzbarkeit und Validierung nutzt, um Schwachstellen in zusammenführbare Pull Requests zu verwandeln.