secure by design - domain primitives
Abstract
- Domain primitives are the smallest building blocks and form the basis of your domain model.
- You should never represent a concept in your domain model as a language primitive or a generic type.
- If a term in your domain already exists outside of your domain but with a slightly different meaning, you should introduce a new term instead of redefining the existing one.
- A domain primitive is immutable and can only exist if it’s valid in the current domain.
- When domain primitives are used, the rest of the code is greatly simplified and becomes more secure.
- You should harden APIs by using your domain primitive library.
- A read-once object is a useful way to represent sensitive data in your code.
- The value of a read-once object can only be retrieved once.
- The read-once object design pattern can mitigate leakage of sensitive data.
- Domain primitives provide the same type of security that concurrent taint analysis would.
Domain primitives are the smallest building blocks and form the basis of your domain model.
- domain primitives and invariants: security issues caused by inexact, error-prone and ambiguous code
- read-once objects: security problems due to leakage of sensitive data
- standing on the shoulders of domain primitives: security issues caused by code burdened by too much complexity
Domain primitives, like value objects, are defined by their value rather than by an identity. Domain primitives lower the cognitive load on developers because there’s no need to understand their inner workings in order to use them. You can safely use them with the confidence that they always represent valid values and well-defined concepts. If they aren’t valid, the won’t exist.
- The invariants of domain primitives are checked at the time of creation.
- Domain primitives can only exist if they are valid.
- Domain primitives should always be used instead of language primitives or generic types.
- Their meaning is defined within the boundaries of the current domain, even if the same term exists outside of the current domain.
- You should use your domain primitive library to create secure code.
Domain primitives vs value objects
oop - Domain Objects and Value Objects - are they equal?
The Value Object is simple enough to be reusable across different domains. The Domain Objects model your actual domain and are typically written to model your specific business or domain, including your business logic.
Avoid exposing your domain publicly
If you expose your internal domain in the public API, then you can’t evolve your domain without forcing the software clients to evolve with you. Hence, you have no other option but to evolve at the same pace as your consumers are able to adapt their clients.
⇒ use a different representation of each of your domain objects, e.g. a DTO
Read-once objects
Key aspects of a read-once object:
- Its main purpose is to facilitate detection of unintentional use.
- It represents a sensitive value or concept.
- It’s often a domain primitive.
- Its value can be read once, and once only.
- It prevents serialization of sensitive data.
- It prevents subclassing and extension.
To enforce such read-once object in Java, we can use:
- enforce invariants at creation
- declare class as
final
to prevent subclassing - wrap value in an
AtomicReference
, so when the accessor methodvalue
is called, it sets the sensitive value tonull
- implements
java.io.Externalizable
interface and always throws an exception in order to prevent accidental serialization
We as humans have a tendency to miss subtle details such as validation when looking at lots of code, and, at the same time, missing validation is something that can cause severe security problems. There’s clearly a need to declutter our entities.
Tip
Use domain primitives for method arguments, constructor arguments, return values, and data fields in entities.
The main benefits of using domain primitives in entity code include the following:
- Input is always validated. The type system ensures you use domain primitives.
- Validation is consistent. It’s always done by the domain primitive constructor.
- Entity code is less cluttered and more to the point. It doesn’t need to do boundary checks, format controls, and so on.
- Entity code is more readable. It speaks the language of the domain.
Taint analysis
In the field of security research, taint analysis investigates how to stop malicious attack data from being used by marking input as tainted. Every input is considered suspicious until it has been cleared of suspicion, which is done when the data is checked through some mechanism.
Most taint analysis tools follow the same framework for terminology:
- Taint sources: The places where dirty input might come into the system, which can be user interfaces, import jobs, or integrations with external systems
- Untainting: The way data is cleaned of suspicion through some type of check
- Propagation policy: What determines whether the result is marked tainted or not when data is processed or combined
- Taint sinks: The places where data is used in a sensitive way: rendered to the user, written to the database, or similar
Note
Running an application with taint analysis instrumentation is currently undergoing research, not something we advise you to do in production—the performance penalty is way too high.
Domain primitives provide the same type of security that concurrent taint analysis would.