For more than 15 years I have worked with Java technologies, particularly on the server-side, with Java Enterprise Edition (Java EE). I have written Java applications and deployed them in production across many environments. Along the way I have worked with many talented individuals and teams architecting, developing and maintaining Java EE applications.
As respects server-side security, the number one thing that stands out to me is that a comprehensive security view of a Java system is very hard to come by, let alone understand, even for experienced Java architects.
In this blog I want to explore Java EE security features and articulate how ShiftLeft leverages and improves the Java Security Model by automatically implementing a policy-based security profile for the application code, and by detecting and securing vulnerable trusted code. With ShiftLeft the Java EE security architecture becomes easy to visualize and digest.
Java Security Overview
Java security can be categorized in two broad areas:
- Platform Security — Provided to the application by the Java platform, and
- Application Security — Specific to the application workflows.
Let’s take a quick look at each of these security realms to better understand how Java security has evolved over the years.
Platform security (also known as provided security because it is supplied by the Java platform) comprises a broad range of features implicitly available within every running JVM aimed at reducing application permissions and avoiding remote code execution (RCE) vulnerabilities.
Through the memory management model covering the Java language specification, garbage collection, strong data typing and bytecode verification, Java offers out-of-the-box protection against one of the most exploited security flaws in networked applications: buffer overflows.
Additionally, the Java Standard Edition (JSE) Security Model (also known as the "JSE Access Control Model" or “JSE Permission Model”) and the Java Authentication and Authorization Service (JAAS) extension provide implicit permission sandboxing fully integrated with the Java Standard Edition API. This permission model evolved from the rigid client-side security of applets in the early days of Java to a flexible policy-based framework available (disabled by default) for all JVM instances (Java EE containers included).
The JSE Permission Model lets developers apply fine-grain policy that determines which security-sensitive actions are permitted, depending on the actual code performing the action (where), user identity (who), and action in itself (what). Every piece of Java code trying to perform a security-sensitive operation needs to go through this permission verification framework.
The goal of the JSE Permission Model is to reduce application runtime permissions granting to the bare minimum required. Typical use cases covered by the model include the following:
- For untrusted applications (such as applets or Java Network Launch Protocol (JNLP) applications on the client-side, or shared Java EE containers on the server-side), only allow the application to perform security-sensitive operations it is expected to, or the ones it advertises it does.
- For trusted server-side applications, minimize their potential for action in case they became compromised.
The goal for Java platform security is to run applications with the minimum needed permissions (principle of least privilege) and to protect against buffer overflows.
The second Java EE security category is application security which encompasses all security-related concerns that are explicitly handled by the application according to its unique business requirements (and thus the responsibility of application developers) — not only the application code, but also the dependency graph of libraries used.
The Java EE security model, which I discuss in more detail in the next section, is especially relevant in the context of application security, since it is present in every Java EE container and also integrated with the Java SE Permission Model.
The realm of application security is owned by the developer. Making the application secure is his or her responsibility.
The Java Security Zoo
Thus far it has been relatively straightforward to describe and (hopefully) understand Java security. So, you may be wondering, what is the problem? What is so difficult about Java security?
The answer is that on close examination one starts to realize that the Java security landscape is a morass of features, tools and APIs spanning the Java language, JVM, class loading mechanism, and user interaction resulting in a security architecture of Byzantine complexity—the Java Security Zoo.
How we got here is a combination of factors:
- Unaligned parallel efforts, such as in the case of the Java SE and Java EE security models, that evolved separately, with unification attempted after-the-fact.
- Evolution tainted by an obsession with backwards compatibility when some refactoring and remodeling of the underlying security architecture was needed to maintain a healthy and easy-to-understand model and APIs. For example, 17 years after JAAS was introduced we are still talking about it instead of a unified Java SE security model. Likewise integration patches or incremental specs (such as the Java Authorization Contract for Containers (JACC)) instead of a unified Java SE security model. To understand, learn, and teach Java security we must constantly revisit the entire evolution of the Java security model . Given the rapid rate of change within the software industry, the blunt reality is that breaking compatibility and starting fresh is a must for creating maintainable, long-lived software. Java has failed at this when it comes to its security architecture.
- Perspective fragmentation caused by the standard J2EE platform roles (EE.2.12, Java Platform, Enterprise Edition 8). Security features are explained and documented from the perspective of a certain role or vendor; it is impossible to find comprehensive documentation that glues all Java security features across releases.
I am dismayed that even now, in the age of microservices, the latest Java EE specification (version 8, released in August 2017) continues talking about roles when the reality is that virtually everyone is using application-embedded web runtimes and scaling via containerization (thus owning the whole stack).
- Immature specifications. Java elements are typically only partially included in some specifications, often leaving out important details. This happened, for example, with the inclusion of Java Authentication and Authorization Service (JAAS) 1.0 into J2EE 1.3, without covering any integration detail. Despite probably having good reasons for doing so — they envisioned the path to take and let different platform providers struggle with the details — the myriad approaches contributed greatly to the zoo and contributed turning the flagship motto “Write once run anywhere” into a bad joke.
- Partial adherence to specifications of some providers, for example Tomcat not supporting Java Authorization Contract for Containers (JACC).
The list is not exhaustive because to do so would require too much text, time and patience. The adage a picture is worth a thousand words is apropos:
The Real World
Web application attacks are the leading cause of data breaches. Server-side Java security concerns primarily deal with potentially malicious data payloads, as opposed to client-side Java security concerns (applets or Java Network Launch Protocol (JNLP) applications) which typically attempt to address malicious code.
It take two steps to perform a security breach: finding a vulnerable execution flow in the application, and exploiting that vulnerability to make the application execute in a way it was not designed to do, such as arbitrary code execution within the running context of the application. (For an excellent analysis of such a breach, see my colleague Niko Schmidt’s exploration of the RCE vulnerability that led to the Equifax breach.)
Let's see how both Java security realms — application and platform security — perform in the real world.
Application Security Belongs to Humans
Application security is the responsibility of developers who can and do make mistakes, or worse simply ignore or are ignorant to security considerations. It is likely that a Java EE application contains one or more security flaws, either directly in its code or inherited from libraries in use, often both.
Every Java EE application should be considered vulnerable.
Application security testing is far from reliable. Passing a suite of security tests is not an indication that no flaws exist in the application or that the system meets all security requirements. Vulnerabilities eventually arise, and security robustness ends up being improved in a reactive way.
Security testing is unreliable; security is “improved” in a reactive way.
Platform Security Is Not Used
Over the years Java’s sandboxing model has received criticism for making users vulnerable to malware that could bypass its restriction mechanisms, usually in the context of client-side applications (attacks external to the JVM).
In the case of the server-side applications, platform security is more efficient since it has to be broken by code already running, thus the vulnerability window is smaller.
However, the Java Security Model, besides being hard to master, has several implementation drawbacks, including:
- Policy has to be maintained in parallel with the application code.
- Policy configuration is cumbersome and error prone.
- If the application is not properly designed, the use of a SecurityManager may degrade application performance beyond an acceptable rate.
- Wrongly documented as “not necessary” if the application code is trusted.
The result is that the the SecurityManager is disabled for the most of the running JVM instances (leading application servers disable it by default, including Glassfish, Tomcat, Weblogic, and Websphere). This means that the vast majority of Java EE applications in production are running with full permissions, often with the people responsible unaware of its implications.
Even in the case of awareness, the scenario can be hard to read. For example, using JAAS might lead one to believe the Java Security Model is fully applied, but if the SecurityManager is not enabled permissions to core system resources are not checked. There are even cases where JAAS is only used for authenticating the user in a pluggable way, without any kind of integration with the underlying Java security permission model (for example, containers not supporting JACC such as Tomcat).
The most severe consequence of this occurs when the application is compromised by a RCE exploit, allowing attackers to gain full control of the JVM, and even of the system (if the JVM process has enough permissions in the OS, another common bad practice).
If the Java Security Model is enabled and properly configured, many known RCE attacks would not succeed, despite the application being vulnerable. For example, a properly configured SecurityManager likely could have spared Equifax from losing 150 million PII records.
ShiftLeft Approach to Java Security
At ShiftLeft we have created a novel and innovative platform that performs continuous analysis of application bytecode and its dependencies. In addition, ShiftLeft monitors and protects the application at runtime depending on actual workloads.
ShiftLeft improves Java application security in multiple ways:
- ShiftLeft detects weaknesses in trusted code, such as sensitive data leaking, so that coding mistakes can be identified early in the application lifecycle, before it goes into production.
- Based on code analysis, ShiftLeft provides runtime protection in a highly- performant fashion, offering live monitoring and alerting against common attack vectors over actual application workloads, such as SQL injection or brute force authentication, and detection of execution of unknown flows.
- ShiftLeft fully leverages the Java Security Model. From the application source code we can automatically generate a very restrictive Java security policy so the target application can run with a SecurityManager enabled and the bare minimum permissions required to fulfill its goal
I’ll leave you with this, a diagram of the security posture of a Java EE application filtered, organized, and presented through ShiftLeft. Thankfully it’s a far cry from the Java security zoo we have been forced to deal with for far too long.
This is a Security Bloggers Network syndicated blog post authored by Ignacio del Valle Alles. Read the original post at: ShiftLeft Blog - Medium