Java Security - Reduce Attack Surface by Constructing Secure Objects

dimitrilc
Introduction

There are many ways to design secure Java objects. In this tutorial, we are going to learn how to create secure Java objects by understanding Accessibility, Extensibility, and Immutability.

Goals

At the end of this tutorial, you would have learned:

  1. Understand how and why to limit accessibility to a class.
  2. Understand how and why to limit extensibility in a class.
  3. Understand why Immutability is important and how to create an immutable object.
Prerequisite Knowledge
  1. Basic Java.
Tools Required
  1. A Java IDE.
Limit Accessibility

Whenever we design a Java object, we should always strive to follow the principle of least privilege, which means we should limit access as much as possible. There are 4 access modifiers in Java, from least to most accessible: private, package-private, protected, and public. Developers should make use of these access modifiers to limit accessibility to data encapsulated within a class.

Let us take a look at a bad example.

    package com.example;

    import java.time.ZonedDateTime;
    import java.util.List;

    public class InsecureBirthdaysHolder {
       public List<ZonedDateTime> birthdays;
    }

The code snippet above uses the public access modifier for the birthdays List, which anybody can access.

Maybe the developer who wrote this piece of code thought that birthdays are not sensitive data as compared to social security numbers, so they did not bother limiting access to the list of birthdays. Little did they know, birthdays can usually be combined with some other publicly available information to impersonate a person’s identity for authentication with insecure services.

A better way to write this code is:

    package com.example;

    import java.time.ZonedDateTime;
    import java.util.List;

    public class SecureBirthdaysHolder {
       private List<ZonedDateTime> birthdays;

       public boolean isCorrectBirthday(ZonedDateTime input){
           return birthdays.contains(input);
       }
    }

The class above looks a lot more secure than the previous one. This time, the birthdays property is private, therefore not visible outside of this class.

Callers can only check if a birthday is in the birthday list, but they are not able to get all birthdays anymore. This method can be coupled with a limit of 3-5 queries before locking out to prevent brute force attacks as well.

Restrict Extensibility

Java classes are open to inheritance by default, which means a malicious actor can override parent methods and change the behavior.

Consider the class below, which is used to authenticate users. This class is also open to inheritance by anybody.

    package com.example;

    import java.util.List;
    import java.util.UUID;

    public class Authenticate {
       private List<UUID> uuids;

       public boolean isCorrectUUID(UUID uuid){
           return uuids.contains(uuid);
       }
    }

A malicious actor can now subclass Authenticate and override the isCorrectUUID method to always return true, therefore allowing all authentication attempts to succeed, bypassing checking in with the uuids list.

    class MaliciousAuthenticate extends Authenticate {
       @Override
       public boolean isCorrectUUID(UUID uuid){
           return true;
       }
    }

To secure the Authenticate class, make it final, and any further attempt to subclass it will fail.

    public final class Authenticate {
       private List<UUID> uuids;

       public boolean isCorrectUUID(UUID uuid){
           return uuids.contains(uuid);
       }
    }
Immutability

Value classes should ensure that their internal values are immutable. If a reference property is mutable, attackers can modify its value even if it is marked as final. One example of a mutable API is the Date class in java.util.Date.

Consider a value class MutableBirthday that contains a Date property below:

    package com.example;

    import java.util.Date;

    public final class MutableBirthday {
       private final Date birthday = new Date(2000, 1, 1);

       public Date getBirthday() {
           return birthday;
       }

       @Override
       public String toString() {
           return "birthday= " + birthday;
       }
    }

Notice that there is no setter for the class above, but because the birthday object itself is mutable, a malicious caller can just call the getter() to get a reference to the birthday object, and then the attacker can modify the object at will.

    var mutableBirthday = new MutableBirthday();
    System.out.println(mutableBirthday);
    mutableBirthday.getBirthday().setDate(10);
    System.out.println(mutableBirthday);

The code above prints:

    birthday= Thu Feb 01 00:00:00 EST 3900
    birthday= Sat Feb 10 00:00:00 EST 3900

As we can see, the caller was able to modify the internal state of the MutableBirthday class.

A solution to the code above is to use the LocalDate/LocalDateTime class, which is immutable. The methods such as plusYears or minusDays actually return a completely new instance instead of the same instance like the Date class does.

The code below is a much better implementation of the Birthday class.

    package com.example;

    import java.time.LocalDate;

    public final class ImmutableBirthday {
       private final LocalDate birthday = LocalDate.of(2000, 1, 1);

       public LocalDate getBirthday() {
           return birthday;
       }

       @Override
       public String toString() {
           return "birthday= " + birthday;
       }
    }

At the call site, regardless of what methods are called on the birthday reference, the original instance state does not change.

    var immutableBirthday = new ImmutableBirthday();
    System.out.println(immutableBirthday);
    immutableBirthday.getBirthday().plusYears(10);
    System.out.println(immutableBirthday);

The code above returns:

    birthday= 2000-01-01
    birthday= 2000-01-01
Summary

We have learned how to limit accessibility, restrict extensibility and implement immutable data classes in this tutorial.

Regarding accessibility, modules can provide another level of access control as well. Immutable data classes also have the extra benefit of being simpler to deal with when concurrency is needed.

The project code can be downloaded here: https://github.com/dmitrilc/DaniWebJavaSecureObjects

39 Views
About the Author

My name is Dimitri Nguyen. I am a Java Developer specializing in backend development on the Java/Spring/MySQL stack.

I can also work on the frontend using Angular/Typescript/JS/HTML/CSS and native Android with Kotlin.

kine_2 0 Newbie Poster

thats wonderful i am programming enthusiast. waiting for more I appreciate your effort

JamesCherrill 4,426 Most Valuable Poster Moderator Featured Poster

Good stuff.

In addition to the above, I'm a big fan of the classes in Collections.unmodifiable , eg Collections.unmodifiableList. Collections.unmodifiableMap etc
Anytime you need to give limited access to a private Collection just provide a getter that returns the Collection wrapped in the corresponding Collections.unmodifiable. Now the user can see the contents but not modify them. These classes are just a wraper round the original Collection, so are very lightweight.

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts learning and sharing knowledge.