Sitemap

Mitigating Software Supply Chain Vulnerabilities with Zero-Trust Dependencies

9 min readMay 7, 2025

--

This is a brief for the research paper “ZTDJava: Mitigating Software Supply Chain Vulnerabilities with Zero-Trust Dependencies”, published at the IEEE/ACM 2025 conference on Software Engineering (ICSE). This work was led by Paschal Amusuo, one of my research assistants. The full paper is available here (open-access preprint here). Paschal wrote this brief, which I have lightly edited. This paper is part of my lab’s body of work on the software supply chain — I put some links at the bottom if you want to read more.

Summary

This paper introduces Zero-Trust Dependencies (ZTD), a novel concept where dependencies in an application are untrusted, and require explicit policy authorization to access system resources. ZTD is distinguished from prior sandboxing work by its support for dependency management and its data-driven simple policies. We measure its feasibility, provide a system design and a prototype to enable adoption, and evaluate its effectiveness and cost in preventing software supply chain vulnerabilities.

Background

The Log4J Incident Taught Us Not To Trust Our Dependencies

Remember the Log4J vulnerability of 2021? A vulnerability in a simple logging library caused a global crisis. Attackers could exploit the vulnerability and execute malicious code within your application.

But first, what happened then? The Log4J library had a feature, Java Naming and Directory Interface (JNDI), that allowed it to find and fetch data from external servers using just a naming identifier [1]. If the fetched data is a serialized Java object, arbitrary code execution can occur when the object is deserialized. Hence, Ii an attacker could control what Log4J logged (e.g. if an application uses Log4J to log fields from user forms or HTTP headers), the attacker could direct Log4J to fetch and execute malicious code on the server the application runs on.

In the wake of this incident, organizations were forced to scramble for fixes, work nights and weekends or even shut down their applications to prevent being exploited, until the vulnerability was patched. Online sources even estimate that the average cost of the Log4J incident response was over $90,000 [2].

This incident highlighted a critical flaw in how we think about dependencies. Essentially, the question arose: Can we truly trust every library we use? The answer, unfortunately, turned out to be no.

Understanding the Software Supply Chain Risk

Modern software development relies heavily on third-party libraries, our “dependencies.” The use of dependencies make software development faster and easier, but they also introduce risk. We often treat them as black boxes, using their functionality but rarely understanding their internal workings or monitoring how they change over time. These dependencies are a part of our software supply chain, and any vulnerability within any dependency can become a vulnerability in our entire application if user-controlled input from the application reaches the vulnerable code.

A vulnerability is any bug or feature of an application that an attacker can exploit. We refer to vulnerabilities that occur in libraries and other dependencies as Software Supply Chain (SSC) Vulnerabilities. These vulnerabilities are especially dangerous because dependencies are typically trusted by the application that uses them. They execute with the full permissions granted to the application.

The following figure shows the structure of modern software applications. All applications (big yellow box) interact with an operating system to provide access to sensitive resources such as the file system, the network, and the ability to execute code. Within the application, there is application logic (“business logic”) and third-party code. This figure is not actually to scale — many reports, e.g. from Sonatype, state that third-party code dominates, comprising 80%-90% of many software applications!

There are two crucial elements of this figure from our perspective. First, those third-party dependencies runs within the box labeled “Application” — from the perspective of the operating system, code within the application all has the same privileges. Second, these dependencies may have vulnerabilities, introduced either maliciously (red box) or accidentally (beige box, like Log4J was).

Model of modern software. Applications are comprised of in-house and third-party code. To run securely, engineers should not trust third-party code. At present their options for doing so are lackluster.

Proposal: Zero-Trust Dependencies (ZTD)

The ZTD concept

What if, instead of trusting dependencies by default, we consider all dependencies as untrusted and only permit resource access if a dependency has explicit authorization?

We refer to this paradigm as Zero-Trust Dependencies (ZTD). Our ZTD concept is based on the Zero-Trust Architecture (ZTA) for computer networks, which the United States NIST recommends and which has been adopted by major technology organizations like Google [3] and Microsoft [4].

The Zero-Trust Architecture requires secure access to the resources in a system, and that users or devices in that system are only granted the least privileges they need. We took this mindset and applied it to the application runtime. In this context, the ZTD paradigm requires:

  1. Secure and Context-based Resource Access: Every access to a resource should be authorized in consideration of its context.
  2. Least-Privilege Policy Enforcement: Access policies should grant minimum access rights to subjects (software dependencies).
  3. Continuous Monitoring: Organizations should monitor the state and activities of the subjects (software dependencies) and use the insights gained to improve the creation and enforcement of policies.

In other words, dependencies should be considered untrusted by default, dependencies that need resource access should have explicit authorization, and dependencies should only have the least set of privileges that they need.

In any security system, there is always a fourth requirement: that the costs of security not outweigh the benefits. In this case, the costs of enforcing ZTD at runtime must not make the resulting computing system unusable.

The Zero-Trust Dependencies (ZTD) concept. To mitigate attacks exploiting vulnerable dependencies, a ZTD system provides secure access via runtime authorization, makes authorization decisions using a least-privileges access policy, and facilitates continuous monitoring of unexpected accesses.

Isn’t this yet another take on sandboxing?

Secure access control and least privilege enforcement are not new topics in software engineering. There are many technologies, like Docker containers [5] and application security managers [6], that can provide secure resource access control and allow the enforcement of least privileges on an application. However, our analysis suggests that prior approaches fail to satisfy all of these requirements.

ZTD is not the first proposal to address dependency vulnerabilities. Let me share a bit more detail on how ZTD differs from prior works.

  • Solutions like MIR [7] control which functions a dependency can call, while others like BreakApp [8] isolate dependencies in containers. However, these solutions either do not address dependency vulnerabilities, or their protection comes at substantial performance cost. The first targets only maliciously injected code, and the second incurs heavy performance overhead, making it impractical for isolating all dependencies.
  • Application-level sandboxing solutions like the Java Security Manager (JSM) [6] can conceptually address dependency vulnerabilities as they allow application engineers grant access permissions to individual classes in the application. However, application engineers do not know the class names in their dependencies and it will require substantial effort to specify class-level policies. Hence, these factors made the JSM hard to configure, increased its performance overhead, and subsequently led to its deprecation.

In contrast, ZTD tackles accidental vulnerabilities like Log4J with minimal overhead by operating at the resource-access level, inspecting what resources a dependency tries to access during a dangerous operation, and only allowing the operation based on the dependency’s permission. This allows for more fine-grained and efficient control.

The following table is taken from the paper (so the references are to the paper’s bibliography), but we hope you get the idea — prior approaches achieve some of these requirements, but not all at once.

Analysis of existing security defenses by ZTA principles. Columns indicate if each technique family provides secure resource access, supports least privilege discovery and enforcement, enables cotinuous monitoring for dependencies, and has low runtime costs

ZTDsys: A ZTD System Design

ZTDsys comprises two major components: Automated policy generation and Context-sensitive policy enforcement.

Automated Policy Generation

One of the key challenges is knowing what permissions each dependency needs. Software engineers often don’t know the internal workings of every library. Our ZTD system addresses this with automated policy generation.

The system observes the resources accessed by each dependency during normal application execution and creates policy files based on this observed behavior. This removes the guesswork and provides an initial set of permissions. These policies are then accessible to engineers to review and adjust. Application engineers can run ZTD policy generation during the testing phase if their test suites are comprehensive, or for a short period of time in production.

Context-sensitive Policy Enforcement

For policy enforcement, ZTD intercepts resource access calls made by the application, and checks if the dependencies involved in the call have the necessary permission. We highlight two aspects of this process.

  1. Context-sensitivity: In an application, one dependency can also depend on another dependency. To prevent an unauthorized dependency from leveraging another dependency’s permission, ZTDsys requires all dependencies involved in a resource access operation (based on the classes or files present in the call stack) to have the necessary permission.
  2. Efficiency: An application may have hundreds of dependencies. In some languages like Java where only class names, not dependency names, are present in the application, it is infeasible to use a map to store or lookup a dependency’s permissions. To ensure dependencies can still be retrieved in constant time, ZTD uses a radix tree [9] to store dependency policies, where components of the class name or file path form the nodes in the patricia tree.

ZTDJava: A Java Implementation

We prototyped the ZTD concept for Java applications. We call the prototype, quite creatively I know, “ZTDJava”. As the following figure illustrations, ZTDJava modifies core Java library classes that are used to access operating system resources (e.g. FileInputStream for file read, ProcessBuilder for shell execution). Whenever a modified method is called, ZTDJava’s runtime monitor checks the policy. If unauthorized access is attempted, ZTDJava can either proactively block the access (fatal enforcement) or log the event for a prompt but reactive incidence management (non-fatal enforcement). This provides flexibility to balance security with application reliability. In fact, if ZTDJava were available before the Log4J incident, organizations could just have removed network access permissions from their Log4J dependency and they would have been protected from the attacks. The vast majority of Log4J users did not need the network functionality, but were shipping with it enabled anyway.

The design of the ZTDJava prototype showing its five components. ZTDJava allows application engineers to generate and enforce least privilege policies in their dependencies.

Evaluation: Effectiveness and Cost

For effectiveness, we tested ZTDJava against real-world exploits of known vulnerabilities. It successfully blocked all exploits in both sample and real applications. This demonstrates ZTD’s ability to mitigate a range of attacks.

Effectiveness of ZTDJava in preventing exploits of SSC vulnerabilities. It blocked exploits of 15 vulnerabilities in sample applications, and 9 vulnerabilities injected in real applications.

For cost, performance overhead and configuration effort are the key concerns. The table below summarizes both aspects, using the recent DaCapoBench benchmark which comprises realistic Java applications for a range of operating contexts (e.g., databases and web servers).

  • We compared ZTDJava’s performance with the Java Security Manager (JSM). ZTDJava had negligible overhead (~0%), compared to JSM’s 21% median overhead. This efficiency is due to ZTD’s focused approach (protecting only a few critical resources) and our efficient policy enforcement.
  • We also compared the configuration cost of ZTDJava and the JSM. In the final column of the table, the notation “x/y” means x dependencies needed ZTDJava policies, and each policy provides an average of y permissions. These policies were generated automatically by observing runs of the application. The configuration costs were much lower than to obtain equivalent policies under JSM.
Comparing ZTDJava and JSM’s performance overhead. ZTD introduces a median 0% overhead while JSM introduced 21%.

Conclusion

ZTD is about taking a proactive approach to security. Instead of blindly trusting our dependencies, we can control their access to sensitive resources and mitigate potential security threats. This is a shift in mindset, but one that can significantly improve the security of our applications. ZTDJava demonstrates the practicality and effectiveness of this approach, offering a powerful tool for modern software development.

Learn more

To learn more about ZTD, our methods, system design and results, check out our paper here: https://arxiv.org/abs/2310.14117

You can also build and use ZTDJava in your Java application. Access the source code here: https://doi.org/10.5281/zenodo.14436182

Our lab has been working on other approaches to improving software supply chain security. Some of our other work is about:

  1. Quantitative and qualitative measurements of software signing in practice
  2. Thinking about cybersecurity guidelines
  3. Defining the properties of a secure software supply chain
  4. Thinking about secure pre-trained model supply chains (1, 2, 3, 4)

References

[1] https://tblocks.com/articles/how-to-prevent-a-log4j-jndi-attack/

[2] https://www.scworld.com/feature/digging-into-the-numbers-one-year-after-log4shell

[3] https://cloud.google.com/learn/what-is-zero-trust

[4] https://www.microsoft.com/en-us/security/business/zero-trust

[5] https://www.docker.com/resources/what-container/

[6] https://docs.oracle.com/javase/tutorial/essential/environment/security.html

[7] Vasilakis, N., Staicu, C. A., Ntousakis, G., Kallas, K., Karel, B., DeHon, A., & Pradel, M. (2021, November). Preventing dynamic library compromise on node. js via rwx-based privilege reduction. In Proceedings of the 2021 ACM SIGSAC Conference on Computer and Communications Security (pp. 1821–1838).

[8] Vasilakis, N., Karel, B., Roessler, N., Dautenhahn, N., DeHon, A., & Smith, J. M. (2018, February). BreakApp: Automated, Flexible Application Compartmentalization. In Network and Distributed System Security (NDSS)

[9] https://en.wikipedia.org/wiki/Radix_tree

--

--

James Davis
James Davis

Written by James Davis

I am a professor in ECE@Purdue. My research assistants and I blog here about research findings and engineering tips.

No responses yet