Traditional Java stacks were engineered for monolithic applications with long startup times and large memory requirements. In today's world powered by cloud, containers, and Kubernetes, Java frameworks have evolved to meet these new requirements.
Modern frameworks such as Quarkus enable Java developers to write applications for a cloud-native world. Their goal is to make Java the leading platform in Kubernetes and serverless environments while offering developers a framework to address a wider range of distributed application architectures. These frameworks can be used to create microservices-based applications written in Java that run in Red Hat OpenShift and serverless environments.
[ Download A Java developer's guide to Quarkus. ]
Applications compiled to native executables have small memory footprints and fast startup times. When you are trying to achieve a faster startup time and smaller memory footprint, it's unlikely the framework will have all the artifacts confined to a zip archive. Which artifacts the user application's pom.xml (Project Object Model) file requires is determined by an Apache Maven dependency mechanism and pulled from the configured Maven repository.
Since all these artifacts depend on the user's application, managing them with all the dependencies is crucial from a security point of view.
What are dependencies?
Imagine you are writing some business code to manage logging. You can implement this logic in the project or use a library, such as slf4j. It often makes sense for Java developers to use existing libraries that solve similar problems. This practice minimizes the amount of code developers need to write and encourages reuse. These libraries are the application's dependencies.
Maven has two types of dependencies:
- Direct dependencies: These dependencies are explicitly included in a
pom.xmlfile in the <dependencies> section. - Transitive dependencies: A project included as a dependency in a larger project can declare its own dependencies in a
pom.xmlfile. These dependencies are then considered transitive dependencies to the primary project. When Maven pulls a direct dependency, it also pulls its transitive dependencies.
[ Learn more about how to install, configure, and use Maven. ]
What is the problem?
For multimodule projects and applications with hundreds of modules, it is very common to have a dependency on multiple versions of the same artifact. Determining and controlling the dependencies and their versions in such projects is not easy because they involve multiple factors, like which version is deeper (Dv 2.0), which one is nearer (Dv 1.0), and which comes earlier (Dv 2.0) in the dependency tree.
A
├── B
│ └── C
│ └── Dv 2.0
├── E
│ └── Dv 3.0
│
└── Dv 1.0
Maven has its own mechanism to determine what dependency version should be pulled. Refer to Maven's Dependency Mechanism to learn more about how it works. Even the sequence in which the pom.xml file lists dependencies matters in this mechanism.
The Maven repository often makes multiple versions of many artifacts available. Since the dependencies and versions to pull rely entirely on the user application's pom.xml file, you must have something to guide Maven to pull the newer and safer versions of the dependencies.
Scan dependencies using Maven's plugin
One part of the problem is identifying the safer versions of dependencies. You can address this by using a Software Composition Analysis (SCA) tool to help detect publicly disclosed vulnerabilities within a project's dependencies. OWASP Dependency-Check can be used to scan applications (and their dependent libraries) to identify known vulnerable artifacts.
OWASP Dependency-Check also provides a dependency-check-maven plugin that uses dependency-check-core to detect publicly disclosed vulnerabilities associated with the project's dependencies. The plugin will generate a report listing the dependency, any identified Common Platform Enumeration (CPE) identifiers, and the associated Common Vulnerability and Exposure (CVE) entries.
[ Learn how IT modernization can help alleviate technical debt. ]
The Dependency-Check plugin can be configured inside the plugin section in your project XML file, as shown below :
<project>
…
<build>
<plugin>
<groupId>org.owasp</groupId>
<artifactId>dependency-check-maven</artifactId>
<version>7.1.2</version>
<executions>
<execution>
<goals
<goal>check</goal>
</goals>
</execution>
</executions>
</plugin>
<build>
...
</project>
Control dependencies using Maven's BOM
The Bill of Materials (BOM) is a special POM file that groups dependency versions that are secured and tested to work together. This can minimize the developers' pain of having to test different versions' compatibility and reduce the security risk. The BOM file has a dependencyManagement section that lists the dependencies and their versions to be used in a project.
<project ...>
<modelVersion>2.0</modelVersion>
<groupId>example</groupId>
<artifactId>example-bom</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.example</groupId>
<artifactId>D</artifactId>
<version>2.0</version>
</dependency>
<dependency>
<groupId>io.example</groupId>
<artifactId>E</artifactId>
<version>3.0</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
Vendors need to compile these BOM files very carefully to manage versions of the important dependencies and help the customer application pull only the safer versions of any dependency. It's recommended that users use these BOM files in their projects so that their applications pull the safer and more compatible versions of dependencies.
You can use this BOM file in their projects in two ways :
1. As a parent POM
A BOM file can be used as a parent POM of a new project. This newly created project will inherit the dependencyManagement section from the BOM, and Maven will use it to resolve all the dependencies required for the project.
<project ...>
<modelVersion>3.0</modelVersion>
<parent>
<groupId>example</groupId>
<artifactId>example-bom</artifactId>
<version>1.0</version>
</parent>
<groupId>user-project</groupId>
<artifactId>new-project</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>New Project</name>
<dependency>
….
</dependency>
</project>
2. As a dependency
Developers can add a BOM to a new project's POM file by editing the dependencyManagement section as a dependency with a POM type:
<project ...>
<modelVersion>3.0</modelVersion>
<groupId>user-project</groupId>
<artifactId>new-project</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>New Project</name>
<dependency>
…
</dependency>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>example</groupId>
<artifactId>example-bom</artifactId>
<version>1.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
Wrap up
Dependency management is crucial, especially with non-zip distributions where Maven fetches dependencies based on the user applications. Using the BOM is a good way to support security and consistency between dependency versions.
[ Check out Red Hat's Portfolio Architecture Center for a wide variety of reference architectures you can use. ]
저자 소개
Paramvir is a Middleware Security Architect in Red Hat's Product Security Team.
채널별 검색
오토메이션
기술, 팀, 인프라를 위한 IT 자동화 최신 동향
인공지능
고객이 어디서나 AI 워크로드를 실행할 수 있도록 지원하는 플랫폼 업데이트
오픈 하이브리드 클라우드
하이브리드 클라우드로 더욱 유연한 미래를 구축하는 방법을 알아보세요
보안
환경과 기술 전반에 걸쳐 리스크를 감소하는 방법에 대한 최신 정보
엣지 컴퓨팅
엣지에서의 운영을 단순화하는 플랫폼 업데이트
인프라
세계적으로 인정받은 기업용 Linux 플랫폼에 대한 최신 정보
애플리케이션
복잡한 애플리케이션에 대한 솔루션 더 보기
가상화
온프레미스와 클라우드 환경에서 워크로드를 유연하게 운영하기 위한 엔터프라이즈 가상화의 미래