Jakarta Security and REST in the Cloud Part 1: Hello World
There is not enough discussion about Security, but there should be.
Join the DZone community and get the full member experience.
Join For FreeDespite being a crucial aspect of any application, security is a topic that few discuss in the software development industry. As a consequence, many decisions are made without taking this issue into account.
This is a series of articles that will talk about security in the Jakarta EE world with Jakarta Security with microservices in the cloud. In this first part, we will create the “Hello world” of security; we will discuss a little about its importance, the most common mistakes, and create our first contact with the Jakarta Security API.
Much has been said about information security, but after all, what does it mean? Information security is related to the protection of data for users and the company itself, based on the pillars of ISO / IEC 17799: 2005:
Confidentiality: Property that limits access to information only to legitimate entities, that is, those authorized by the information owner.
Integrity: Property that guarantees that the manipulated information maintains all the original characteristics established by the information owner, including change control and a guarantee of its life cycle (current, intermediate and permanent).
Availability: Property that guarantees that the information is always available for legitimate use, that is, by those users authorized by the information owner.
Authenticity: Property that guarantees that the information comes from the advertised source and that it has not been subject to changes during a process.
Security problems result in enormous losses for companies, whether in the short, medium, or long term. After all, even after the correction is carried out by the company, it takes time to revert to the level of trust and credibility that company had before the event, giving a great advantage to the competition.
First Java Security Tips
An excellent way to start talking about security in Java starts with some simple tips to avoid breaches in our system. So, here we list the biggest security mistakes:
Encapsulation: It is impossible to start with security problems and not mention the biggest challenge in Java applications: encapsulation problems. Indeed, in addition to being a code smell problem leaving all classes and attributes public, this attitude generates several data integrity problems. After all, the big point about OOP, according to the principles of keeping “clean” code, is the fact of hiding the data to expose the behavior. It is vital to always think about having a protected API.
Concatenated queries: A widespread problem is allowing queries from concatenated strings. Doing so may result in SQL Injections.
Beware of the Log: The log is an important point, both for checking behavior and for checking bugs. It is imperative to pay attention to the fields that will be exposed in the “toString”, for example.
Caution when exposing sensitive data: Very common in microservices. It is important to know what information will be exposed and to whom. There are a few ways to avoid this by thinking only about microservices. For example, using the notation that ignores the field to be serialized, or explaining the data that will be exposed by creating a DTO layer.
Avoid Java serialization: It is vital to pay attention to the use of this interface. There are several security problems in it, so use the Serializable interface only if necessary. Remember that, in the vast majority of cases, its use is not recommended.
Watch out for encryption or hashing algorithms: Obviously, the use of encryption is significant, so be careful with implementation.
Attention to your dependencies: Several studies estimate that about 90% of the code that is put into production is related to third party projects. Thus, it is imperative to have due attention when updating software, including the JVM itself. Keep in mind that in addition to new features and performance improvements, updates help to fix a variety of security issues if we do it often enough.
Database password: This is one of the biggest security problems. Avoid putting the password inside the code. The Twelve Factor App talks a lot about the advantages of configuration. In addition, NoSQL does not mean "NoSecurity", so it is important to enter a password or restrict access to this type of database. There is a study that says that about 75% of Redis banks that are exposed to the public do not use a password.
Access to servers and databases: This problem is much more related to operations. However, it is crucial to check access to servers and databases. That is, server access the databases they need and that only public access servers and the necessary ports must be exposed.
Using security tools is not a bad thing. There are now tools that manage security, and experts are focused on that. Much worse than paying for an expensive and mature solution, it is reinventing the wheel with a solution that causes several security problems, not to mention that it results in a waste of time and great work, making the focus leave our business.
An excellent tip to avoid a large number of these security errors is to look at the top 10 best Java security practices written by Snyk.
Hello World
After explaining the security concepts, let's create a Hello World application using Payara and the security API provided by Jakarta EE. We will create several resources, and each of them will return a simple text. However, each available service will have its own permission rule, given that we have three access rules (manager, user, and admin) and we will have the following services:
One that everyone can access without any problem.
One that only the admin accesses.
One that only the manager and admin can access.
One that the user accesses.
One that no one accesses, but what is the purpose of this example? It would be a feature that is not available to any user yet.
If we already work with JAX-RS, then we are used to creating a class that will inherit Application and will have the ApplicationPath notation. This class will safely have some more information. In that case, we will define the rules that will be used in the application. In our case, we will also use one of the authentication mechanisms that the API already provides.
By default, the Jakarta EE security API provides the following authentication mechanisms:
BasicAuthenticationMechanismDefinition
FormAuthenticationMechanismDefinition
CustomFormAuthenrticationMechanismDefinition
In this case, we will use the Basic mechanism, which we will not explain in this article because we will have a text entirely dedicated to that mechanism.
xxxxxxxxxx
import javax.annotation.security.DeclareRoles;
import javax.enterprise.context.ApplicationScoped;
import javax.security.enterprise.authentication.mechanism.http.BasicAuthenticationMechanismDefinition;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
"") (
realmName = "userRealm") (
"ADMIN", "MANAGER", "USER"}) // You need to indicate all roles that are used by the app ({
public class ApplicationConfig extends Application {
}
As soon as the user sends the information via Basic, we will need to validate the credentials. That is, the user first authenticates themselves so that they can be verified. In Jakarta EE Security, this validation process is performed thanks to the IdentityStore
, which can have several implementations, such as database, files, or LDAP. In our example, this will be really simple and everything will be managed by the memory and the username.
xxxxxxxxxx
import javax.enterprise.context.ApplicationScoped;
import javax.security.enterprise.credential.Credential;
import javax.security.enterprise.credential.UsernamePasswordCredential;
import javax.security.enterprise.identitystore.CredentialValidationResult;
import javax.security.enterprise.identitystore.IdentityStore;
import java.util.Collections;
import static javax.security.enterprise.identitystore.CredentialValidationResult.INVALID_RESULT;
public class InMemoryIdentityStore implements IdentityStore {
public int priority() {
return 10;
}
public CredentialValidationResult validate(Credential credential) {
if (credential instanceof UsernamePasswordCredential) {
UsernamePasswordCredential user = UsernamePasswordCredential
.class.cast(credential);
switch (user.getCaller()) {
case "admin":
return new CredentialValidationResult("admin", Collections.singleton("ADMIN"));
case "manager":
return new CredentialValidationResult("admin", Collections.singleton("MANAGER"));
case "user":
return new CredentialValidationResult("admin", Collections.singleton("USER"));
default:
return INVALID_RESULT;
}
}
return INVALID_RESULT;
}
}
After this super validation mechanism, the last step is to create the services and identify them according to the permission rule.
xxxxxxxxxx
import javax.annotation.security.DenyAll;
import javax.annotation.security.PermitAll;
import javax.annotation.security.RolesAllowed;
import javax.enterprise.context.RequestScoped;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
"") (
public class HelloWorldResource {
"text/plain") (
public String doGet() {
return "hello from everyone";
}
"admin") (
"ADMIN") (
"text/plain") (
public String admin() {
return "hello from admin";
}
"manager") (
"MANAGER", "ADMIN"}) ({
"text/plain") (
public String manager() {
return "hello from manager";
}
"user") (
"MANAGER", "ADMIN", "USER"}) ({
"text/plain") (
public String user() {
return "hello from user";
}
"nobody") (
"text/plain") (
public String nobody() {
return "hello from nobody";
}
}
The application is ready for us to use! An interesting thing about this example is that we can test the returns and the respective codes, as in the 401 and 403 situations.
Moving to the Cloud
I have had the pleasure of talking several times about the advantages of using cloud computing without commenting on its risk, according to the type of service that was chosen. One of the significant benefits of PaaS is the abstraction of the infrastructure layer, considerably reducing the risk of migration to the cloud, including security. Having a service team that will be responsible for controlling access to servers and containers within the cluster, in addition to the database update process and backup automation, are critical, highly abstract points that a PaaS can offer.
To facilitate the migration of local code to a cloud environment, we will use a PaaS, in this case, Platform.sh. In a nutshell, it is the second generation of PaaS from which it will manage all resources for us following the concept of infrastructure as code. Push our Git repository, and the PaaS will be responsible for creating the containers, configuring access permissions within the cluster, and finalizing the deployment in production.
To do the deployment, we need three files: One to define the application information, another for the services that the application needs, and the last to define the routes. So that we will have the following:
xxxxxxxxxx
"https://{default}/":
type upstream
upstream"app:http"
"https://www.{default}/"
type redirect
to"https://{default}/"
To configure the application:
xxxxxxxxxx
name app
type"java:11"
disk1024
hooks
build mvn clean package payara-micro bundle
web
commands
start java -jar -Xmx$(jq .info.limits.memory /run/config.json)m -XX +ExitOnOutOfMemoryError target/microprofile-microbundle.jar --port $PORT
As we use everything in memory, it is not necessary to use the service file.
In this article, we talk a little about the security aspect, the importance of thinking about it. An important code-level thing was that we were able to make this practical example with just three classes. This clearly demonstrates that there has been a considerable improvement in the security API within the Java specifications. In the second part, we will talk a little about BASIC, its advantages and disadvantages. To see the final application of this series, take a look at this link.
Opinions expressed by DZone contributors are their own.
Comments