应该采取哪些步骤来升级Sping Boot 2.x应用程序,并使其能够使用SnakeYAML 2.0而不是其当前使用的易受攻击的snakeyaml版本?
icnyk63a1#
我想为社区贡献一个解决方案,解决许多人面临的一个共同问题。这个问题源于一个事实,即在spring Boot 中使用的yaml解析器snakeyaml v1.33存在已知的安全漏洞。虽然有些人可能会认为这些漏洞是基于snakeyaml在其应用程序中使用的方式的误报,它们仍然存在潜在的安全风险。唯一的解决方案是升级到Sping Boot 2.7.10或包含修复的更高版本,允许您将snakeyaml的版本升级到2.0。如果您要在2.7版本以下的任何版本的spring Boot 上升级到snakeyaml 2.0版本。10你会得到以下错误:
java.lang.NoSuchMethodError: org.yaml.snakeyaml.representer.Representer: method 'void <init>()' not found
我想提供一个解决方案来解决这个问题。假设你不能升级到新版本的spring Boot ,并且使用基于属性的配置不是一个可行的选择。请使用下面的代码允许在你的项目中配置snakeyaml 2.0。
package com.example.demo.yaml; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.regex.Pattern; import java.util.stream.Collectors; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.BaseConstructor; import org.yaml.snakeyaml.constructor.Constructor; import org.yaml.snakeyaml.constructor.SafeConstructor; import org.yaml.snakeyaml.error.Mark; import org.yaml.snakeyaml.nodes.CollectionNode; import org.yaml.snakeyaml.nodes.MappingNode; import org.yaml.snakeyaml.nodes.Node; import org.yaml.snakeyaml.nodes.NodeTuple; import org.yaml.snakeyaml.nodes.ScalarNode; import org.yaml.snakeyaml.nodes.Tag; import org.yaml.snakeyaml.representer.Representer; import org.yaml.snakeyaml.resolver.Resolver; import org.springframework.beans.factory.config.YamlProcessor; import org.springframework.boot.origin.Origin; import org.springframework.boot.origin.OriginTrackedValue; import org.springframework.boot.origin.TextResourceOrigin; import org.springframework.boot.origin.TextResourceOrigin.Location; import org.springframework.core.io.Resource; import org.springframework.util.ReflectionUtils; /** * Class to load {@code .yml} files into a map of {@code String} to * {@link OriginTrackedValue}. * * @author Madhura Bhave * @author Phillip Webb */ class CustomOriginTrackedYamlLoader extends YamlProcessor { private static final boolean HAS_RESOLVER_LIMIT = ReflectionUtils.findMethod(Resolver.class, "addImplicitResolver", Tag.class, Pattern.class, String.class, int.class) != null; private final Resource resource; CustomOriginTrackedYamlLoader(Resource resource) { this.resource = resource; setResources(resource); } @Override protected Yaml createYaml() { LoaderOptions loaderOptions = new LoaderOptions(); loaderOptions.setAllowDuplicateKeys(false); loaderOptions.setMaxAliasesForCollections(Integer.MAX_VALUE); loaderOptions.setAllowRecursiveKeys(true); return createYaml(loaderOptions); } private Yaml createYaml(LoaderOptions loaderOptions) { BaseConstructor constructor = new OriginTrackingConstructor(loaderOptions); DumperOptions dumperOptions = new DumperOptions(); Representer representer = new Representer(dumperOptions); Resolver resolver = HAS_RESOLVER_LIMIT ? new NoTimestampResolverWithLimit() : new NoTimestampResolver(); return new Yaml(constructor, representer, dumperOptions, loaderOptions, resolver); } List<Map<String, Object>> load() { final List<Map<String, Object>> result = new ArrayList<>(); process((properties, map) -> result.add(getFlattenedMap(map))); return result; } /** * {@link Constructor} that tracks property origins. */ private class OriginTrackingConstructor extends SafeConstructor { OriginTrackingConstructor(LoaderOptions loadingConfig) { super(loadingConfig); } @Override public Object getData() throws NoSuchElementException { Object data = super.getData(); if (data instanceof CharSequence && ((CharSequence) data).length() == 0) { return null; } return data; } @Override protected Object constructObject(Node node) { if (node instanceof CollectionNode && ((CollectionNode<?>) node).getValue().isEmpty()) { return constructTrackedObject(node, super.constructObject(node)); } if (node instanceof ScalarNode) { if (!(node instanceof CustomOriginTrackedYamlLoader.KeyScalarNode)) { return constructTrackedObject(node, super.constructObject(node)); } } if (node instanceof MappingNode) { replaceMappingNodeKeys((MappingNode) node); } return super.constructObject(node); } private void replaceMappingNodeKeys(MappingNode node) { node.setValue(node.getValue().stream().map(CustomOriginTrackedYamlLoader.KeyScalarNode::get).collect(Collectors.toList())); } private Object constructTrackedObject(Node node, Object value) { Origin origin = getOrigin(node); return OriginTrackedValue.of(getValue(value), origin); } private Object getValue(Object value) { return (value != null) ? value : ""; } private Origin getOrigin(Node node) { Mark mark = node.getStartMark(); Location location = new Location(mark.getLine(), mark.getColumn()); return new TextResourceOrigin(CustomOriginTrackedYamlLoader.this.resource, location); } } /** * {@link ScalarNode} that replaces the key node in a {@link NodeTuple}. */ private static class KeyScalarNode extends ScalarNode { KeyScalarNode(ScalarNode node) { super(node.getTag(), node.getValue(), node.getStartMark(), node.getEndMark(), node.getScalarStyle()); } static NodeTuple get(NodeTuple nodeTuple) { Node keyNode = nodeTuple.getKeyNode(); Node valueNode = nodeTuple.getValueNode(); return new NodeTuple(CustomOriginTrackedYamlLoader.KeyScalarNode.get(keyNode), valueNode); } private static Node get(Node node) { if (node instanceof ScalarNode) { return new CustomOriginTrackedYamlLoader.KeyScalarNode((ScalarNode) node); } return node; } } /** * {@link Resolver} that limits {@link Tag#TIMESTAMP} tags. */ private static class NoTimestampResolver extends Resolver { @Override public void addImplicitResolver(Tag tag, Pattern regexp, String first) { if (tag == Tag.TIMESTAMP) { return; } super.addImplicitResolver(tag, regexp, first); } } /** * {@link Resolver} that limits {@link Tag#TIMESTAMP} tags. */ private static class NoTimestampResolverWithLimit extends Resolver { @Override public void addImplicitResolver(Tag tag, Pattern regexp, String first, int limit) { if (tag == Tag.TIMESTAMP) { return; } super.addImplicitResolver(tag, regexp, first, limit); } } }
package com.example.demo.yaml; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; import org.springframework.boot.env.OriginTrackedMapPropertySource; import org.springframework.boot.env.PropertySourceLoader; import org.springframework.core.env.PropertySource; import org.springframework.core.io.Resource; import org.springframework.util.ClassUtils; /** * Strategy to load '.yml' (or '.yaml') files into a {@link PropertySource}. * * @author Dave Syer * @author Phillip Webb * @author Andy Wilkinson * @since 1.0.0 */ public class CustomYamlPropertySourceLoader implements PropertySourceLoader { @Override public String[] getFileExtensions() { return new String[] { "yml", "yaml" }; } @Override public List<PropertySource<?>> load(String name, Resource resource) throws IOException { if (!ClassUtils.isPresent("org.yaml.snakeyaml.Yaml", getClass().getClassLoader())) { throw new IllegalStateException( "Attempted to load " + name + " but snakeyaml was not found on the classpath"); } List<Map<String, Object>> loaded = new CustomOriginTrackedYamlLoader(resource).load(); if (loaded.isEmpty()) { return Collections.emptyList(); } List<PropertySource<?>> propertySources = new ArrayList<>(loaded.size()); for (int i = 0; i < loaded.size(); i++) { String documentNumber = (loaded.size() != 1) ? " (document #" + i + ")" : ""; propertySources.add(new OriginTrackedMapPropertySource(name + documentNumber, Collections.unmodifiableMap(loaded.get(i)), true)); } return propertySources; } }
src/main/resources/META-INF/spring.factories
# PropertySource Loaders org.springframework.boot.env.PropertySourceLoader=\ org.springframework.boot.env.PropertiesPropertySourceLoader,\ com.example.demo.yaml.CustomYamlPropertySourceLoader
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.8</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>17</java.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.yaml</groupId> <artifactId>snakeyaml</artifactId> <version>2.0</version> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
链接到github:Spring-Boot-SnakeYAML2-Upgrade
1条答案
按热度按时间icnyk63a1#
我想为社区贡献一个解决方案,解决许多人面临的一个共同问题。这个问题源于一个事实,即在spring Boot 中使用的yaml解析器snakeyaml v1.33存在已知的安全漏洞。虽然有些人可能会认为这些漏洞是基于snakeyaml在其应用程序中使用的方式的误报,它们仍然存在潜在的安全风险。唯一的解决方案是升级到Sping Boot 2.7.10或包含修复的更高版本,允许您将snakeyaml的版本升级到2.0。如果您要在2.7版本以下的任何版本的spring Boot 上升级到snakeyaml 2.0版本。10你会得到以下错误:
我想提供一个解决方案来解决这个问题。假设你不能升级到新版本的spring Boot ,并且使用基于属性的配置不是一个可行的选择。请使用下面的代码允许在你的项目中配置snakeyaml 2.0。
src/main/resources/META-INF/spring.factories
链接到github:Spring-Boot-SnakeYAML2-Upgrade