JavaRush/Java Blog/Random EN/Security in Java: best practices

Security in Java: best practices

Published in the Random EN group
members
In server applications, one of the most important indicators is security. This is one of the types of Non-Functional Requirements . Security in Java: best practices - 1Security has many components. Of course, in order to fully cover all those protective principles and actions that are known, you need to write more than one article, so let’s focus on the most important. A person who is well versed in this topic, will be able to set up all processes and ensure that they do not create new security holes, will be needed in any team. Of course, you shouldn’t think that if you follow these practices, the application will be completely safe. No! But it will definitely be safer with them. Go.

1. Provide security at the Java language level

First of all, security in Java starts right at the language feature level. This is what we would do if there were no access modifiers?... Anarchy, no less. A programming language helps us write secure code and also take advantage of many implicit security features:
  1. Strong typing. Java is a statically typed language that provides the ability to detect type errors at run time.
  2. Access modifiers. Thanks to them, we can configure access to classes, methods and class fields the way we need.
  3. Automatic memory management. For this purpose, we (Javaists ;)) have Garbage Collector, which frees you from manual configuration. Yes, sometimes problems arise.
  4. Bytecode checking: Java compiles to bytecode, which is checked by runtime before running it.
Among other things, there are recommendations from Oracle on security. Of course, it’s not written in a “high style” and you might fall asleep several times while reading it, but it’s worth it. A particularly important document is the Secure Coding Guidelines for Java SE , which provides tips on how to write secure code. This document contains a lot of useful information. If possible, it's definitely worth reading. To spark interest in this material, here are some interesting tips:
  1. Avoid serializing secure-sensitive classes. In this case, you can get the class interface from the serialized file, not to mention the data that is being serialized.
  2. Try to avoid mutable data classes. This gives all the benefits of immutable classes (e.g. thread safety). If there is a mutable object, this may lead to unexpected behavior.
  3. Make copies of returned mutable objects. If a method returns a reference to an internal mutable object, then client code can change the internal state of the object.
  4. And so on…
In general, the Secure Coding Guidelines for Java SE contains a set of tips and tricks on how to write code in Java correctly and securely.

2. Eliminate SQL injection vulnerability

Unique vulnerability. Its uniqueness lies in the fact that it is both one of the most famous and one of the most common vulnerabilities. If you are not interested in the issue of security, then you will not know about it. What is SQL injection? This is an attack on a database by injecting additional SQL code where it is not expected. Let's say we have a method that takes some parameter to query the database. For example, username. The code with the vulnerability will look something like this:
// Метод достает из базы данных всех пользователей с определенным именем
public List<User> findByFirstName(String firstName) throws SQLException {
   // Создается связь с базой данных
   Connection connection = DriverManager.getConnection(DB_URL, USER, PASS);

   // Пишем sql request в базу данных с нашим firstName
   String query = "SELECT * FROM USERS WHERE firstName = " + firstName;

   // выполняем request
   Statement statement = connection.createStatement();
   ResultSet result = statement.executeQuery(query);

   // при помощи mapToUsers переводит ResultSet в коллекцию юзеров.
   return mapToUsers(result);
}

private List<User> mapToUsers(ResultSet resultSet) {
   //переводит в коллекцию юзеров
}
In this example, the sql query is prepared in advance in a separate line. It would seem that what the problem is, right? Maybe the problem is that it would be better to use String.format? No? What then? Let's put ourselves in the place of a tester and think about what can be conveyed in the value firstName. For example:
  1. You can pass what is expected - the username. Then the database will return all users with that name.
  2. You can pass an empty string: then all users will be returned.
  3. Or you can pass the following: “''; DROP TABLE USERS;”. And here there will be bigger problems. This query will remove the table from the database. With all the data. EVERYONE.
Can you imagine what problems this could cause? Then you can write whatever you want. You can change the name of all users, you can delete their addresses. The scope for sabotage is vast. To avoid this, you need to stop injecting a ready-made query and instead build it using parameters. This should be the only way to query the database. This way you can eliminate this vulnerability. Example:
// Метод достает из базы данных всех пользователей с определенным именем
public List<User> findByFirstName(String firstName) throws SQLException {
   // Создается связь с базой данных
   Connection connection = DriverManager.getConnection(DB_URL, USER, PASS);

   // Создаем параметризированный request.
   String query = "SELECT * FROM USERS WHERE firstName = ?";

   // Создаем подготовленный стейтмент с параметризованным requestом
   PreparedStatement statement = connection.prepareStatement(query);

   // Передаем meaning параметра
   statement.setString(1, firstName);

   // выполняем request
   ResultSet result = statement.executeQuery(query);

   // при помощи mapToUsers переводим ResultSet в коллекцию юзеров.
   return mapToUsers(result);
}

private List<User> mapToUsers(ResultSet resultSet) {
   //переводим в коллекцию юзеров
}
This way, this vulnerability is avoided. For those who want to dive deeper into this article, here is a great example . How do you know if you understand this part? If the joke below became clear, then this is a sure sign that the essence of the vulnerability is clear :D Security in Java: best practices - 2

3. Scan and keep dependencies updated

What does it mean? For those who don’t know what a dependency is, I’ll explain: it’s a jar archive with code that is connected to a project using automatic build systems (Maven, Gradle, Ant) in order to reuse someone else’s solution. For example, Project Lombok , which generates getters, setters, etc. for us at runtime. And if we talk about large applications, they use many different dependencies. Some are transitive (that is, each dependency can have its own dependencies, and so on). Therefore, attackers are increasingly paying attention to open-source dependencies, since they are regularly used and can cause problems for many clients. It is important to make sure that there are no known vulnerabilities in the entire dependency tree (which is exactly what it looks like). And there are several ways to do this.

Use Snyk for monitoring

The Snyk tool checks all project dependencies and flags known vulnerabilities. There you can register and import your projects via GitHub, for example. Security in Java: best practices - 3Also, as you can see from the picture above, if a newer version has a solution to this vulnerability, Snyk will offer to do so and create a Pull-Request. It can be used free of charge for open-source projects. Projects will be scanned at some frequency: once a week, once a month. I registered and added all my public repositories to the Snyk scan (there is nothing dangerous about this: they are already open to everyone). Next, Snyk showed the result of the scan: Security in Java: best practices - 4And after a while, Snyk-bot prepared several Pull-Requests in projects where dependencies need to be updated: Security in Java: best practices - 5And here’s another: Security in Java: best practices - 6So this is an excellent tool for searching for vulnerabilities and monitoring for updating new versions.

Use GitHub Security Lab

Those who work on GitHub can also take advantage of their built-in tools. You can read more about this approach in my translation from their blog Announcement of GitHub Security Lab . This tool, of course, is simpler than Snyk, but you definitely shouldn’t neglect it. Plus, the number of known vulnerabilities will only grow, so both Snyk and GitHub Security Lab will expand and improve.

Activate Sonatype DepShield

If you use GitHub to store your repositories, you can add one of the applications to your projects from MarketPlace - Sonatype DepShield. With its help, you can also scan projects for dependencies. Moreover, if it finds something, a GitHub Issue will be created with a corresponding description, as shown below: Security in Java: best practices - 7

4. Handle sensitive data with care

Security in Java: best practices - 8In English speech, the phrase “sensitive data” is more common. Disclosure of personal information, credit card numbers and other personal information of the client may cause irreparable harm. First of all, you need to take a close look at the application design and determine whether any data is actually needed. Perhaps there is no need for some of them, but they were added for a future that has not come and is unlikely to come. In addition, during project logging, such data may leak. A simple way to prevent sensitive data from getting into your logs is to clean up the methods toString()of domain entities (such as User, Student, Teacher, and so on). This will prevent sensitive fields from being accidentally printed. If you use Lombok to generate a method toString(), you can use an annotation @ToString.Excludeto prevent the field from being used in the output via the method toString(). Also, be very careful when sharing data with the outside world. For example, there is an http endpoint that shows the names of all users. There is no need to show the user's internal unique ID. Why? Because using it, an attacker can obtain other, more confidential information about each user. For example, if you use Jackson to serialize and deserialize POJOs into JSON , you can use the @JsonIgnoreand annotations @JsonIgnorePropertiesto prevent specific fields from being serialized or deserialized. In general, you need to use different POJO classes for different places. What does it mean?
  1. To work with the database, use only POJO - Entity.
  2. To work with business logic, transfer Entity to Model.
  3. To work with the outside world and send http requests, use third entities - DTO.
This way you can clearly define which fields will be visible from the outside and which will not.

Use strong encryption and hashing algorithms

Confidential customer data must be stored securely. To do this you need to use encryption. Depending on the task, you need to decide what type of encryption to use. Further, stronger encryption takes more time, so again you need to consider how much the need for it justifies the time spent on it. Of course, you can write the algorithm yourself. But this is unnecessary. You can take advantage of existing solutions in this area. For example, Google Tink :
<!-- https://mvnrepository.com/artifact/com.google.crypto.tink/tink -->
<dependency>
   <groupId>com.google.crypto.tink</groupId>
   <artifactId>tink</artifactId>
   <version>1.3.0</version>
</dependency>
Let's see how to use it, using the example of how to encrypt one way and the other:
private static void encryptDecryptExample() {
   AeadConfig.register();
   KeysetHandle handle = KeysetHandle.generateNew(AeadKeyTemplates.AES128_CTR_HMAC_SHA256);

   String plaintext = "Цой жив!";
   String aad = "Юрий Клинских";

   Aead aead = handle.getPrimitive(Aead.class);
   byte[] encrypted = aead.encrypt(plaintext.getBytes(), aad.getBytes());
   String encryptedString = Base64.getEncoder().encodeToString(encrypted);
   System.out.println(encryptedString);

   byte[] decrypted = aead.decrypt(Base64.getDecoder().decode(encrypted), aad.getBytes());
   System.out.println(new String(decrypted));
}

Password encryption

For this task, it is safest to use asymmetric encryption. Why? Because the application really does not need to decrypt passwords back. This is the general approach. In reality, when a user enters a password, the system encrypts it and compares it with what is in the password vault. Encryption is carried out using the same means, so you can expect them to match (if you enter the correct password ;), of course). BCrypt and SCrypt are suitable for this purpose. Both are one-way functions (cryptographic hashes) with computationally complex algorithms that take a lot of time. This is exactly what you need, since deciphering it head-on will take forever. For example, Spring Security supports a range of algorithms. SCryptPasswordEncoderYou can also use BCryptPasswordEncoder. What is a strong encryption algorithm now may be weak next year. As a result, we conclude that it is necessary to check the algorithms used and update libraries with algorithms.

Instead of output

Today we talked about safety and, of course, many things were left behind the scenes. I just opened the door to a new world for you: a world that lives its own life. With security it is the same as with politics: if you do not engage in politics, politics will engage in you. Traditionally, I suggest subscribing to my Github account . There I post my work on various technologies that I study and apply at work.

useful links

Yes, almost all articles on the site are written in English. Whether we like it or not, English is the language for programmers to communicate. All the newest articles, books, and magazines on programming are written in English. That’s why my links to recommendations are mostly in English:
  1. Habr: SQL Injection for Beginners
  2. Oracle: Java Security Resource Center
  3. Oracle: Secure Coding Guidelines for Java SE
  4. Baeldung: The Basics of Java Security
  5. Medium: 10 tips to power-up your Java security
  6. Snyk: 10 java security best practices
  7. JR: Announcement of GitHub Security Lab: protecting all your code together
Comments
  • Popular
  • New
  • Old
You must be signed in to leave a comment
This page doesn't have any comments yet