The Java ecosystem looks like a fortress, and security is one of its claims to fame. Between Java’s spec+ifications, robust frameworks like Spring, and an endless sea of third-party libraries, you are building on top a Mountain of Security.
Yet, data breaches are not an anomaly but a regular occurrence. From small startups to global conglomerates, breaches remain a persistent, systemic, and increasing threat across the industry. If the technology stack is that good, why are the results that bad?
The answer is not a single bug or vulnerability, but a methodological gap. We are building on a mountain of security, but we forget that the building blocks we are using are like Swiss Cheese. Each component and layer is full of holes. Unless you intentionally stack these layers so that they cover each other’s gaps, a breach is not just possible, it is inevitable.
The Mountain: JVM, Frameworks, and Libraries
When talking about Java Security, many think of, for example:
- Bytecode protection to statically validate the binary, types, and to avoid stack overflows, with runtime protection to dynamically cover what can only be detected during execution (like bounds checking).
- Memory management that makes invalid memory access impossible, and class loaders to isolate system code from application code.
- Built-in capabilities like JAAS (Java Authentication and Authorization Service), JCA (Java Cryptography Architecture), JSSE (Java Secure Socket Extension), and more.
All these are great, built into the language and the JVM. You undoubtedly also use a framework like Spring and 3rd party libraries. Those leverage the built-in Java capabilities and add many capabilities on top.
Since you are already using so many security components, how come this mountain of security is not secure? Will the next Java enhancements, Spring feature, or 3rd party library you add make the difference, or is there something more structurally wrong?
Overcoming the Holes in the Cheese
When you look at a block of Swiss cheese, you cannot see all the way through. You can only see through the holes if looking at a single slice. The question is how to pile the slices of this “security cheese” so the holes are not visible side-to-side.
Securing a Java application spans three distinct phases:
- Development & Testing – coding & building the application
- Deployment & Maintenance – configuring and running the application
- Operating & Monitoring – ensuring the application is running securely
Each aspect has strengths, weaknesses, and inevitable holes. If you do not pay sufficient attention to each of these cheese layers, you will likely end up with holes from side to side, culminating in a data breach.
The trick is to understand the different emphasis in each layer and take advantage of it.
Development & Testing aims to build a solid application. Developers emphasize achieving full functionality while reducing bugs. The core focus is on delivering a functional and working app. Security is important, but not the primary mission or a critical team measurement.
Deployment & Maintenance aim to harden the application and deliver a solid and stable production environment. The objective is to produce a reliable and secure system. While you can send change requests to development, the focus is on system administration.
Operating & Monitoring is about vigilance. To ensure the application runs well, and nothing inappropriate happens inside it. From uptime to performance and security, the objective is to ensure proper continuous services.
Each of these layers requires both automation to reduce failures and the human touch to apply common sense and identify when things should be different. Unfortunately, you are missing at least one of these in each layer.
However, even with the best coding standards and careful deployment and maintenance, vulnerabilities exist. Consider the bugs you will discover next week or the week after that. They are not necessarily groundbreaking zero-day attacks, but simple SQL injection vulnerabilities you’re not currently aware of. The only way to close these gaps is through regular monitoring of application activity. Such monitoring will uncover both external attacks and internal abuse.
The bottom line is that without addressing all 3 aspects, you will likely get breached and never realize it.
Development & Testing
Data breaches are, ultimately, about bugs and vulnerabilities. That means problems in the architecture, design, or implementation. Developing high-quality code is imperative to security.
Here are some standard practices organizations follow:
- Use a proper framework like Spring and reliable 3rd party libraries.
- Employ methodologies such as agile, scrum, code reviews, and more.
- Fully automated builds and regression tests.
- Leverage tools for Static Analysis (SAST) and Dynamic Analysis (DAST)
- Follow coding standards and best practices.
These are good and important steps to improve code quality and security. However, it’s not enough because even companies that follow all these and more are breached.
What may be missing is not on the automation and practices side but on the human side. It’s the common sense or intuition of a senior developer or seasoned QA engineer who can recognize potential security flaws. To achieve that, you need to invest in better security education and narrow the required field of expertise.
The Education Gap
There is no special secret everyone needs to know or a single paper or class that brings people up to speed. It’s a continuous process that gradually improves skill levels. Here are some small examples to better explain the challenge:
Storing user passwords in clear text is universally recognized as unacceptable. However, storing the password for the database service account is a challenge because the application requires access to it. It cannot be hashed, and any encryption key will be accessible by the code. That is a tricky question that requires proper attention, and a security-educated developer would realize that.
Another pet peeve – why is SQL injection still a concern in 2026? It is mostly because developers are unaware of how it occurs and what to avoid. It is also because they mistake framework security for absolute security. They use Hibernate or Spring Data JPA and think it protects them, but the moment they use string concatenation to build a dynamic JPQL string or a native query, they are exposed. Embedding literals in SQL is always wrong. Bind variables must be used universally as they separate the literals (data) from the SQL (code) and eliminate the risk of SQL injection.
Many developers emphasize input sanitization as a catch-all solution to address countless security weaknesses, including SQL injection. While input validation and sanitization are good and legitimate practices, they are also a symptom of a deeper problem. Developers make assumptions about the input and fail to code what happens when it doesn’t. Input with unexpected HTML tags causes XSS. APIs that contain file names allow accessing unexpected files, and parameters that appear multiple times can be used in HPP attacks. Even an unhandled exception when converting a string into a number can have unexpected consequences. That is also part of the larger issue of error handling that, when not coded and tested properly, exposes you to an attack.
A related subject is the practices of throwing exceptions and printing their long stack traces. Similarly, developers often display detailed database error messages. These seem like a good idea as they help debug problems. However, that’s also how hackers figure out what the bug is and how to exploit it. Detailed debugging information belongs in log files, not to the user’s monitor.
As mentioned earlier, these are examples. The underlying problem in those and countless others is the lack of security education and awareness in development teams. Quality code comes from educated, skilled engineers. Getting the application to work is the floor, while human intuition and security literacy are the ceiling. There’s a massive gap between this floor and the ceiling, and between them the hackers hunt.
The Cognitive Toll
Part of the developer educational challenge is the scope. The mountain of frameworks, libraries, and tools used to build the application. On the one hand, this mountain increases the risk of supply chain vulnerabilities. However, just as important is the toll it takes on education and skill.
When developers are intimately familiar with the libraries and tools they use, they are far less likely to have bugs. There’s a big difference between using an API you learned 5 minutes ago and using one you’ve been working with for years.
The more tools, libraries, and frameworks a project relies on, the less familiar developers are with each one. Superficial familiarity inevitably causes bugs and security flaws. By reducing the mountain, you allow developers to gain a deeper understanding of the components they use, how they function, and how to wield them.
The Testing Gap
Most standard tests aim to validate that the application works. It’s true for both development and QA testing. Security testing, on the other hand, must ensure the application cannot be broken. That requires a “Hacker Mindset” that most QA and development teams lack.
Security testing means trying various types of invalid input, applying unreasonable stress and operating conditions, and testing every URL parameter. It also means inspecting internal access points invisible to the regular end-user, sending parameters via GET or POST that the application isn’t expecting, etc. Just as QA aims to imitate a regular user, security tests must aim to imitate a hacker.
Locating the “hidden” pages and API calls may be the most challenging. The difficulty is that no one knows about them, so no one maintains or tests them. For example, consider a BOLA (Broken Object Level Authorization) on a page or an API endpoint that was not tested because it is used only through AJAX or never accessed at all.
Ultimately, the point is that testing teams need to have better security education because if they don’t know what security flaws look like, they will never be able to find them.
Deployment & Maintenance
Deployment and maintenance are treated as an afterthought. They are not built into the product requirements but handled as a follow-up to the manufactured product. In the old days, someone needed to figure out how to get an app that worked in development to work in production.
Nowadays, it’s the CI/CD pipeline that converts those improvised steps into an automated process.
The CI/CD pipeline may include, for example:
- An automated build of the code from version control
- An automated regression test
- Automatically scan the SBOM (Software Bill of Materials) to identify potential supply chain vulnerabilities.
- An automated deployment of VM images and of the software inside them.
The old way depended on the few people who knew how to deploy, and contained the risk that they would make a mistake or miss something. The new way has mostly replaced humans with automation. However, these processes ensure a mistake is never re-evaluated and is forever baked into the pipeline.
The Mindset Conflict
Another flaw in the CI/CD pipeline relates to who builds and maintains it. For example, if it’s a developer, a developer’s job is to make the app run. When developers are responsible, they may relax a policy, open a port, grant a permission, or use an unnecessary option to get everything working.
Professional deployment requires the opposite mindset: Hardening. Yes, you need to make sure everything works, but it has to work in the tightest and most restrictive environment.
- Minimalism: If a library isn’t needed for production, it shouldn’t be installed.
- Least Privileged: Does the JVM really need to run as root? Does it need write access to all these directories?
- Tight Configuration: A config file that works in Dev is a liability in Prod. You need to tighten security, disable debugging, and lock everything down.
- Secure Environment: You must enable the firewall, close all unnecessary ports, enable SELinux, disable accounts, manage and secure the logs, and more.
A classic example was deploying Java applications with a full JDK. The Java Development Kit has a massive attack surface of compilers and tools that aren’t required in production. Today, this can be addressed by JLink, GraalVM, etc., but you still need personnel who can do it.
The processes of deployment and maintenance are fundamentally opposite to those of development. It is the realm of security-conscious system administrators who aim to ensure reliable, continuous, and secure operations. Using other personnel to manage the CI/CD pipeline is a recipe for security problems.
Maintenance
Maintenance is where vigilance goes to die. Every 3rd-party library is a potential backdoor. We’ve seen that with Log4j and others. An automated SBOM (Software Bill of Materials) scan is a start, but it’s not a solution. Maintenance requires a human who “agonizes” over every change and ensures a patch or a change doesn’t open a new hole. A misconfigured IAM role or Kubernetes network policy is all it takes.
Maintenance and deployment mistakes are not only related to Java, but always require tightening. It is how AWS went down for 15 hours, how River City Media exposed 1.37 billion user records, how Microsoft exposed 250 million customer support records, and how Capital One’s Amazon S3 storage buckets were stolen.
Just like development, deployment lacks experienced security-aware individuals. Automation is a tool, not a solution. You need people who are part of the deployment process, aware of its intricacies, knowledgeable about changes incorporated into the software, and constantly validating that production is reliable and secure.
Complexity
Modern Java deployments are incredibly complex, comprising an astonishing number of components both in the application and around it.
The number of tools, frameworks, and libraries used from development to production is unbelievable, and a mistake in any of those can mean a vulnerability. Not just the bugs in those systems, but also configuration mistakes, incompatibilities, or a simple overlooked detail.
Gone are the days when DevOps engineers knew the details of every possible parameter in the handful of systems they used. Nowadays, it is already an achievement when they are aware of all the systems in use and their general function.
One approach is to simplify when possible. Viewing complexity as a risk factor and driving towards fewer tools, fewer frameworks, fewer integrations, and better isolation. Another approach is to invest in education, training, and mentoring to ensure relevant personnel are fully aware of every component and nuance. Ideally, you adopt both approaches and simplify while improving skills.
Operating & Monitoring
Ensuring the system operates well and securely is the most critical layer, and the security portion is almost universally neglected. This layer aims to compensate for all the bugs, vulnerabilities, configuration mistakes, and other problems that infiltrated production. It should be the most robust and heavily invested, but instead, it is almost entirely ignored.
The “Automated Gun” Problem
The main security measure deployed in production today is a WAF. It is a useful protection but far from sufficient. Its biggest limitation is conceptual. Relying solely on automated blocking is like having automated anti-aircraft guns shooting at the sky.
- They mostly fire at birds or nothing at all (False Positives).
- They miss stealth bombers (Sophisticated attacks).
- The “blueprints” for these guns (e.g., WAF signatures) are known to the enemy.
- They sometimes hit an enemy plane, but the enemy has unlimited attempts and only needs one successful penetration.
This is a game you cannot win. The odds are stacked so high against the defenders that the attackers will always win. Sadly, this aligns perfectly with reality.
To make things worse, you won’t realize you lost until months or years later. If an attacker bypasses these automated defenses, you are blind. In most organizations, breaches go undetected for months because no one knows.
The last line of defense was meant to be the most formidable and compensate for everything else. It ends up underwhelming and the weakest link in the chain.
The Three Pillars of Runtime Security
The core principle in security is to block what you can and detect the rest. You only need to detect an exploited vulnerability in time.
You must also remember that there’s the danger that lies within. Internal threats account for about 20% of data breaches. You must, therefore, identify attacks from both external and internal sources. Attacks against unknown vulnerabilities, simple exploits that leverage credential theft, or an abuse of privilege.
A proper defense is made of three core tenets:
I. Detective Controls
Detective controls are at the core of any security. A bank with no security camera or patrolling guards is merely a large vault door. With sufficient time, attempts, and skill, any vault can be broken into, especially one with countless vulnerabilities.
Effective detective controls require:
- Timely Alerts: Getting near-realtime information so we can react to attacks in time.
- Regular Reports: Gaining visibility into security-sensitive activities.
- Anomaly Analysis: Flag individual suspicious activities out of billions.
- Reactive Forensics: The capacity to investigate past events and understand what happened.
These standard measures allow for the full cycle of detective security, from detection to human investigation.
II. The Human Element (Proactive Forensics)
The detection measures above are great since they are calibrated more sensitively than preventive ones, but they are heavily reliant on automation. Automation is the natural reaction to the massive volume of activity, and it can filter through billions of events. However, it cannot apply common sense.
You need security personnel to engage with the data. Proactive forensics isn’t about waiting for an alarm to sound. It is about looking at the activity profile of your application and asking why this is happening. It is a powerful tool for control design, planning the alerts, reports, and anomalies you require. It is also effective in identifying gaps as the activity profile changes over time.
Proactive forensics helps you stay on top of what’s happening without relying solely on automated controls you set up years ago. It allows your security guards to roam the halls of your application.
III. Declarative Runtime Prevention
Built-in application security is rigid. If you discover an attack or a vulnerability today, it may take weeks to fix the code, test it, and deploy it. You need a Declarative security layer. Not a high-overhead RASP with automated protection, but simple declarative prevention to close vulnerabilities in real time.
Effective runtime security requires blocking the same activity you monitor. That way, your team can address the attacks they detect. That means blocking URLs and IPs visible at the network layer, evaluating the user in the app, or stopping deeper activity such as SQL queries sent to the database. These policies must deploy instantly across multiple application servers, without code changes or service restarts. When an attack is detected, mitigation should take minutes, not days.
This is not an automated gun but a sniper rifle, aiming at a target wielded by a security engineer. Whether as a permanent fix or a temporary one, the ability to take action empowers security personnel to react without taking the entire application offline.
Final Thoughts
Securing a Java application isn’t about finding the “perfect tool”. It’s about accepting that everything is flawed and you must compensate for those flaws. Your code has bugs, your deployment has misconfigurations, and your automated defenses can be bypassed. Yet, you must avoid a breach.
The only way to win is to stack these slices of “Swiss Cheese” so that the holes don’t align. So that a bug doesn’t trigger a data breach. You need to educate your developers on security and how breaches occur. You should empower your deployment teams to harden the environment. And most importantly, turn on the lights in production and gain visibility and control over your activity.
That is how you secure your application, not just pray and hope for the best. Don’t keep doing more of what’s already failing. It may not be insanity, but it is definitely not a good strategy.





