Security
Security by Design in Enterprise Software Development
Threat modeling with STRIDE, OWASP ASVS, least privilege, secrets management, supply-chain hygiene, safe logging and incident readiness for enterprise software.
- By
- Gwavo Technologies Engineering
- Published
- Reading time
- 8 min

Security is rarely lost in a single dramatic decision. It erodes through small ones: an administrative endpoint left without an authorization check, a token committed to a repository, a log line that captures a full card number, a dependency nobody has updated in two years. Security by design is the discipline of making the secure choice the default at each of these moments, in architecture, code, infrastructure and operations, rather than hoping a penetration test at the end will catch whatever slipped through.
This article describes the practices we consider essential for enterprise software, and the established standards that support them.
Why security has to be designed in
Defects are cheapest to fix while they are still ideas. An authorization model that is wrong at the design stage costs a conversation to correct; the same flaw discovered in production can mean an incident, a forensic investigation and a disclosure to customers or regulators. Several of the most serious vulnerability classes, including broken access control, insecure design and security misconfiguration, all of which feature in the OWASP Top 10, are architectural rather than coding errors. No scanner will find them reliably.
Designing security in also changes the economics for the team. When secure patterns are built into templates, libraries and pipelines, developers do not need to be security specialists to produce secure software. They need a well-paved road, and the habit of staying on it.
Threat modeling with STRIDE
Threat modeling is a structured way of asking what can go wrong before it does. It does not require heavy tooling; a whiteboard session with the right people is often enough. The four-question framework popularized by Adam Shostack, and reflected in the Threat Modeling Manifesto, keeps the exercise focused: what are we working on, what can go wrong, what are we going to do about it, and did we do a good enough job?
Start with a data-flow diagram that shows processes, data stores, external entities and trust boundaries, the points where data crosses from one level of trust to another, such as from a mobile app to an API or from a service to a payment provider. Then apply STRIDE to each element:
| Threat | Property at risk | Question to ask |
|---|---|---|
| Spoofing | Authentication | Can a caller pretend to be another user or service? |
| Tampering | Integrity | Can data be altered in transit or in storage without detection? |
| Repudiation | Non-repudiation | Can someone deny an action because no trustworthy record exists? |
| Information disclosure | Confidentiality | Can data leak through responses, errors, logs or backups? |
| Denial of service | Availability | Can one client exhaust resources or lock others out? |
| Elevation of privilege | Authorization | Can a user obtain rights they were never granted? |
Record each threat, its mitigation and its owner in the backlog alongside functional work, so that security items are planned and tracked like any other. Revisit the model whenever the architecture changes, with a new integration, a new data store or a new type of user, not just once at the start of a project.
Building security into the lifecycle
A secure software development lifecycle embeds security activities in every phase rather than appending them at the end. The NIST Secure Software Development Framework (SP 800-218) and OWASP SAMM provide useful structures. In practice, the essentials are:
- Requirements. Use the OWASP Application Security Verification Standard (ASVS) to define security requirements. Its three verification levels let a team set an appropriate bar, with the higher levels suited to systems that handle payments, identity or health data.
- Design. Threat model new features and significant changes, and review authentication, authorization and data flows explicitly.
- Implementation. Rely on vetted frameworks for authentication, cryptography and output encoding instead of writing your own. Use parameterized queries, validate input against strict schemas and encode output for its context.
- Verification. Run static analysis, dependency scanning and secret scanning on every pull request, dynamic testing against staging environments, and focused manual review for security-sensitive changes. Commission independent penetration tests before major releases.
- Release and operations. Harden configurations, monitor continuously and feed lessons from incidents back into requirements.
Treat the OWASP Top 10 as an awareness document for training and code review rather than a complete specification. ASVS is the more appropriate basis for defining and verifying controls, as OWASP itself recommends.
Least privilege and secrets management
Least privilege everywhere
Every identity, whether human, service or pipeline, should hold only the permissions it needs, for only as long as it needs them.
- Model authorization explicitly, with roles or attributes defined in one place and checked on every request at the level of individual objects, not merely at the route level.
- Separate administrative functions from customer-facing ones, and require phishing-resistant multi-factor authentication for privileged access.
- Give each service its own identity and narrowly scoped database credentials. A reporting service has no business writing to the payments schema.
- Prefer just-in-time, time-bound elevation for production access over standing administrator rights, and review access regularly.
- Scope CI/CD credentials tightly. A pipeline that can deploy to production is a high-value target in its own right.
Secrets do not belong in code
API keys, database passwords, signing keys and certificates belong in a dedicated secrets manager or key management service, never in source code, container images or unencrypted configuration files.
- Inject secrets at runtime and grant access per workload identity.
- Prefer short-lived, automatically issued credentials, such as workload identity federation or dynamic database credentials, over long-lived static keys.
- Rotate secrets on a schedule and immediately after any suspected exposure, and design applications to reload them without redeployment.
- Enable secret scanning in repositories and pipelines, and treat any committed secret as compromised, even after the commit has been removed.
Supply-chain hygiene
Modern applications are largely assembled from third-party code. Every dependency, build tool and base image is part of your attack surface.
- Know what you ship. Generate a software bill of materials (SBOM) in a standard format such as CycloneDX or SPDX for every build, so that you can tell within minutes whether a newly disclosed vulnerability affects you.
- Pin and verify. Commit lockfiles, pin versions and verify integrity hashes. Reference CI actions and container base images by immutable digest rather than by mutable tag.
- Scan and update continuously. Monitor dependencies for known vulnerabilities and keep them current with automated update tooling. Small, frequent upgrades are far less risky than rare, large ones.
- Choose dependencies deliberately. Before adopting a package, look at its maintenance activity and ownership; the OpenSSF Scorecard project automates many of these checks. Guard against typosquatting and against dependency confusion between internal and public package names.
- Protect the build. Run builds in isolated, ephemeral environments, sign artifacts (Sigstore makes this practical) and use the SLSA framework as a roadmap toward verifiable build provenance.
Protecting data in transit, at rest and in logs
Encryption
- Encrypt all traffic with TLS 1.2 or later, preferring TLS 1.3, including internal service-to-service traffic, and enable HSTS on public web applications.
- Encrypt data at rest at the storage layer, and add application-level or field-level encryption for the most sensitive values, using envelope encryption with keys held in a key management service.
- Hash passwords with a memory-hard algorithm such as Argon2id, as the OWASP Password Storage Cheat Sheet recommends. Never store them with reversible encryption.
- Never implement cryptographic primitives yourself. Use well-maintained libraries through their high-level interfaces.
Logging without leaking
Logs are indispensable for detection and investigation, and they are also a frequent source of data leaks, because they are copied into many tools and retained for long periods.
- Use structured logs with explicit fields, and maintain an allow-list of what may be logged rather than a deny-list of what may not.
- Never log passwords, tokens, session identifiers, full card numbers or secrets. Mask or tokenize personal data such as phone numbers and identity document numbers.
- Redact centrally, in the logging library or pipeline, so that protection does not depend on every developer remembering every rule.
- Do log security-relevant events, such as authentication attempts, permission changes, administrative actions and access to sensitive records, with who, what, when and from where, to support both detection and non-repudiation.
- Restrict access to logs, protect their integrity and set retention periods that meet legal obligations without keeping data longer than necessary.
Central redaction can be as simple as configuring it once in the shared logger, as in this example using the Pino library for Node.js:
import pino from "pino";
export const logger = pino({
redact: {
paths: [
"req.headers.authorization",
"req.headers.cookie",
"*.password",
"*.token",
"payment.cardNumber",
],
censor: "[REDACTED]",
},
});
Incident readiness
No set of controls is perfect, so prepare for the day one of them fails. NIST SP 800-61 offers a widely used reference for incident handling.
- Detect. Centralize logs, define alerts on meaningful signals, such as unusual authentication patterns, privilege changes, spikes in authorization failures or unexpected outbound traffic, and make sure someone is accountable for responding.
- Prepare. Write an incident response plan with clear roles, escalation paths and communication templates, including who decides on notifying customers and regulators, and within which deadlines.
- Rehearse. Run tabletop exercises on realistic scenarios, such as a leaked credential or a compromised dependency. Plans that have never been rehearsed rarely work as written.
- Recover. Keep backups that are encrypted, regularly restored in tests and isolated from production credentials, so that ransomware or a compromised administrator account cannot destroy them.
- Learn. Hold blameless post-incident reviews and turn their findings into backlog items, updated threat models and new automated checks.
A baseline for every project
Security by design does not require a large security department. It requires a baseline applied consistently. For every new enterprise system, we recommend confirming that:
- A threat model exists, is linked to the backlog and is updated with each architectural change.
- Security requirements are derived from an appropriate OWASP ASVS level.
- Authorization is checked at the object level on every request and covered by automated tests.
- Secrets live in a secrets manager, are rotated, and are scanned for on every commit.
- Every build produces an SBOM, and dependencies are pinned, scanned and updated routinely.
- Data is encrypted in transit and at rest, and logs are structured and redacted by default.
- Privileged access requires multi-factor authentication and time-bound elevation.
- An incident response plan exists and has been rehearsed.
None of these practices is novel, and that is precisely the point. Security by design is less about clever defenses than about disciplined, repeatable engineering: the kind that allows an organization to adopt new technology with confidence rather than anxiety.


