This chapter distills practical security guidance for microservices: define services as small, business‑oriented components with independent runtimes and deployments, and design them to tolerate partial failure. It cautions against creating a distributed monolith and emphasizes three often-overlooked properties—independent runtimes, independent updates, and being designed for down—using patterns like bulkheads, circuit breakers, and event-driven messaging to keep the system resilient.
A core theme is treating each service as a bounded context and exposing only domain operations in public APIs so the service can enforce invariants itself. This promotes clear ownership of concepts, explicit translation across contexts, and safer evolution over time. Guidance includes splitting monoliths along semantic boundaries, validating and translating inputs at service borders with strong domain types, and evolving semantics without breaking context mappings—thereby avoiding ripple effects and subtle security defects.
The chapter also examines cross-cutting data concerns through the CIA-T lens. For confidentiality, propagate caller identity across calls with tokens and assess sensitivity as context-dependent, especially in aggregating services. For integrity and availability, favor authoritative sources, cryptographic checks, and defensive patterns. Achieving traceability requires unique service identity (name, version, instance) and transaction correlation IDs across systems. Finally, it promotes structured, domain-oriented logging performed inside each service, optionally signed to attest integrity, and categorized into separate streams (such as audit, behavior, and error) to prevent leakage of sensitive information and to enable strict access control, especially for long‑lived audit data.
Figure 13.1. Log data is transformed into JSON in an external normalization step.
Figure 13.2. Log data is structured into JSON before being passed to the logging system.
Figure 13.3. The unique trace ID is shared between systems A, B, and C.
Summary
- A good microservice should have an independent runtime, allow independent updates, and be designed for other services being down.
- Treating each microservice as a bounded context helps you design more secure APIs.
- Secure design principles such as domain primitives and context mapping are also applicable when designing microservice APIs.
- In order to avoid common security pitfalls, only expose domain operations in APIs, use explicit context mapping between services, and pay extra attention to evolving APIs.
- It’s important to analyze confidentiality, integrity, availability, and traceability (CIA-T) across all services.
- Identify data that’s sensitive and possibly needs to be secured across services.
- The integrity of log data is important from a security standpoint.
- Normalization and categorization of log data requires extensive domain knowledge and should be part of the service design.
- A service must be uniquely identifiable by its name, version number, and instance ID.
- A transaction must be traceable across systems.
- Using a logger with a domain-oriented API facilitates a design that considers the confidentiality of log data.
- Don’t intermix sensitive and nonsensitive data in the same log because it can lead to accidental information leakage.
Secure by Design ebook for free