6
Ensuring integrity of state

This chapter covers

  • Managing mutable states using entities
  • Ensuring an entity is consistent on creation
  • Ensuring an entity keeps its integrity

Mutable state is an important aspect of systems. To some degree, changing state is the point of many systems, like the online bookstore of chapter 2. The system keeps track of a variety of state changes: books are put in a shopping cart, the order is paid for, and the books are shipped to the customer. If there aren’t state changes, not much interesting happens.1  Mutable state can be represented technically in many different ways. We’ll take a look at some alternatives and explore our preferred way—explicitly modeling mutable state in the style of Domain-Driven Design (DDD) as entities, as described in chapter 3.

Because entities contain the state that represents your business, it’s important that a newly created entity follow the business rules. Entities that can be created in an inconsistent state can cause both bugs and security flaws that are hard to find or detect. But fulfilling all the constraints at the moment of creation can be difficult. How difficult depends on how strict or complicated the constraints are. We’ll walk through a couple of techniques that are suitable for handling most mutable states, starting with simple techniques for simple constraints and ending up with the builder pattern, which can handle even pretty complicated situations.

Once entities are created consistently, they need to stay consistent. We’ll guide you through some common traps that can risk the integrity of your entities, as well as providing advice on how to design so that your entities stay secure. Let’s get started with different ways of managing state so you can see why using entities is our preferred way.

6.1 Managing state using entities

A central theme for most systems is to track how the state of things changes. As a bag is loaded onto a plane, both the state of the bag and the state of the plane change, and suddenly new rules apply. The bag can’t be opened any longer, which it could be before it was loaded. On the other hand, it can now be unloaded—something that wasn’t possible before. For the plane, the load it carries increases. This is what computer scientists call mutable state. In the systems you write, you need to keep track of these state changes and ensure that all the changes follow the rules.

If the systems you write don’t handle change properly, you’ll have security issues sooner or later, whether mild or severe. A luggage handling system at an airport needs to keep track of bags. If a bag hasn’t been screened, it’s not allowed to be loaded onto a plane, and the system must stop that from happening. The system must also keep track of what bags belong to which passenger and whether that passenger boards the flight. If the passenger doesn’t show, the bags must be unloaded. Not doing so is a severe security risk and could be dangerous.

All designs in this chapter revolve around modeling change as entities (to use the terminology of DDD). As we described in chapter 3, DDD describes entities as things with a durable identity, whose state can change over its lifetime. The baggage in the airport is a good example. It can be checked in, screened, loaded onto, and unloaded from flights, but we still perceive it as the same bag with a changed state. Entities are our preferred way to implement mutable state, but let’s briefly look at the alternatives (figure 6.1).

figure06-01.eps

Figure 6.1 Some different ways to implement state

When implementing a system, there are many ways to track and handle how state changes:

  • You can keep state in a cookie.
  • You can make changes in the database directly using SQL or stored procedures.
  • You can use an application that loads the state from the server, updates the state, and sends it back.

All of these approaches are possible and have various merits. Unfortunately, a lot of systems are made up of an inconsistent mix of these approaches. This is a risk. If the responsibility for holding state and controlling its changes is too spread out, there’s a danger that the different parts won’t fit together perfectly. Those small logical cracks are what open up the possibility of security breaches. Therefore, what we prefer is a kind of design where it’s easy to see what states are allowed and what changes are permitted according to the state.

In our experience, the most effective way to ensure a mutable state is handled in a safe and secure way is to model states as entities in the style of DDD. During modeling, you decide what concepts are most important to model. Is it bag, flight, and passenger that makes it easiest to understand the rules, or should you think about the situation in terms of check-in, loading, and boarded travelers? In the first case, the business rule can be expressed as “a bag is only allowed to be on the same flight as the passenger that checked in the bag.” In the second case, the same rule would be rephrased as “the loading of the plane may only contain checked-in bags that belong to the boarded travelers.” In this example, you might agree that the first phrase reads more easily, so that would be your model. But it’s effort well spent to explore different models, as we described in chapter 3, and to seek deeper understanding, as the case study in chapter 2 showed.

Entities have the benefit of collecting all the understanding of state and how it changes in the same place. We also prefer to implement mutable state using a class that keeps the data with the associated behavior as methods in the same class. You’ll see numerous examples of this in this chapter and the following chapter, which focuses on how to reduce the complexity of state.

Entities can be designed in an infinite number of ways, so we’re going to share some of our favorite tricks and patterns to keep your design and code clear and secure. The rest of this chapter is about how to create and keep entities in a consistent state that upholds business integrity. Let’s get started with how to create entities in a secure way.

6.2 Consistent on creation

An entity that isn’t consistent with the business rules is a security problem. This is particularly true for a newly created entity, so it’s important that the mechanism for creating objects guarantees entities are consistent on creation. This might seem obvious, but still it’s sometimes treated as a technicality—and the consequences just might be disastrous.

A colleague of ours was once working with a large Asian bank. He found several security vulnerabilities, but they were all dismissed as minor technical flaws. It wasn’t until he managed to create an account without an owner that he got attention. A bank account without an owner is an abhorrence, and the existence of such an account could cause a bank to lose its banking license. Suddenly the issue was escalated to top management, who gave it the highest priority.

An entity that isn’t consistent with the rules is a security problem, and the best way we’ve found to counteract this risk is to insist that every entity object should be consistent immediately on creation. In this section, we’ll show the perils of the most common anti-pattern—the no-arg constructor—and look at some alternative designs. We’ll walk through different ways to create an entity, starting with the simplest and progressing to the more advanced. When constraints become more complex, you need more complicated constructions. Simple constructions suffice for simple constraints. We’ll start with the simplest construction of all, the no-arg constructor, which is so simple, it’s useful for almost nothing at all. We’ll end with the builder pattern for handling the most complex situations.

Because entities often represent data that’s stored and changed over a long period of time, entities are often saved in a database. If you have a relational database together with an object-relational mapper (ORM) such as JPA or Hibernate, there’s often confusion that leads to bad and insecure design. We’ll comment on different ways of using such frameworks in a way that doesn’t violate security.

Although our introductory example in this chapter was from the financial domain, the problem isn’t restricted to that domain. We find inconsistent newly created entities in code in all kinds of domains. One thing that many have in common is that they often stem from constructing objects using a no-arg constructor.

6.2.1 The perils of no-arg constructors

The simplest way to create an entity is definitely using the constructor. And what could be simpler than calling a constructor without any arguments (aka, a no-arg constructor)? The problem is that no-arg constructors seldom live up to the promise of creating a fully consistent, ready-to-use object.

If you think about it, a no-arg constructor is a strange thing to find in code. It promises not only to create a Car object, for instance, but a car for which you don’t need to specify any attributes at all. It doesn’t have a color, a number of doors, or even a brand. Or, if it has any of those attributes, it’s a default value that should apply to all cars on creation. In practice, no-arg constructors don’t stand up to the contract for constructors to create consistent objects that are ready to use.

Often, we encounter entities that seem to have a convention for creation: first you call a no-arg constructor, then you call a number of setter methods to initialize the object before it’s ready to be used. But there’s nothing in the code that enforces this convention. And, sadly, the convention is often forgotten or broken in a way that leads to inconsistent entities.

Let’s look at the kind of code we often encounter. In listing 6.1, you see an Account class with some attributes: an account must have an account number, an owner, and an interest rate. It might also have an optional credit limit, which allows the account to go into debt to a certain degree, or a fallback account (often a savings account), from which money will be drawn if needed to prevent the account from becoming empty or going into debt. The method AccountService.openAccount shows how this no-arg constructor is intended to be used. First the constructor is called, and then the Account object is filled with data by calling one setter method after another.

Listing 6.1 Account class with no-arg construction and setter initialization

public class Account {
    private AccountNumber number;    ①  
    private LegalPerson owner;    ①  
    private Percentage interest;    ①  
    private Money creditLimit;    ②  
    private AccountNumber fallbackAccount;    ②  

    public Account() {}    ③  

    public AccountNumber getNumber() {
        return number;
    }

    public void setNumber(AccountNumber number) {    ④  
        this.number = number;
    }

    public LegalPerson getOwner() {
        return owner;
    }

    public void setOwner(LegalPerson owner) {    ④  
        this.owner = owner;
    }
    ...
}

class AccountService {

    void openAccount() {
        Account account= new Account();    ⑤  
        account.setNumber(number);    ⑥  
        account.setOwner(accountowner);    ⑥  
        account.setInterest(interest);    ⑥  
        account.setCreditLimit(limit);    ⑦  
        ...
    }
}

What this approach lets you do is create a completely blank object and then fill in the fields you want. But there’s no guarantee your Account object will fulfill even the most fundamental and important business constraints. Furthermore, the design is brittle because it relies on every creation to remember all the steps. If the conditions change, there’s an update nightmare. For example, as part of international attempts to curb corruption, many countries have passed financial regulations on people classified as politically exposed. People in government, for instance, might be more exposed to temptations of corruption or bribery due to the influence of their office. Each account has to trace whether the owner is a politically exposed person or not.

Imagine you’re working with Account and are given the requirement that it needs a new mandatory field, boolean politicallyExposedPerson. Also, it’s required to set the field explicitly each time an entity is created. Now you need to find every single place the constructor new Account() is called and ensure that setPoliticallyExposedPerson is also called.

The compiler doesn’t point out mistakes as it would if you added a parameter to a constructor argument list. A well-honed suite of unit tests would catch the errors, but the codebases with no-arg constructors we see have seldom had those kinds of test suites. Unfortunately, some places will be overlooked each time an attribute is added; not necessarily the same place every time, but rather different places each time. Over time, that kind of process tends to result in an inconsistent codebase, where sooner or later there are security loopholes.

6.2.2 ORM frameworks and no-arg constructors

If you use an object-relational mapper framework such as JPA (Java Persistence API) or Hibernate, it might seem like you’re forced into having a no-arg constructor for your entities. Tutorials for these frameworks inevitably start with creating an entity with a no-arg constructor, and it looks like that’s the way code should or must be written. But that’s not completely true. If you use such frameworks, you have two options for avoiding security loopholes in this regard: either separating the domain model from the persistence model or ensuring that the persistence framework is secured from exposing inconsistent objects.

The first alternative is to conceptually separate from the persistence model. If you do this, your persistence model resides in a separate package, together with other infrastructure code. When you load data from the database, the persistence framework loads it into objects in the persistence package. Thereafter, you construct domain objects using those objects before letting the domain objects handle business-logic calls. In this way, you’re completely in charge of any creation of domain objects, and any JPA annotations stay in the persistence package.

If you don’t make this distinction and directly map your domain objects using the persistence framework, you’ll need to do due diligence on how you use the framework instead. We know this style is pretty common, and it’s possible to do it in a secure way, so we’d like to share some tricks for doing so.

When it comes to no-arg constructors, it’s true that persistence frameworks like Hibernate and JPA do need a no-arg constructor. The reason for this is that the framework needs to create objects when it loads data from the database. The way it does so is by internally creating an empty object and then filling it with data from the database through reflection. It therefore needs to have a no-arg constructor to start with. But neither JPA nor Hibernate needs a public constructor—they can do well with a private no-arg constructor. Furthermore, the frameworks don’t need setter methods to inject the data; it can be done through reflection directly into private fields if you set the persistence style to field annotations.

Now, let’s move on to what you can do about this situation. Because you don’t want a no-arg constructor, you can instead use a constructor that sets all mandatory fields.

6.2.3 All mandatory fields as constructor arguments

Let’s take a look at the simplest way of solving the security problem of inconsistent entities: instead of using a no-arg constructor that doesn’t convey enough information for consistent creation, use a constructor where you pass in all the required information (figure 6.2).

figure06-02.eps

Figure 6.2 We don’t want half-finished cars to be visible to the outside.

Extend the constructor parameter list until all mandatory information is sent. There’s no need for parameters for optional information at this stage. Ensure that the entity is created in a fully consistent state. Optional information can be supplied after the entity is constructed by calling one method at a time.

Listing 6.2 shows the result of applying this approach to the previous Account example. The constructor requires an account number, owner, and interest rate—all the mandatory attributes. The optional attributes for credit limit and fallback account aren’t in the constructor list but are set afterwards via separate method calls.

Listing 6.2 Constructor takes the mandatory attributes, methods the optional

import org.apache.commons.lang3.Validate.*;

public class Account {
    private AccountNumber number;
    private LegalPerson owner;
    private Percentage interest;
    private Money creditLimit;
    private AccountNumber fallbackAccount;

    public Account(AccountNumber number,
                    LegalPerson owner,
                    Percentage interest) {    ①  
        this.number = notNull(number);    ②  
        this.owner = notNull(owner);    ②  
        this.interest = notNull(interest);    ②  
    }

    protected Account() {}    ③  

    public AccountNumber number() { ... }    ④  

    public LegalPerson owner() { ... }

    public void changeInterest(
                    Percentage interest) {    ⑤  
        notNull(interest);    ⑥  
        this.interest = interest;
    }

    public Money creditlimit() { ... }

    public void changeCreditLimit(
                    Money creditLimit) {    ⑦  
        notNull(creditLimit);
        this.creditLimit = creditLimit;
    }

    public AccountNumber fallbackAccount() {
        return fallbackAccount;
    }

    public void changeFallbackAccount(AccountNumber fallbackAccount) {
        notNull(fallbackAccount);
        this.fallbackAccount = Validate.notNull(fallbackAccount);
    }

    public void clearFallbackAccount() {
        this.fallbackAccount = null;
    }
}

class AccountService {
    void openAccount() {
        AccountNumber number = ...
        LegalPerson accountowner = ...
        Percentage interest = ...
        Money limit = ...    ⑧  
        Account account =
            new Account(number,
                        accountowner,
                        interest);    ⑨  
        account.changeCreditLimit(limit);    ⑩  
        accountRepostitory.registerNew(account);
    }
}

Because the constructor list contains mandatory fields, don’t expect any of them to have a null argument at any time. You can include such checks in the constructor.

We are slowly moving from the most basic objects with only mandatory attributes toward more complex conditions, sometimes with more attributes. Taking all those attributes as parameters to the constructor soon becomes awkward. We guess you’ve felt the awkwardness of 20-parameter constructors at some point.2  Also, with more complex conditions that span between attributes, it’s hard for a constructor to uphold them.

The most complex types of entities will need the builder pattern to be created in a feasible way. But, on our way to the builder pattern, we’ll first look at an interesting way to create objects with mandatory and optional fields in a way that makes the code on the client side read fluently. We turn our attention next to fluent interfaces.

6.2.4 Construction with a fluent interface

For construction of more advanced entities with more constraints, you need a more potent tool. Eventually we’ll get to the builder pattern, but we aren’t there yet. On our way to this, we’ll start with a design style that makes the builder pattern easier to understand and use, as well as making the client code simpler to read and ensuring it does the right thing. We’re talking about the fluent interface.

This style of interface design was given its name by Eric Evans and Martin Fowler in 2005, even though the design style it describes has roots in the Smalltalk community that dates back to the 1970s. The ambition of a fluent interface is to make the code read more like a fluent text in a natural language, and this is often accomplished through the form of method chaining.

Let’s describe this style by applying it so you can see how it affects the code needed to set up an entity. Listing 6.3 shows the code for Account, adapted to provide a fluent interface. The constructor doesn’t change, but note the methods for the optional credit limit and fallback account. These methods also return a reference to the modified instance, the object itself. You can see in AccountService.openAccount how this enables the client code to chain or cascade these method calls so that the code almost reads as text.

Listing 6.3 Account class with a fluent interface

public class Account {
    private final AccountNumber number;
    private final LegalPerson owner;
    private Percentage interest;
    private Money creditLimit;
    private AccountNumber fallbackAccount;

    public Account(AccountNumber number,
        LegalPerson owner,
        Percentage interest) {
        ...    ①  
    }

    public Account withCreditLimit(Money creditLimit) {
        this.creditLimit = creditLimit;
        return this;    ②  
    }

    public Account withFallbackAccount(AccountNumber fallbackAccount) {
        this.fallbackAccount = fallbackAccount;
        return this;    ②  
    }
}

class AccountService {

    void openAccount() {
        Account account = new Account(number,
                                      accountowner,
                                      interest)    ③  
                            .withCreditLimit(limit)
                            .withFallbackAccount(fallbackAccount);
        ...
    }
}

There’s no doubt that fluent interfaces yield code that reads differently from conventional code; it reads more fluently. But there are trade-offs. Most importantly, this design style clashes with one flavor of the command-query separation (CQS) principle, which states that a method should either be a command or a query.3  A usual interpretation is that a command should change state but return nothing, and a query should return the answer but change nothing. In this example of a fluent interface, the with… methods do change state but don’t have void as a return type—perhaps a small violation, but certainly something to take into account.

Fluent interfaces work well when you want to enrich your object under creation, one step at a time (first credit limit, then fallback account). But the object needs to be consistent after each step. By themselves, fluent interfaces aren’t enough to enforce advanced constraints. Alone, this approach doesn’t handle restrictions that span multiple properties; for example, if an account is required to have either a credit limit or a fallback account but isn’t allowed to have both. To handle these more advanced constraints, we’ll combine fluent interfaces with the builder pattern—but first, you’ll see what advanced constraints might look like.

6.2.5 Catching advanced constraints in code

Advanced constraints on an entity might be restrictions among attributes. If one attribute has a certain value, then other attributes are restricted in some way. If the attribute has another value, then the other attributes are restricted in other ways. These kinds of advanced constraints often take the form of invariants, or properties that need to be true during the entire lifetime of an object. Invariants must hold from creation and through all state changes that the object experiences.4 

In our example of the bank account, we have two optional attributes: credit limit and fallback account. An advanced constraint might span both of these attributes. For the sake of the example, let’s look at the situation where an account must have either but isn’t allowed to have both (figure 6.3).

figure06-03.eps

Figure 6.3 Safeguarding a bank account by either a credit limit or a fallback account

As a diligent programmer, you need to ensure that you never leave the object with any invariant broken. We’ve found it fruitful to capture such invariants in a specific method, which can be called when there’s a need to ensure that the object is in a consistent state. In particular, it’s called at the end of each public method before handing control back to the caller. In listing 6.4, you can see how the method checkInvariants contains these checks. In this listing, the method checks that there’s either a credit limit or a fallback account, but not both. If this isn’t the case, then Validate.validState throws an IllegalStateException.

Listing 6.4 Checking advanced constraints in separate method

import static org.apache.commons.lang3.Validate.validState;

    private void checkInvariants()
        throws IllegalStateException {    ①  

        validState(fallbackAccount != null
                    ^ creditLimit != null);    ②  
    }

You don’t need to call this method from outside the Account class—an Account object should always be consistent as seen from the outside. But why have a method that checks something that should always be true? The subtle point of the previous statement is that the invariants should always be true as seen from outside the object.

After a method has returned control to the caller outside the object, all the invariants must be fulfilled. But during the run of a method, there might be places where the invariants aren’t fulfilled. For example, if switching from credit limit to fallback account, there might be a short period of time when the credit limit has been removed, but the fallback account isn’t set yet. You can see this moment in listing 6.5: after creditLimit has been unset but before fallbackAccount is set, the Account object doesn’t fulfill the invariants. This isn’t a violation of the invariants, as the processing isn’t finished yet. The method has its chance to clear up the mess before returning control to the caller.

Listing 6.5 Changing from a credit limit to a fallback account

    public void changeToFallbackAccount(AccountNumber fallbackAccount) {
        this.creditLimit = null;    ①  
        this.fallbackAccount = fallbackAccount;    ②  
        checkInvariants();    ③  
    }

The design pattern of having a validation method together with the fluent interface design lets you tackle a lot of complexity. But there’ll always be situations where that doesn’t suffice. The ultimate tool is the builder pattern, which is the topic of the next section.

6.2.6 The builder pattern for upholding advanced constraints

You have seen how to ensure an object is created consistently by adding all mandatory fields to the constructor and using setters for optional attributes. But if there are constraints between optional attributes, this doesn’t suffice.

Let’s continue the example where a bank account must have either a credit limit or a fallback account set but not both. We’d like to do the construction in steps but still ensure that the object is fully created with all constraints fulfilled before the rest of the code gets access to it. This is what the builder pattern does.5 

The basic idea of the builder pattern is to hide the complexity of constructing an entity within another object, the builder. Figure 6.4 shows how a car builder hides the car so that you never need to see the half-built car; it’s revealed when the assembly is finished and the car is complete.

figure06-04.eps

Figure 6.4 Hiding a half-finished car until it’s finished

Now let’s see what this looks like in code using the account example. In listing 6.6, you see the client code. We start by creating an AccountBuilder, fiddle around with it (ordering it to build an account inside itself), and when we’re satisfied, we tell the AccountBuilder to reveal the finished account.

Listing 6.6 Account with a credit limit constructed using AccountBuilder

    void openAccount() {
        AccountBuilder accountBuilder =    ①  
                new AccountBuilder(number,    ②  
                                   accountOwner,
                                   interest);
        accountBuilder.withCreditLimit(limit);    ③  
    ④  
        Account account = accountBuilder.build();    ⑤  
        ...
    }

In this example, if you had wanted an account with a fallback account, you would have called withFallbackAccount before finishing the build. This pattern extends well to more complex situations too. In those cases, you fiddle around with the builder for a little bit longer, calling more methods to configure the product before calling build to obtain the final product. There’s no need for a multitude of constructors or overloaded methods. And if AccountBuilder has a fluent interface where the method withCreditLimit returns a reference to the builder itself, the code can be even more elegant:

    void openAccount_fluent() {
        Account account =
                new AccountBuilder(number,    ①  
                                   accountOwner,
                                   interest)
                        .withCreditLimit(limit)    ②  
                        .build();    ③  
        ...
    }

The tricky part of the builder pattern is how to implement the builder. The account builder needs to be able to manipulate an Account object even when it’s not consistent (something you want to avoid to start with). Remember that you can’t let the builder leave the product in an inconsistent state, because the account builder can’t work on the account from the outside.

The classic way of putting the builder on the inside is to put both classes (Account and its builder) in a module, then provide one interface that the account can show the rest of the world and another interface that the builder can work with. This works, but sooner or later you’ll get a headache from doing that.

Listing 6.7 shows how we can get around this dilemma using inner classes in Java. The inner class Builder inside Account has access to the inner mechanics of Account without any special methods. Because Builder is a static class, it can create a not-yet-complete Account using the private constructor and work on it until the client on the outside calls the method build to reveal the finished Account object.

Listing 6.7 Builder of accounts implemented as an inner class

import static org.apache.commons.lang3.Validate.notNull;
import static org.apache.commons.lang3.Validate.validState;

public class Account {
    private final AccountNumber number;
    private final LegalPerson owner;
    private Percentage interest;
    private Money creditLimit;
    private AccountNumber fallbackAccount;

    private Account(AccountNumber number,
                    LegalPerson owner,
                    Percentage interest) {    ①  
        this.number = notNull(number);
        this.owner = notNull(owner);
        this.interest = notNull(interest);
    }

    ...

    private void checkInvariants() throws IllegalStateException {
        validState(fallbackAccount != null
                    ^ creditLimit != null);    ②  
    }

    public static class Builder {    ③  
        private Account product;

        public Builder(AccountNumber number,
                       LegalPerson owner,
                       Percentage interest) {    ④  
            product = new Account(number, owner, interest);
        }

        public Builder withCreditLimit(Money creditLimit) {
            validState(product != null);    ⑤  
            product.creditLimit = creditLimit;
            return this;    ⑥  
        }

        public Builder withFallbackAccount(AccountNumber fallbackAccount) {
            validState(product != null);    ⑤  
            product.fallbackAccount = fallbackAccount;
            return this;    ⑥  
        }

        public Account build() {
            validState(product != null);
            product.checkInvariants();    ⑦  
            Account result = product;
            product = null;    ⑧  
            return result;    ⑨  
        }
    }
}

By making the Account constructor private, you ensure that the only way to construct an account is with the Builder class and build method. The account under construction can be inconsistent as long as it can only be accessed by the builder object that’s building it. But when it’s time to release the account from the builder, it’s required to fulfill all invariants.

When the Account is delivered to the caller using build(), you also make the builder object release the reference to the product (Account). This is to ensure that another call to build won’t hand out another reference to the same Account. Once the builder produces an Account, it self-destructs. From an object-orientation theoretical point of view, the concept of an inner class is a bit strange, but from an implementation point of view, it’s practical in this specific situation.

Used this way, the builder process replaces calling the constructor. Phrased slightly differently, you can say that when creating an object is a complex process, use the builder process in multiple steps; when it’s a simple process, use a constructor in a single step. Note the similarity between the words constructor and builder —they mean the same thing.

6.2.7 ORM frameworks and advanced constraints

When you have entities with the kind of complexity we’re handling here, with constraints that span attributes, you need to think about how you relate to the database. This is true if you use an ORM framework such as JPA or Hibernate to directly map your domain objects to the database.

If your domain is small and you have full control of the only application that touches the database, you might consider it to be inside your security region, your trusted area of control. In that case, you might assume that the data in the database is consistent with your business rules and that you can safely load it into your application without validation. Things stay simple, but you should be sure you have strict control over your data.

If your domain is a little richer, or if you can’t guarantee that you’re the only one touching the database, we recommend that you look at the database as a separate system and validate data when loading it. If you’re using your domain entities to directly map to the database using the ORM, things get more delicate. It’s still possible, but we advise that you read up on persistence frameworks.

For advanced constraints, you’ll need to ensure that the invariants hold after you’ve loaded the data from the database, which is exactly what the checkInvariants method does. All you need to do is ensure that it’s run on load. This is what the annotation @PostLoad, demonstrated in the following listing, does in both JPA and Hibernate.

Listing 6.8 Integrating invariants check with an ORM framework

@PostLoad    ①  
private void checkInvariants() throws IllegalStateException {
    Validate.isTrue(fallbackAccount != null
                ^ creditLimit != null);    ②  
}

6.2.8 Which construction to use when

In this section, we showed three patterns for ensuring that entities are created in a consistent state: constructor with mandatory attributes, fluent interface, and the builder pattern. All three have the common goal of ensuring that an object is consistent on creation by moving away from the perils of a no-arg constructor.

Even if the builder pattern makes it possible to build the construction over the run of lots of calls to the builder, we recommend that the builder be a short-lived object—the main limitation of all these approaches. Exactly what short-lived means depends on the situation, but for a web-based system, we recommend finishing the construction in the same request or client-server round-trip. If the construction is so complex that it needs several steps of interaction with the client, we’d rather recommend that you make initialization a state of its own in the entity.

Now that your entity is constructed in a nice and consistent state, you certainly want it to stay that way. You don’t want the integrity of your entity to be compromised so that its security is in the hands of the calling code.

6.3 Integrity of entities

Entities that are created following all the business rules are a good thing. It’s even better if they continue to follow the rules, and one of the points of designing entities as objects is that you can encapsulate all the relevant business logic close to the data. What you want to avoid at this stage is designs that leak that data into the hands of your clients in a way that enables them to change the data in violation of the business rules. This is what information security professionals refer to as protecting the integrity of the information.

If there are ways to change entity data without the entity being in control, you can be almost sure that sooner or later there’ll either be a mistake or a malicious attack that changes the data in violation of the rules. In either case, bad integrity of entities is a security problem. The basic trick of ensuring the integrity of entities is to never leave anything mutable accessible to the outside.

In this section, we want to share some common pitfalls of designs that don’t protect the integrity of entities, as well as ways of designing so that integrity is protected. We’ll look at some different cases: fields with getter and setter methods, mutable objects, and collections.

6.3.1 Getter and setter methods

Most developers agree that leaving a data field open as a mutable public field is a bad idea—and to many, it’s close to taboo to even discuss it. But to our surprise, there are many developers who are happy to leave fields equally open by providing a pair of unrestricted setter and getter methods. It might be aesthetically more pleasing according to some tastes, but from a security point of view, it’s equally bad. Let’s look at the ramifications of setters and getters.

In the following listing, the attribute paid of an order is protected as a private field. But it can still be manipulated by the outside in the same way as if it was unprotected because it has an unrestricted setter and getter method pair.

Listing 6.9 Data field not really protected when there is a setter method

class Order {
    private CustomerID custid;
    private List<OrderLine> orderitems;
    private Addr billingaddr;
    private Addr shippingaddr;
    private boolean paid;    ①  
    private boolean shipped;

    public void setPaid(boolean paid) {
        this.paid = paid;    ②  
    }
    public boolean getPaid() { return paid; }
}

Order order = ...
order.paid = true;    ③  
order.setPaid(true);    ④  

Let’s work with this for a while. You want to protect the field paid from being changed arbitrarily. Making it private, like the data field boolean paid in the Order class, is a good start. But protecting data fields doesn’t help much when the data is open to arbitrary modification through setter methods. In some cases, there’s intelligent functionality in the setter and getter methods, and they do some good for security by encapsulating behavior. But often setters and getters open up the data field to arbitrary access and modification.

What kind of behavior might be interesting to encapsulate? Let’s return to the paid field. Is it sensible that its value can be changed without restrictions? Probably not. In this case, it only makes sense to go from not paid to paid, which happens when you receive a payment for the order. There’s no business case for going in the opposite direction.

You can secure this design by restricting how the data can change. The obvious way is to replace the arbitrary setPaid with a method for the specific situation of marking an order as paid:

class Order {
    private boolean paid = false;    ①  
    private boolean shipped;

    public void markPaid() { this.paid = true; }    ②  
    public boolean isPaid() { return paid; }
}

Now you’ve ensured that the attribute paid can only change according to business rules. It’s important to note that encapsulation is about enclosing the interpretations and rules about the data together with the data. It’s not about just technically protecting a data field from direct access.

6.3.2 Avoid sharing mutable objects

An entity needs to share its data with its surroundings. The Order entity in listing 6.9 will certainly need to share the shipping address sooner or later. The safest way for an entity to do this is by sharing domain primitives, which are immutable and therefore safe to share (as explained in chapter 5).

Sharing an object that’s possible to mutate comes with the risk that the reference will be used to change the object you’re using for your state representation. In listing 6.10, the attribute Person.name is represented by an immutable String, but the attribute Person.title is represented by a mutable StringBuffer. Even if the code for accessing them looks alike, the difference is fundamental. When the immutable name is used, the object Person keeps its integrity. But when the mutable title is used, it accidentally changes the representation that Person uses for keeping its state. The integrity of the Person object is violated.

Listing 6.10 Person class that shares one immutable object and one mutable one

class Person {
    private String name;
    private StringBuffer title;

    String name() {
        return name;    ①  
    }

    StringBuffer title() {    ②  
        return title;
    }
}

String personalizedLetter(Person p) {
    String greeting =
        p.name()
         .concat(", we'd like to make you an offer");    ③  
    String salute =
        p.title()
          .append(", we'd like to make you an offer")
          .toString();    ④  
    ...
}

The risk of changing mutable objects in data fields is why we advise using immutable domain primitives instead, both for representation of data fields and for method arguments and return types.

If for some reason you find yourself in a situation writing code where your only option is to work with a mutable object, there’s a trick. When your methods return a reference to an encapsulated object, first clone the object. In this way, you avoid having your mutable object changed by someone else. If they use the reference you give them to mutate the object, they’ll only change their copy, not your original. In the following listing, you see this trick applied to java.util.Date.

Listing 6.11 Person class that returns a clone of birthdate

class Person {

    private Date birthdate;

    Date birthdate() {
        return birthdate.clone();    ①  
    }
}

Most often, it’s not too hard to avoid sharing mutable objects because many modern libraries have good immutable classes. But there’s one case that often surfaces as a problem—collections.

6.3.3 Securing the integrity of collections

Even if a class is well designed so as not to leak mutable objects, there’s one tricky area: collections such as lists or sets. For example, an Order object of an online bookstore might have a list of order lines, where each line describes a book being bought together with the quantity. On the inside, this is stored as a data field, List<OrderLine> orderItems. For such collections, we’ve seen a few pitfalls that we’d like to share.

To start with, you obviously must not expose the list to the outside. Making the field orderItems public would allow anyone to replace the list arbitrarily. Neither should you use a setter method like void setOrderItems(List<OrderLine> orderItems) that does the same thing. Instead of exporting the collection to the outside and letting the client work on the list, you want to encapsulate what is done inside the entity.

For example, to add items to the list, you should have a void addOrderItem(OrderLine orderItem) method. If you want to know the total quantity of items in the order, you don’t give the client the list to do the sum; instead, you capture the calculation inside an int nrItems() method. Think of the entity as a gravity well for functionality and let computations move into the entity. The result over time is a much greater consistency of business rules, as well as greater integrity of data. You might even come to the point where there’s no need to expose the list at all because everything that works on that list is now inside the entity abstraction.

If there’s still a legitimate need for the rest of the code to work on the list of order items, then you need to share it in some form; for example, by providing a method List<OrderItem> orderItems(). But now you’re back to the same problem as before. The list is a mutable object, and there’s nothing that stops a client from obtaining a reference to the order item list and then modifying it by adding new order items or removing existing items. In the following listing, you can see how void addFreeShipping(Order) works directly on the order item list.

Listing 6.12 Violating integrity by working on the list from outside

void addFreeShipping(Order order) {
    if(order.value().greaterThan(FREE_SHIPPING_LIMIT) {
        List<OrderLine> orderlines = order.orderItems();
        orderlines.add(    ①  
            new OrderLine(SHIPPING_VOUCHER, 1));
    }
}

In this example, the method orderItems gives out a reference to the list where order items are stored. The client directly changes this list, and the Order object doesn’t get a chance to control the change. This is definitely a security loophole, and something we see quite often.

The way to secure this design is to ensure that when an entity returns its data, it should be a nonmodifiable copy. For data fields of primitive types, this isn’t a problem. In the previous example of boolean isPaid(), the value returned is a copy of the boolean value in the data field. The receiver can do whatever it likes without affecting the Order. To secure the design for List<OrderItem> orderItems(), you must ensure the copy that’s returned can’t be used to make changes to your internal list. You can clone the list in the same way as you did with Date, but for collections, there’s also a special trick using a read-only proxy.

Starting with cloning, the usual way to copy collections isn’t by using clone but instead by using the so-called copy constructors. Every class in the collections library in Java has a constructor that takes another collection as an argument and creates a new collection with the same content. You can see in the following listing how this is used for orderItems, which returns a copy of the list of order items.

Listing 6.13 Copy constructor to return a copy of the list of order items

class Order {
    private List<OrderLine> orderitems;    ①  
    public List<OrderLine> getOrderItems() {
        return new ArrayList(orderitems);    ②  
    }
}

The caller of orderItems gets a copy of the collection, and any changes are made to that copy, not to the list inside Order. The drawback of this approach is that the caller can still do operations on the list, thinking they should result in state changes. This can lead to hard-to-find bugs. But, as we mentioned, for collections there’s another neat trick available. In the handy Collections utility class, there are many useful static methods. One of them is

static <T> List<T> unmodifiableList(List<? extends T> list)

You can see this used in listing 6.14. The method returns a read-only proxy to the original list. Any attempt to call a modifying method on the returned list results in an UnsupportedOperationException.6 

Listing 6.14 Exporting unmodifiable collections to protect internals

class Order {
    private CustomerID custId;
    private List<OrderLine> orderitems;    ①  
    public List<OrderLine> orderItems() {    ①  
        return new Collections.unmodifiableList(orderitems);
    }
}

List<OrderItem> items = order.orderItems();    ①  
items.add(new OrderItem(SHIPPING_VOUCHER, 1));    ②  

A word of caution: even if the list is unmodifiable from the outside, that’s not the same as immutable. It’s still possible to change the underlying list orderitems from inside the Order object; for example, by adding a new item. It’s a way of locking out changes made by clients, not a way to make the list immutable. Either way, by copying the list or returning an unmodifiable proxy, you’ve now secured the integrity of the order item list. It can’t be modified from the outside, either by mistake or by malicious manipulations, which was the original intent of making the data field private.

What you’ve now secured is the content of the list, which consists of references to objects. You’ve also ensured that those references can’t be removed and no new references can be added to the list. The next step is to ensure that the objects in the list can’t be changed themselves. The best way to do this is to make the items in the list immutable, as described earlier in this chapter.

This chapter has been devoted to entities as a secure way of representing mutable states. In particular, we’ve dived into the important aspects of ensuring that entities are created in a state that’s consistent with business constraints, and that entities uphold their integrity and stay consistent. In this chapter, you’ve learned about the patterns listed in table 6.1.

Table 6.1 What pattern serves which purpose and addresses which problem?
PatternPurposeSecurity concern
Creation through a constructor with all mandatory attributes; optional attributes set via method callsEntities fulfill simple business rules from the start.Integrity
Creation through fluent interfaceSimplified client code for creating entities with simple business rulesIntegrity
Creation through builder patternConsistent creation for advanced constraintsIntegrity
Public fields only for final attributes that can’t changeEncapsulate behavior, not data as suchIntegrity
Restrictions on getter/setter methodsWithout restrictions, there’s no encapsulation.Integrity
Securing collections through immutabilityAvoid opening collection data for access or modification by mistake.Integrity, confidentiality

In the next chapter, we’re going to consider another aspect of entities: what to do when the number or complexity of states grows unmanageable. We’ll look at what designs you have at your disposal to keep the entities secure even in these conditions.

Summary

  • Entities are the preferred way to handle mutable states.
  • Entities must be consistent on creation.
  • No-arg constructors are dangerous.
  • The builder pattern can be used to construct entities with complex constraints.
  • You need to protect the integrity of attributes when they’re accessed.
  • A private data field with unrestricted getter and setter methods isn’t more secure than a public data field.
  • You should avoid sharing mutable objects and use immutable domain primitives instead.
  • You shouldn’t expose a collection, but rather expose a useful property of the collection.
  • Collections can be protected by exposing an unmodifiable version.
  • You must take care so that the data in a collection can’t be changed from the outside.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.12.205