snakeyaml 2.0:解决不安全的避免漏洞
#java #opensourcesecurity

在去年12月,我们向您报告了CVE-2022-1471。在适当情况下,这个不安全的避免问题很容易导致任意代码执行。

在“深度潜水博客”文章中,我解释了该库中的问题以及如何执行它。问题的要旨是,默认情况下,Snakeyaml将传入的YAML解析为通用对象类型。这创造了一个机会,可以使课程路径上可用的其他类别化。无论抛出的ClassCastException如何,如果对象已经加载,则损坏都会造成。

影响很大程度上取决于您如何使用库。例如,许多开发人员仅使用YAML为其应用提供配置。只有当您接受来自未知来源的YAML时,这种漏洞才能利用。但是,我们简直无法预测人们将如何使用这样的库,默认设置应该是安全的。

用Snyk开源升级Snakeyaml

让我们使用Snyk开源来找到旧的SnakeYaml库的替代品。如果我与Snyk CLI本地运行snyk test,我会发现有一个可用的替代品。


Web界面还告诉我,可以使用SnykyAML 2.0版本来解决该问题。唯一的问题是Spring Boot 3仍将1.X版本发票,因此我必须在Maven或Gradle清单文件中手动替换版本。


请注意,手动将库更改为新的主要版本可能会破坏事物。不要盲目地进行这些更改,并意识到这对应用程序内部运作的潜在影响。

用Snakeyaml 2.0缓解避难所

SnakeyAML 2.0于2023年初发布,以减轻可能导致可能的任意代码执行的默认行为。在此版本中,每个新YAML()使用的构造函数现在扩展了SafeConstructor。结果,我们只能解析有限的类型。 Snakeyaml的Safeconstrutor可以构建标准Java类,例如原始类和基本类,例如字符串和地图。

默认情况下不能再解析特定类型。因此,下面我的yaml文件将提供一个例外:

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

现在,默认实现不再脆弱。但是,这是一个打破的变化,因此您的初始代码可能会不再起作用。

使用SnakeyAml 2.x时如何修复我的yaml解析逻辑

首先,我们必须确保不再使用snakeyaml 1.x。即使是最新的Spring Boot版本3.1当前也没有与Snakeyaml 2.x一起发货。这意味着我们必须在清单文件中升级它。对于Maven实施,我们可以使用POM文件among other things的一部分。在Gradle中,也可以使用dependency constraints

更新及其依赖性。

请注意,与早期版本相比,SnakeyAml 2.x会破坏API。这意味着我们必须将YAML解析实施重写为新的安全默认设备,以使其再次工作。

让我们考虑一个非常简单的域,其中有两个类:

  • 评论

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
}

如果要从上述实体创建yaml文件,则可能在使用SnakeyAml 1.x时写下类似于以下内容的内容:

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);
    }
}

这将导致以下yaml文件:

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

使用snakeyaml 2.x,!!mypackage.Person不再出现了。现在,将对象解析到yaml文件时,我们可以摆脱对象引用。但是,在此迁移之前导出的YAML文件仍然存在问题。

幸运的是,什么时候仍然可以解决此问题!解析特定对象时,您可以设置解析器需要使用的构造函数。此外,我们可以将特定的TagInspector添加到允许我们的包装标签的TagInspector中。这使您只允许允许适合对象的YAML文件,并且与先前与1.x版本创建的YAML向后兼容。

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;
        }
    }

此外,最好从YAML文件中删除对实际对象的引用或标签。在SnakeyAml的早期版本中,这已经可以通过将代表添加到您的YAML对象中,以将顶级对象的标签映射到映射中。下面,您会看到一个与Snakeyaml 2.x兼容的示例:

    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);
        }
    }

yaml文件将不再以!!mypackage.Person(或类似)开头。如果您所有的YAML文件都干净,则可以从解析器中删除Taginspector表单。

与Snyk保持最新

与库的所有版本保持最新有关,对于开源安全性至关重要。如果您直接或间接接受外部来源的YAML文件,则使用SnakeyAml 1.x版本可能会导致不必要的安全问题。

Snyk Open Source可以帮助您找到并解决这些问题,或者在必要时将您指向替代版本。 /p>


另外,如果您将GIT存储库连接到Snyk,即使没有关键的安全问题,我们也可以提供拉动请求,以使您的依赖关系保持最新状态。最好的预防是最好的,并且在图书馆中保持最新有助于您最大程度地降低脆弱性的风险,并减少由于安全问题而随附的额外工作。