secure by design - ensuring integrity of state

Abstract

  • 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.

Managing state using entities

Entities are the preferred way to implement mutable state. During modeling, you decide what concepts are most important to model.

Implement mutable state using a class that keeps the data with the associated behavior as methods in the same class.

Consistent on creation

Avoid no-arg constructors

There’s nothing in the code that enforces any convention for creation, and such convention are often forgotten or broken in a way that leads to inconsistent entities.

ORM frameworks may require to have no-arg constructors. We can avoid security loophols:

  1. conceptually separate from the persistence model, i.e. persistence models reside in the infrastructure code to emphasize that they are different contexts and to make the mapping explicit
  2. ensure the persistence framework is secured from exposing inconsistent objets
  3. neither JPA nor Hibernate needs a public constructor, they can do well with a private no-arg constructor, furthermore they also don’t need setter methods as they are using reflection

All mandatory fields as constructor arguments

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.

Construction with a fluent interface

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.

Builder pattern for upholding advanced constraints

The basic idea of the builder pattern is to hide the complexity of constructing an entity within another object, the builder.

ORM frameworks and advanced constraints

You’ll need to ensure that the invariants hold after you’ve loaded the data from the database, e.g. using the @PostLoad annotation:

@PostLoad
private void checkInvariants() {
  // ^ is the Java syntax for XOR operator
  Validate.isTrue(fallbackAccount != null ^ creditLimit != null)
}

Integrity of entities

The basic trick of ensuring the integrity of entities is to never leave anything mutable accessible to the outside.

  • no need to always have getter / setter methods on POJO, replace them with method for specific situations
  • avoid sharing mutable objects
  • if only option is to work with a mutable object, i.e. when your methods return a reference to an encapsulated object, first clone the object
  • securing the integrity of collections
    • no setter method like void setOrderItems(List<OrderItem> orderItems)
    • to add new item to the list, create a method void addOrderItem(OrderItem orderItem)
    • to secure the design for List<OrderItem> orderItems(), ensure the copy that’s returned can’t be used to make changes to your internal list: do not use clone, but create a new ArrayList(orderItems), or Collections.unmodifiableList(orderItems)
    • make the items in the list immutable