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:
- 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
- ensure the persistence framework is secured from exposing inconsistent objets
- 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:
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 useclone
, but create anew ArrayList(orderItems)
, orCollections.unmodifiableList(orderItems)
- make the items in the list immutable
- no setter method like