When it came along in the mid-1990s, Java promised a revolution in programming languages. At the time, a great deal of business programming took place in C or C++.
Anyone who’s written those languages professionally knows that they can be full of non-obvious pitfalls. Java was a revelation because it removed many of those pitfalls. Instead of worrying about manually allocating and freeing memory, Java did all of that for you. This removed a whole class of vulnerabilities, and Java gained a reputation as a more secure programming language.
That reputation as a more secure language doesn’t mean that all code you write in Java is automatically secure. As a developer, it’s still your job to make sure that you’re shipping secure code. The good news is that by remaining vigilant, you’ll ensure that the state of your Java security is top-of-the-line. The goal of this article is to lay out five best practices that’ll aid you in making sure your Java code is secure.
#1: Audit your external libraries
This is by far the most important security advice for any programmer, using any language. For the vast majority of projects, external libraries comprise the majority of code shipped. This code runs just like any code your team writes, and it can be compromised just like any code your team writes.
Unfortunately, many organizations can’t even provide a list of third-party libraries their software uses. They literally don’t know what code is running on their servers or what they’re packaging for their customers. That doesn’t mean the code in those libraries is malicious, of course. Almost all library software is written with the intention of being helpful. However, any library can still contain subtle vulnerabilities that create risks inside your company’s software. Allocating developer time to review the code of an external library can save your team from shipping a vulnerability that degrades the quality of your software.
Security isn’t the only reason to audit external libraries. It’s entirely possible that while investigating the quality of the code, your team will discover other performance problems or bugs. If the libraries you’re using are open source, discovering a bug like this is a great opportunity for your team to practice contributing to the broader open source community.
It’s also possible that you’re using some libraries that aren’t open source. In those instances, it’ll be more difficult for your team to get a glimpse of the source code. When that happens, it’s common for the vendors of that software to provide the results of independent audits they’ve commissioned on their own code. Ask to see the results of these audits.
#2: Use mature encryption libraries
Java’s encryption libraries can be a bit prickly. The APIs aren’t particularly easy to use, and the error messages they return are often opaque. Many developers, when confronted with this reality, think to themselves that they should write their own encryption. They recognize that encrypting sensitive data is important, but they fall victim to a classic blunder: they think that because they can’t crack the encryption they’ve devised, that encryption cannot be cracked.
This line of thinking has led to the compromise of many sensitive pieces of data. The truth is that it’s not hard to create encryption so clever that you yourself cannot crack it. But unless you’re the most clever person in the world, there’s always someone who’s more clever.
Instead of writing your own encryption, spend time understanding Java’s built-in encryption libraries. Use them as they’re supposed to be used. Those encryption algorithms are proven secure by extensive mathematical calculations, not by the feelings of the people who designed them.
#3: Manage application secrets
Managing application secrets is challenging for every developer. As a developer, your goal is to strike a balance between usability and security. You recognize that hard-coding your database password is no safer than writing it on a Post-it and leaving that lying on your desk. On the other side, you don’t want to have to help every new developer on your team while it takes them four hours to get your application running.
The answer is to find a trusted secrets manager and use that in your application. That secret manager will provide libraries your team can plug into your code easily. It’ll also provide safe, encrypted storage for your application’s keys. What’s more, a high-quality key management service like AWS KMS will ensure that the code secret it’s storing doesn’t live in memory any longer than it has to. The result is a system that’s easy to both use and learn, and which provides significantly better security than a common option like using environment variables.
#4: Validate your inputs
Software would be much simpler if it weren’t for those pesky users. The most challenging part of securing software is making sure that the people who use it aren’t accidentally making the software less secure. Much like with application secrets, it’s critical to strike a balance between security and usability. Software with too many onerous layers of security checks is frustrating to use, while software with no security checks or layers might regularly divulge sensitive information to unauthorized users.
Validating user input is something that a lot of developers ignore because they think it’ll be tedious. The truth is that, yes, it can be tedious to validate user inputs. But that validation is doubly worth it. It’s the rare action that makes the software more secure and easier to use at the same time. Good validation stops a user from entering input that might break the application. It also provides nice, easy-to-read error messages that explain to the user what’s wrong with the value they just entered. Good validation also stops one vector that malicious users actively utilize to break your software.
Validating user input in Java can be as simple as using the built-in Scanner library, all the way up to writing extensive, complicated custom validation logic. The key thing to remember is that every bit of validation helps.
Validate serialized values, too
Java provides an option for serializing object trees and sending them to some Java application on another server. This serialization process can make communicating between servers much easier than it might be otherwise. It can also be a source of significant security vulnerabilities. Opening your application to serialization means regularly running untrusted code on your server. Before unserializing any input to your application, your application should validate that input to ensure that it’s not malicious.
Alternatively, avoid using serialization at all, as Oracle is phasing it out.
#5: Avoid reinventing every wheel
This is the opposite of the point from #1. Many teams, in the interest of security, avoid using third-party libraries at all. They rewrite common logic internally, trusting that the relative obscurity of their code means malicious users won’t find any vulnerabilities there. This is a bad security posture. Relying on “security through obscurity” doesn’t work when you’re actually shipping code. Eventually, someone else will get their hands on your code (if nobody uses your code, that’s bad news for your business!) and at that point, your code is no longer obscure.
Instead, use common libraries that your team audits, like in tip #1. This has the added bonus of almost always being both faster and much cheaper for your business. While it’s true that even very well-audited code libraries can still contain critical vulnerabilities, those libraries will be fixed much quicker than if your team were taking care of it by themselves. Instead of betting on your own company’s failure, bet on the skills of open source developers, including your own company’s, to keep your application secure.
There is no such thing as perfect security
Even if you follow every rule in this guide, it’s likely that you’ll still wind up writing some vulnerable code sometimes. That’s OK. You don’t want to make a habit of it, and you should remain vigilant to avoid doing so when you can, but you shouldn’t expect that every bit of code you ship will be secure forever. That’s simply not the reality of the world.
Instead, the key is to regularly examine your team’s practices to ensure that they’re in line with best practices. You should work with developers to help them understand why certain security procedures are necessary. You should also invest in Application Security Management solutions to let your team know when something goes wrong and to block attacks in real-time.
Getting Java security right, like all security, is something you have to do every single day. But it’s not magic, and it’s not impossible. You just have to put in the work.
This post was written by Eric Boersma. Eric is a software developer and development manager who’s done everything from IT security in pharmaceuticals to writing intelligence software for the US government to building international development teams for non-profits. He loves to talk about the things he’s learned along the way, and he enjoys listening to and learning from others as well.