Articles

Secure Development with Java / Spring

When we develop an application security is something to take into account from the design and never forget about it!

If we detect a bug at the beginning of the development it will have a much lower cost than if we detect it later in production.

The idea is to consider security when we are going to develop applications, thinking about what bugs or security needs our application could have from the moment we start to develop it.

First recommendations:

  • There are no components that we can trust unless proven otherwise.
  • We must have authentication mechanisms that cannot be bypassed.
  • We must authorize as well as authenticate.
  • Validating data is going to save us big headaches.
  • Identifying sensitive data is also important to know what to protect.


Security risks in a web app

OWASP stands for Open Web Application Security Project. It is an open web application security project, a non-profit organization that supports infrastructure and IT security projects related to web applications.

Among the top ten security risks we have:

  1. SQL Injections
  2. Loss of authentication
  3. Sensitive data exposure
  4. XML external entities (XXE)
  5. Loss of access control
  6. Incorrect security configuration
  7. Cross site scripting (XSS)
  8. Insecure deserialization
  9. Use of components with known vulnerabilities
  10. Insufficient monitoring and logging

We will not go into detail on each one, but we will focus on the most common ones and how some tools help us to prevent them.


SQL Injection

Despite being one of the most known vulnerabilities, it is still in the TOP TEN.

Note: A SQL injection is a type of attack in which a piece of SQL code is used to manipulate a database and access valuable information.

Typical example

Suppose we want to get a user’s purchases by their id. To do this we can put together a function like the following:

 

public List<CompraDTO> obtenerComprasPorUsuarioId(String usuarioId)  throws SQLException {

String sql = «select * »

+ «from Compra where usuario_id = ‘»

+ usuarioId

+ «‘»;

Connection c = dataSource.getConnection();

ResultSet rs = c.createStatement().executeQuery(sql);

// …

Now, if we set out to exploit the vulnerability in this code we could do:

curl -X GET \

‘http://localhost:8080/compras?usuarioId=123%27%20or%20%271%27=%271’ \

 

123 or ‘1’ = ‘1

And this ends up translating to:

select *  from Compra where usuarioId = ‘123’ or ‘1’ = ‘1’

 

While this example is pretty DUMB, there are situations where we may want to concatenate strings to put together a query:

  • Complex queries with dynamic search criteria: e.g. adding UNION depending on a user’s search criteria.
  • Dynamic grouping and sorting: REST APIs used as a backend of a table for example.

Furthermore we may think that by using JPA we are saved from SQL injection but in reality JPA and ORMs do not prevent us from writing vulnerable code.

We can perfectly do something like what we see in the following example:

public List<PurchaseDTO> getPurchaseByUserId(String userId){

String jql = «Select c from Purchase c where c.userId = ‘» + userId + «‘»;

TypedQuery<Purchase> q = em.createQuery(jql, Purchase.class);

return q.getResultList()

.stream()

.map(this::toCompraDTO)

.collect(Collectors.toList());

We are using an unvalidated data input to create a JPA query so we are still exposed to the same type of vulnerability.

The solution comes from the side of using parameterizable queries.

Simple solution

public List<CompraDTO> getComprasPorUserId(String userId) throws Exception {

String sql = «select * from Purchase »

+ «where user_id = ?»;

Connection c = dataSource.getConnection();

PreparedStatement p = c.prepareStatement(sql);

p.setString(1, customerId);

ResultSet rs = p.executeQuery(sql));

……

Solution with JPA

String jql = «Select c from Compra c where c.usuarioId = :usuarioId»;

TypedQuery<Compra> q = em.createQuery(jql, Compra.class)

.setParameter(«usuarioId», usuarioId);

A good option can be to sanitize data, that is to make it safe.

private static final Set<String> VALID_COLUMNS_FOR_ORDER_BY  = Collections.unmodifiableSet(Stream

.of(«importe»,»fecha»)

.collect(Collectors.toCollection(HashSet::new)));

public List<CompraDTO> obtenerComprasPorUsuarioId(String usuarioId,  String orderBy) throws Exception {

String sql = «select * from Compra where usuario_id = ? «;

if (VALID_COLUMNS_FOR_ORDER_BY.contains(orderBy)) {

sql = sql + » order by » + orderBy;

} else {

throw new IllegalArgumentException(«Buen intento!»);

}

Connection c = dataSource.getConnection();

PreparedStatement p = c.prepareStatement(sql);

p.setString(1,customerId);

Now let’s move on to Spring…

Session control with Spring Security

Another common type of attack comes from our session side. If we use Spring we can control exactly when our session is created and how Spring Security will interact with it. For that we have the values:

  • always : a session will always be created if one does not already exist.
  • ifRequired : a session will be created only if it is required ( default )
  • never : the framework will never create a session by itself, but will use one if one already exists.
  • stateless : Spring Security will not create or use any session.

@Override

protected void configure(HttpSecurity http) throws Exception {

http.sessionManagement()

.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)

This configuration only controls what Spring Security does, not the whole application.

Concurrent session control

When a user who is already authenticated tries to authenticate again, the application can deal with that event in one of several ways. It can invalidate the user’s active session and re-authenticate the user with a new session, or allow both sessions to exist concurrently.

The first step in enabling concurrent session control support is:

@Bean

public HttpSessionEventPublisher httpSessionEventPublisher() {

return new HttpSessionEventPublisher();

}

This is very important to ensure that you are notified when the session is destroyed .

To allow multiple concurrent sessions for the same user we do the following:

@Override

protected void configure(HttpSecurity http) throws Exception {

http.sessionManagement().maximumSessions(2)

}

Timeout

After the session times out, if the user submits a request with an expired session id, he/she will be redirected to a previously configured URL

http.sessionManagement()

.expiredUrl(«/sessionExpired.html»)

.invalidSessionUrl(«/invalidSession.html»);

 

We can set the session timeout value of the embedded server session using properties:

server.servlet.session.timeout=15m

Avoid using parameters in URL

Exposing session information in the URL is a security risk. For that we can choose where to store the JSESSIONID.

servletContext.setSessionTrackingModes(EnumSet.of(SessionTrackingMode.COOKIE));

This chooses where to store the JSESSIONID : in the cookie or in a URL parameter.

Session binding

Session fixation is a very common type of attack in which it is possible for an attacker to create a session by accessing a site and then have another user log in with the same session (by sending them a link containing the session identifier as a parameter, for example).

To prevent this attack we can:

  • Regenerate the session ID at authentication.

Accept only server-generated session IDs.

  • Timeout and replace old session IDs.

Implement a robust logout feature.

  • Require a new session upon visits from suspicious senders.

Session lockout protection

http.sessionManagement()

.sessionFixation().migrateSession()

By default, Spring Security has this protection enabled («migrateSession «): on authentication a new HTTP session is created, the old one is invalidated and the attributes of the old one are copied.

If this is not the desired behavior, two other options are available:

  • when «none» is set, the original session will not be invalidated.
  • when «newSession» is set, a clean session will be created without any of the attributes of the previous session being copied.

Session Fixation – Spring Security

Session pinning policy Description
none() Spring Security will disable session fixation protection

 

migrateSession() When the user has successfully authenticated, a new session will be created and move the values from the previous session to the new session. (Default and applicable for most cases)

 

newSession() When the user is successfully authenticated, a new session is created and does not copy any attributes from the previous session.

 

changeSessionId() This is done using the new Servlet containers (Servlet 3.1 and later). It will not create a new session after user authentication, but will change the session ID.

 

 

 

Lack of Secure and HttpOnly attributes in Cookies

The Secure attribute ensures that cookies are sent via HTTPS, while the HttpOnly attribute does not allow scripts to be executed or cookies to be accessed via the DOM.

It is always recommended to implement these attributes to reduce the possibility of attack.

Secure Session Cookie

We can use HTTPOnly and secure flags to secure our session cookie:

  • httpOnly: if true, the browser script will not be able to access the cookie.
  • secure: if true, the cookie will be sent only over an HTTPS connection.

In the application.properties

server.servlet.session.cookie.http-only=true

server.servlet.session.cookie.secure=true

 

 

 

Insecure HTTP methods

Incorrect control of these methods can allow an attacker to modify or delete server resources.

If any of them are needed, it is advisable to handle accesses with a whitelist by returning «405 Method not allowed» for disallowed accesses.

For those methods that are not needed, it is recommended to disable them.

Web Application Firewall

A web application firewall (WAF) is a type of firewall that monitors, filters or blocks HTTP traffic to and from a web application.

For the review of HTTP traffic, the WAF applies a set of rules (defined in advance) to carry out the detection of malformed HTTP requests, web attacks.

A firewall protects a network, but if you are hosting web applications, you should definitely consider a WAF. It is important to note that a WAF does not replace a Firewall; they are independent devices or functions that complement each other.

To summarize!

It is important to keep security in mind from the beginning and maintain it throughout the development process! Here are some recommendations for Spring applications that we suggest you follow.

Browse all categories
Related Tags
Do you want more information? Talk us