Become a rock star Java developer. Learn how to improve your coding for the most sought-after job role in 2022. Discover how to give a security boost to your Java programs and to your career by following our secure coding best practices
Java is a language that’s used by nearly 10 million developers and runs on 56 billion devices globally. But is it a security risk? Let’s just say that Java is definitely catching the attention of attackers. Log4shell and the recently discovered Spring4shell are two common examples of malicious attacks based on Java vulnerabilities. So, does it mean that you should write your application in another language?
I wouldn’t give up on it so quickly, especially when you consider that we’re talking about the backbone of Android development and of many other applications. What can you do then? Start building your Java application with security in mind, from the very beginning and throughout the development cycle. Following Java secure coding best practices will help minimize risks and prevent your organization from joining other big names on the victims’ list.
Discover how to get the best from your “beans” and build more secure applications from class-level language to API authorization. Ready? Grab some Java (coffee) and start learning about Java secure coding best practices!
9 Java Secure Coding Best Practices
What do Atlassian Confluence (collaboration platform), Java RMI (a mechanism to make functions available over a network) and VMware Horizon (a virtualization platform) have in common? They’ve all been a victim of attacks exploiting Java vulnerabilities.
With the average cost of data breaches going to the roof, reaching a whopping $4.24 million (USD) in 2021, and 16% of organizations impacted by Spring4shell exploitation attempts in just the first four days of the attacks, writing secure code has become a matter of life and death for your organization and applications.
The following Java secure coding best practices will be the golden beans that’ll enable you to transform your code from plain, bland Java into tasty, secure code. Let’s get rolling and find out how to mess with threat actors’ taste buds!
|Java Secure Coding Best Practices||Examples|
|1. Write Simple Java Code and Keep it Clean||
|2. Implement Secure Authentication and Network Connection||
|3. Protect Sensitive Information||
|4. Avoid Data Serialization||
|5. Manage Errors Correctly||
|6. Prevent Injection Attacks||
|7. Log and Monitor Users’ Activities||
|8. Check and Install Updates Regularly||
|9. Use Java Security Manager||
1. Write Simple Java Code and Keep it Clean
“Clean code is simple and direct. Clean code reads like well-written prose. Clean code never obscures the designers’ intent but rather is full of crisp abstractions and straightforward lines of control.”
Grady Booch (one of the developers and co-authors of the unified modeling language [UML])
Yup. Keep it simple and you’ll keep it secure, error-free, and with less chance of vulnerabilities. In fact, coding is a bit like talking to people. If you’re simple and direct, your counterpart won’t need to rack their brains to understand what you’re trying to say. It’ll be crystal clear, leaving no doubt, no risk of making mistakes. That’s how effective communication works in coding as well. So how can you keep your Java code simple and clean?
Keep Everything Small and Limit Interactions
Minimize your application programming interface (API) and interface objects as much as possible; break up components and limit their interactions. Why? Because if a breach occurs (yes, it can still happen as nothing is 100% hacker-proof in the IT world), it’ll enable you to limit the damage to just the compromised component.
Minimize Classes and Methods’ Sizes
Can you imagine managing a school class of 50 or 100 students? Good luck to the teacher. It’s a much better bet to have smaller class sizes. Likewise, ensure you keep your coding classes’ (templates used to make objects) sizes smaller by focusing on a small number of functionalities. The same goes for methods (the procedure associated with an object). They should be limited to no more than one page and do a single job. If you do that, usually 30 lines of code will be more than enough.
Use Known, Tested Libraries and Frameworks
There’s no need to reinvent the wheel. It’s inefficient and risky. Java has plenty of choices with so many reliable and well-established libraries available for any possible scenario. Do some research, find the best library for your requirements, and use it. Looking for some examples? We’ve got you covered:
- Parsing libraries. When you say parsing libraries, you say JSON. It’s one of the most used parsing libraries for transferring information from a client to a server. Its non-identical twin? The slower and more verbose XML. Remember that if you’re using a Java development kit (JDK), you should opt for Jackson or GSON.
- General-purpose libraries. No matter which kind of project you’re working on, Apache Commons, including several open-source reusable components, can help you simplify a lot of tasks. Google Guava is another good open-source suite of core libraries for Java, mainly developed by Google engineers. It’s available also for Android.
- Logging libraries. Logging is an important part of our Java secure coding best practices list. We’ll learn more about it in a bit, but while we’re at it, let’s have a quick look at some libraries. JDK comes with its own logging library, however, I usually prefer other alternatives like Simple Logging Facade for Java (SLF4J), and LogBack, the successor of the old Log4j.
Refactor Your Code Often
Review and improve your code often without changing its functionalities. Refactoring should be included in your coding routine and software development process life cycle (SDLC). How? By making security a priority at every stage of the development process (devsecops or secure devops). This will make your code more consistent and much simpler to read. Do you want to know more? Learn about refactoring processes and tools in our Ultimate Programming Best Practices Guide.
2. Implement Secure Authentication and Network Connection
Authentication issues and poorly protected network connections are often causes of data breaches. How can you improve their security when writing applications in Java?
Use Java Login Modules
Authentication failures are so critical nowadays that they are also included in the OWASP’s top 10 web application security risks. To avoid them, use Java’s default login modules like:
- Lightweight Directory Access Protocol (LDAP). Where the entered username and password are checked against the user credentials stored in an LDAP directory.
- KeyStore. Used for cryptographic key-based authentication.
- Krb5. A module using the Kerberos protocol, providing strong authentication by using secret-key cryptography and a trusted third party.
(Note: In this article, we’re linking to the official documentation for the latest available version of Java. If you’re using an older version, you should be able to find the same information by selecting your version in the Oracle Help page and search by keywords)
Combine the Java login module of your choice with the Java authentication and authorization service (JAAS), and you’ll secure your Java application access against malicious attacks.
Secure Network Communication With Encryption and Message Integrity
Java ensures secure network communication through the Java secure socket extension (JSSE), supporting:
- Secure Socket Layer/Transport Layer Security (SSL/TLS). It’ll enable you to secure your application’s data transfer through data encryption and public-key infrastructure. SSL 3.0 and All TLS versions are supported.
- Datagram Transport Layer Security (DTLS). Used for datagram-based applications, DTLS is based on the TLS protocol and it helps prevent tampering or eavesdropping attacks by securing the connection.
3. Protect Sensitive Information
One of the organizations I worked for some time ago had to send me the company’s laptop by courier to my home address. This isn’t unusual in a world where working from home has become the norm. However, when I opened the box, I found a sticky note. Do you know what was it? The laptop’s password. There, in plain text, available to anyone opening the box.
OK, I guess you won’t write your application’s passwords on sticky notes, however, the concept remains the same: sensitive information must be protected at all costs. How?
Store Password Hashes
Storing plain text passwords is a no-go, even in the digital world. Why? Because it isn’t secure and leaves your and your customers’ sensitive data at risk. Use a strong hashing algorithm like SHA-256 and add salt (a random string of characters added before password hashing) and pepper (another random value added to the password but stored separately) to your password.
Filter Sensitive Information
Have you ever used the FileNotFoundException message? If you did, or if you’re planning to use it, make sure you filter it. Why? Because these kinds of messages include information about the file system’s layout and the name of the missing file.
Never Log Confidential Information
Credit card numbers, bank account details, passport numbers, and passwords all attract hackers like honey attracts bears. Avoid storing this sensitive information in log files or, even worse, in plain text. If you can’t do without them, encrypt your sensitive data as explained earlier when talking about passwords.
4. Stay Away From Serialization
Serialization enables you to transform an object into a byte stream so that can be stored on a disk or transmitted over the network. This is a cool but extremely dangerous process due to several vulnerabilities that can materialize as a result. Serialization in Java is like the gorgeous lily of the valley: it looks beautiful but can be deadly. What can you do then?
Think Before Using Serialization in Java
When you serialize a class, for example, you create a public interface that makes all class fields accessible to anyone. Therefore, before deciding to go for it anyway, think about the fields that will be exposed. Is it worth the risk?
Use Another Form of Data Transfer
JSON (we talked about this before, haven’t we?), Protocol Buffer, Apache Avro, there are many cross-platform protocols that you can use instead of Java serialization. Check them out, pick the one you prefer, and secure your application.
Use Serialization Sparely
A slow application isn’t a good application. Serialization is very CPU intensive, therefore, it’ll have an impact on your application performance. Avoid serializing some data by using the transient keyword. Then customize the serialization with readObject() and writeObject () instead like in the example below:
5. Manage Errors Correctly
Accidentally revealing useful information, like account information or system details, can be a disaster if an attacker intercepts that data. There are methods you can employ to avoid this, such as:
Keep Error Messages As Generic As Possible
What’s wrong with the error message code below?
If you check it closely, you’ll notice that the error message that the code will generate in case of an exception will expose sensitive information about the database (e.g., table and column names). That’s invaluable to potential attackers as they could use them for an injection attack that’ll enable them to get access to sensitive data stored in the database. The solution?
- Keep your error message information to a minimum (i.e., as least verbose as possible) and generic.
- Keep sensitive data separated from non-sensitive ones if you can.
- Remove debugging information before going into production.
This applies also to the error messages that the application’s users will visualize. Messages like the following can be a treasure trove of information for cybercriminals:
The entered username doesn’t exist
The password is invalid
Let’s put us in the hacker’ shoes for a moment. You want to hack an email provider and get hold of its customers’ passwords and usernames list. You type a username and password on the site’s login page and receive the following error: “The entered username doesn’t exist.” Yes! You’re now one step forward! Why? Because you now know that the username you’ve just entered doesn’t exist. You now can:
- Try your luck with another username (i.e., use brute force tactics), or
- Open a new account with that username and use it for your spamming campaigns.
So, think about these tips next time you’re working on error messages for your Java application! To view more examples of error handling best practices, check OWASP’s error handling cheat sheet.
Log Error Messages Wisely
Finding the balance between a too detailed error message (that’ll provide an attacker with everything he needs to exploit your system or application) and a too cryptic one isn’t easy but it’s still feasible if you:
- Don’t reveal the method used to determine the error. A malicious actor could use the same method or the information contained in it to get access to your system. Never reveal them, not even if requested.
- Include just enough information to allow internal investigation. Sometimes less is more and incident investigations can be successful even with a minimum of information.
- Centralize your logging. Tools available in common logging libraries will enable your security teams to analyze data in a wink with the support of tools like Kibana and Grafana.
- Never include account information or system details. Nobody needs to know the account or system details to understand an error message, especially if this critical information is included as plain text.
Monitor Your System, Always
Don’t get caught unprepared! Actively monitor your systems and event logs and let your data do the talking. Once again, tools like
can be powerful allies and can really make a difference.
When I was working as an application security engineer, those kind of monitoring tools helped us several times to identify suspicious user login or single IP peaks in real time. This enabled us to stop the attack at the very beginning saving the company thousands of dollars and keeping its reputation pristine.
6. Prevent Injection Attacks
Number three in the latest OWASP’s Top 10 web application security list, code injection (e.g., SQL injection or cross-site scripting [XSS]) is a technique used by attackers to inject malicious code into an application through a user input field (e.g., web forms). Regardless of whether injections are server-side or client-side attacks, the damage is done once the application executes the infected code. So, how can you prevent it?
Parameterize Your Queries
The example below shows a typical flaw that would enable an attacker to inject code into the query through the unvalidated “customerName” parameter:
Let’s see the same query written using a parameterized query (i.e., prepared statement). As you can see in the example below, the code will be much more secure as the hacker won’t be able to change the intent of the query:
Configure Your XML Parser
Do you know what XML external entities (XXE) attacks are? Basically, when the XXE is enabled, an attacker can create a malicious XML, and conveniently read the content of any file on an application’s server or even interact with the back end. It can lead to a denial of service (DoS), loss of confidential data, or server-side request forgery attacks (SSRF).
In Java, most XML parsers have XXE enabled by default, so you’ll have to disable it manually. How? If you are using one among the common XML parsers below, you can follow the procedure described in OWASP’s XML external entity prevention cheat sheet:
Sanitize (and Validate) Your Data
External input sanitization and validation will keep your application and organization out of trouble and minimize the risk of attacks (including Xpath injections. If you don’t take these steps, attackers can exploit to gain access to detailed information about the XML data structure or access protected data by sending malicious information to the website). How?
- Replace unwanted characters. This can be done by using the replaceAll() method available in Java or,
- Encode dangerous characters like @ or ‘. Another option is to encode characters. Interested in learning more? The OWASP’s Java encoding library is full of encoders free to use.
- Validate inputs. Filter the users’ inputs based on valid values (whitelisting or allowlisting) or based on values that should be rejected (blacklisting or blocklisting, the easiest to maintain option). You can add a minimum and maximum value range check for numerical parameters and dates, and/or a minimum and maximum length check for strings.
The example below shows how to validate the zip code parameter on a form using a regular expression (regex) in Java:
7. Log and Monitor Users’ Activities
Before you say it, no, we’re not suggesting spying on your users! The purpose of logging and monitoring your users’ activities is twofold:
- Protect information and data.
- Ensure availability and compliance with data privacy and security regulations (e.g., General Data Protection Regulation [GDPR] and the California Consumer Protection Act [CCPA]).
At the beginning of this article, we talked about the most used Java logging libraries and tools to monitor and analyze logs. The very same tools can help you keep an eye on users’ activities like:
- Logins (successful and failed).
- Password changes and resets.
- User creation and removal.
- Access level changes.
- Requests from suspicious IP ranges.
8. Check and Install Updates Regularly
When was the last time you checked your components for updates and vulnerabilities? If you don’t remember without having to consult a calendar, then it’s time to act. Vulnerable outdated components is a category that ranks sixth on the OWASP top 10 web security application vulnerabilities list. With so many dependencies, third-party plugins, and components, keeping everything up to date isn’t a task for the faint-hearted.
Add the following best practices to the security cards up your sleeve to ensure you’ll never miss a patch or update.
Scan the Libraries You’re Using and Check Your Dependencies
Software is updated all the time, patches are being released, and new versions are deployed. Skip or accidentally miss them and your organization may be quickly victim of an attack. The same happens to libraries, therefore it’s paramount that you check them regularly for vulnerabilities. Tools like
can do the job for you. By the way, if you’re using an integrated development environment (IDE), you’re in luck as most frameworks will automatically warn you about patches and updates. Don’t ignore their warnings, though.
And you, do you know how many dependencies your latest Java application has? If not, then you’ll definitely want to implement the following.
Run Static Analysis
Static code analysis rules (i.e., debugging without running the code) will help you find bugs, vulnerabilities, and more in your Java code. Pick your static analysis tool among those listed on the Analysis Tools website, check some examples of what’s available below and start securing your code.
- SonarSource. Static analysis rules are classified by severity, fully documented, and regularly updated. Can be used with SonarQube (free) or SonarCloud (free version available).
- SpotBugs. Can be used with any Java version. Add Find Security Bugs to it and you’ll get OWASP top 10 and CWE vulnerabilities covered.
- Reshift. Offering several Java security rules also detecting CWE and OWAPS top 10 vulnerabilities, it has a free version with unlimited public repositories.
Install Updates Regularly
Yes, installing updates can be a pain, but as a developer, you’re responsible to keep your components, like libraries, software modules, and frameworks up to date. How can you do that effectively? You could opt for automatic updates, when possible. However, you should never solely rely on that as something could easily go wrong (e.g., critical security updates pushed too late). Therefore, make sure you also:
- Regularly check Oracle’s page for security alerts and the Java Security Resource Center page.
- If you’re using OpenJDK subscribe to their vuln-announce mailing list.
- Subscribe to Java and cybersecurity newsletters.
- Set up a patch management program (e.g., update and patch your systems once a month on a dedicated day).
9. Use Java Security Manager
Even if Java Security Manager has been deprecated in Java 17, as many developers are still using Java 8 or versions older than 17, we thought it’s still worth including it in our Java secure coding best practices. What does this tool do? Java Security Manager lets you control and restrict Java processes that, by default, have no restrictions.
Why is it so dangerous? Anything (or anyone) that has access to everything is always a risk. The Attach API (an extension allowing your application to connect to a running virtual machine [VM]) is a typical example. Through this API, a malicious third party could easily connect to other Java VMs and control them.
Why risk it? Launch Java Security Manager to activate it:
Hint: When launched for the first time, it’ll use the default policy. If you need a different set of permissions for a specific application, you can always create your own and replace it. One size doesn’t fit all when it comes to permissions and policies
Why Java Code Security Matters
I just did a search in the National Institute of Standards and Technology (NIST) vulnerabilities database using “Java” as a keyword. Do you know how many vulnerabilities came out? More than 7,000. Not bad, eh? Of course, it’s certainly not good, either.
With Java-based attacks becoming more and more frequent, developing secure and robust code is essential for any developer and organization. Securing your Java code will enable you to:
- Reduce the risk of attacks. The more secure your code is, the smaller your risk of a data breach will be.
- Keep your data safe. Using encryption will help you limit the damage of a successful attack by adding a layer of protection to your sensitive data.
- Easily get rid of common vulnerabilities. By following Java secure coding best practices and guidelines, it’ll be much easier to stay on top of vulnerabilities saving you and your organization time and money.
- Save money. Prevention is always better than cure. Once again, fixing vulnerabilities before the worst happens will reduce all costs that may arise in case of a leak of users’ and/or customers’ sensitive information.
And I could add more points to the list like enabling you to write a code that is easier to read and maintain, increasing customer satisfaction, and adopting your products — just as a few quick examples. But I’m sure you’ve gotten the picture by now. Time to wrap up, relax and enjoy a nice cup of Java as a reward for sticking with us till the end.
Final Thoughts on 9 Java Secure Coding Best Practices to Have Down Pat
No matter if you decide to follow all the java secure coding best practices we’ve listed in this article or only a few, there are two things that you cannot do without when writing Java code: encryption and vigilance. Cybercriminals never sleep — therefore, you’ll have to make sure that your code is secure 24 hours a day, 365 days a year.
Bake security into your development lifecycle, check your code for vulnerabilities, monitor it, and log security issues. Only this way you’ll have a better chance to keep your organization and Java application safe from attacks and data breaches.
At the end of the day, Java coding is like brewing your perfect mixture of Java coffee: do it with care, pay attention to every step of the process, and keep an eye on it while it’s brewing so it doesn’t burn. Only this way, you and your users will be able to enjoy a secure and vulnerability-free application while drinking the best Java cup of coffee ever.