Skip to main content

Command Palette

Search for a command to run...

Shift Left: How to Make Security Every Engineer's Job Without Making It Nobody's Job

Updated
14 min read
Shift Left: How to Make Security Every Engineer's Job Without Making It Nobody's Job

Shift Left: How to Make Security Every Engineer's Job Without Making It Nobody's Job

Series: The Modern SDLC · Post 8 of 17 Post 7: CI Pipeline · Post 9: Infrastructure as Code →


Security teams have a problem that no other team in engineering has. Their job is to prevent things that haven't happened yet, and the cost of failure lands somewhere else — on the business, on the customers, on the engineers who shipped the vulnerable code. When security works, nothing happens and nobody notices. When it fails, everyone notices at once.

The traditional response to this problem was a gate at the end of the development process: build the thing, then hand it to security to review before launch. This model has one fatal flaw. By the time security reviews a completed system, fixing what they find is expensive. The architecture is set. The data model is fixed. The third-party dependencies are already in production. Changing them requires rework measured in weeks, not hours.

Shifting left means moving security earlier — into design, into code review, into the CI pipeline, into the decisions engineers make every day. Not as an additional burden on engineers, but as a change to where security expertise gets applied. Caught in the editor, a security issue costs minutes. Caught in a post-breach investigation, it costs months and potentially everything.

This post covers how to build that shift into your actual development process.


The one thing to remember

A vulnerability found by a developer in their editor costs almost nothing to fix. The same vulnerability found in a production breach costs potentially everything. Shift left is not a philosophy — it's a financial decision.


The cost curve that makes the argument

The economic case for shifting security left is unambiguous. IBM's Cost of a Data Breach report consistently puts the average cost of a production breach above four million dollars. Security tooling that catches vulnerabilities in development costs a few hundred dollars per month and a week of setup.

The curve is steep: issues caught in design cost hours of remediation. Issues caught in code review cost hours to days. Issues caught in CI cost days. Issues caught in a pre-launch pen test cost weeks — architectural changes, new dependencies, retesting. Issues caught in production after a breach cost months, plus legal exposure, plus customer notification, plus regulatory scrutiny.

This is the argument to make to leadership when investing in DevSecOps. Not "security is important" — everyone agrees with that. "Finding vulnerabilities before production is dramatically cheaper than finding them after." That framing gets budget approved.


Threat modelling: find problems before writing code

Threat modelling is a structured conversation about what can go wrong, conducted at design time before a line of code is written. It's the highest-leverage security activity available and the most consistently skipped.

The STRIDE framework is the standard starting point. For each component in your system, work through six threat categories:

Spoofing — can an attacker impersonate a legitimate user or service? Mitigations: strong authentication, mutual TLS between services, signed tokens with short expiry.

Tampering — can data be modified in transit or at rest without detection? Mitigations: integrity checks, digital signatures, TLS everywhere, write-once audit logs.

Repudiation — can an actor deny performing an action after the fact? Mitigations: immutable audit trails, signed requests, event sourcing for sensitive operations.

Information disclosure — can sensitive data leak to unauthorised parties? Mitigations: encryption at rest and in transit, least privilege access, scrubbed error messages that don't expose internal state.

Denial of service — can the system be made unavailable? Mitigations: rate limiting, circuit breakers, auto-scaling, CDN for static assets, graceful degradation.

Elevation of privilege — can a user gain more access than they're authorised for? Mitigations: RBAC enforced at the middleware layer, input validation, no direct object references without ownership verification.

The process: draw a data flow diagram of the system, identify trust boundaries (points where data crosses a privilege boundary), apply STRIDE to each component, assign mitigations, document findings in an ADR. A well-run threat modelling session takes three to four hours and surfaces more meaningful security concerns than any automated scanner, because scanners find known patterns in code while threat modelling finds design-level flaws.

Tools: OWASP Threat Dragon (free, diagramming-based), Microsoft Threat Modeling Tool (free, more structured).


Secrets management: the constraint that must not flex

A secret committed to a git repository is compromised. Not "might be compromised if someone looks." Compromised. Git history is permanent, repositories get cloned, CI systems cache content, and automated scanners harvest credentials from public repositories continuously. Even in a private repository, the blast radius of a credentials leak is unpredictable.

The rule is absolute: secrets never touch source code. Not in commented-out lines. Not in example files. Not in test fixtures. Not in CI configuration checked into the repository. Never.

Before the commit: pre-commit detection. Gitleaks or detect-secrets as a git hook scans staged files for credential patterns before the commit is made. This is the first line of defence and the cheapest. Run it on every machine, automatically, as part of the repository setup.

In CI: scanning as a backstop. The same tools run in the CI pipeline catch anything that bypassed the pre-commit hook — credentials committed with --no-verify, or on a machine where the hook wasn't installed. This stage should fail the pipeline immediately and alert the security team.

At runtime: vault-based secret delivery. Application secrets are fetched at runtime from a secrets manager — HashiCorp Vault, AWS Secrets Manager, GCP Secret Manager, Azure Key Vault — never baked into images, environment files on disk, or Kubernetes ConfigMaps. The application authenticates to the secrets manager using its identity (an IAM role, a service account, a Vault AppRole) and retrieves the credentials it needs.

Secret rotation as a practice, not an emergency response. Database passwords, API keys, and certificates should all have defined TTLs and automated rotation. HashiCorp Vault's dynamic secrets — credentials generated on demand that expire automatically — mean that even if an attacker captures a credential, it expires before they can do significant damage with it.

If a secret is committed: rotate it immediately, before removing it from history. Assume it was already read. Removal from git history is secondary to rotation — treat the credential as fully compromised from the moment of the commit.


The CI security pipeline: shift left in practice

Security checks in CI are only valuable if they're configured to actually block on real findings. A scanner that runs and produces a report nobody reads is security theatre — worse than nothing, because it creates the impression of a security practice without the substance.

SAST (Static Application Security Testing) analyses source code for vulnerability patterns. Semgrep is the most practical option for most teams: fast, highly configurable, and supports custom rules. CodeQL provides deeper analysis for GitHub repositories and finds more subtle vulnerabilities. Bandit for Python. ESLint security plugins for JavaScript.

Configuration matters as much as tool choice. Block the pipeline on high and critical findings. Alert but don't block on medium findings. Suppress or ignore low findings entirely. Alert fatigue — where engineers see so many findings that they stop reading them — destroys the value of scanning faster than almost anything else.

Dependency CVE scanning flags known vulnerabilities in your dependencies. Every dependency your project uses has a known CVE history. Snyk integrates into CI and IDE simultaneously. OWASP Dependency-Check is the open-source alternative. npm audit and pip audit are the built-in options for their respective ecosystems.

The response policy: block on critical CVEs with available fixes. Report medium and low without blocking. Review high-severity findings without an available fix — sometimes the vulnerability doesn't affect your usage pattern, sometimes you need a workaround, sometimes you accept the risk with documentation. What you shouldn't do is ignore them silently.

IaC scanning on every infrastructure change. Checkov scans Terraform, CloudFormation, Kubernetes manifests, and Dockerfiles for misconfigurations against 1000+ built-in security rules. Findings like "this S3 bucket has public read access," "this security group allows all inbound traffic," and "this EC2 instance has no encryption" are caught in the PR review before the misconfiguration exists in a real environment.

Container image scanning with Trivy or Grype after the image is built and before it's pushed. OS-level CVEs in the base image, vulnerabilities in system packages, secrets accidentally included in the image filesystem — all caught before the image reaches the registry.


Supply chain security: you're responsible for your dependencies

The SolarWinds attack in 2020 and the Log4Shell vulnerability in 2021 changed how the industry thinks about software supply chain risk. You are responsible for every line of code in your final artefact — including code you didn't write.

Dependency pinning is the foundation. Lockfiles (package-lock.json, poetry.lock, go.sum) pin exact versions. Ranges in package manifests (^1.2.0) allow automatic updates to any compatible version, which means a malicious maintainer can push a patch release that installs automatically on your next build. Pin. Review. Deliberately update.

SBOM (Software Bill of Materials) — generated by Syft or CycloneDX — is a machine-readable manifest of every component in your software. When a new CVE is published, an SBOM lets you answer "are we affected?" in seconds rather than days. Increasingly required by enterprise customers, and required by US federal regulations under the 2021 Executive Order on Cybersecurity.

Artefact signing with Cosign and Sigstore provides cryptographic proof that a container image or binary was produced by your CI pipeline and hasn't been tampered with in the registry. Keyless OIDC signing means no key management overhead. Consumers can verify the signature with a single command before deployment.

SLSA (Supply chain Levels for Software Artefacts) is a framework from Google that defines maturity levels for supply chain security: L1 (documented build process), L2 (CI-produced artefacts), L3 (tamper-resistant, auditable build process). Aim for L2 at minimum — it means your artefacts were produced by a CI system, not built on a developer's laptop and pushed manually.

Private package registries as a defence against dependency confusion attacks. If your organisation uses internal packages, a malicious public package with the same name but a higher version number can be installed automatically unless your package manager is configured to prefer the private registry. Artifactory, Nexus, or AWS CodeArtifact act as a proxy and prevent this class of attack.


The OWASP Top 10: design these out, not test for them

The OWASP Top 10 list of critical web application security risks has been largely stable for twenty years. The same categories of vulnerability appear in production systems year after year not because engineers don't know about them but because they're easy to introduce accidentally when you're not thinking about them.

The approach that works is building mitigations into the framework and platform layer so that the secure path is the default path, and developers have to actively work against their tooling to introduce these vulnerabilities.

Broken access control (A01): Enforce authorisation at the middleware layer, not scattered through individual handler functions. Default deny — every route requires explicit authorisation, not opt-in protection. Never trust client-supplied resource IDs without verifying the authenticated user owns them.

Cryptographic failures (A02): TLS 1.2+ everywhere, enforced at the load balancer. Encrypt sensitive data at rest. Use established cryptographic libraries — bcrypt for passwords, libsodium for general crypto. Never implement custom cryptographic algorithms.

Injection (A03): Parameterised queries only. Never build SQL or command strings by interpolating user input. Use an ORM but understand what queries it generates — ORMs don't eliminate injection risk, they reduce it for common patterns. Validate and sanitise all input at the boundary.

Insecure design (A04): This is the category that threat modelling addresses. Security requirements belong in user stories. "The API must not expose user data to unauthorised callers" is an acceptance criterion, not an afterthought.

Security misconfiguration (A05): IaC scanning catches misconfiguration before deployment. CSPM (Cloud Security Posture Management) tools — Wiz, Orca, AWS Security Hub — monitor for drift in production. Disable default credentials on every service. Remove default accounts. Minimise attack surface by disabling unused features.

Vulnerable components (A06): Renovate Bot for automated dependency updates. CVE scanning in CI. SBOM for rapid impact assessment. The answer to "how quickly can we patch a critical CVE?" should be measured in hours, not weeks.

Authentication failures (A07): Use a proven identity provider — Auth0, AWS Cognito, Keycloak — rather than building authentication from scratch. MFA for all privileged accounts. Short-lived JWTs. Secure session management with appropriate expiry and invalidation.

Software and data integrity failures (A08): Verify checksums on downloaded artefacts. Sign and verify CI outputs. Never deserialise untrusted data without schema validation.

Security logging and monitoring failures (A09): Log all authentication events, access control failures, and input validation errors. Alert on anomalies. Retain logs with tamper evidence. Define retention policies that satisfy compliance requirements.

SSRF (A10): Validate and allowlist URLs before making server-side requests. Deny requests to internal IP ranges, cloud metadata endpoints, and localhost. Use a dedicated egress proxy for outbound service calls.


Security culture: the human layer

No scanner catches a developer who builds an insecure access control model because they didn't understand the requirements. No tool prevents a team from choosing a third-party library with a poor security track record because nobody evaluated it. Tools catch known patterns. Culture prevents unknown ones.

Security champions embedded in product teams — one engineer per team who has deeper security knowledge, acts as a resource for their colleagues, reviews security-sensitive changes, and serves as a bridge to the central security team. Not a gatekeeper. An enabler.

Security in the Definition of Done. "No new high or critical CVEs introduced" and "authentication and authorisation reviewed" as explicit checklist items. This makes security a shared responsibility, verified on every piece of work, rather than a specialist review that happens occasionally.

Blameless security retrospectives. When a vulnerability is found — in any environment — run a blameless analysis. Focus on which process failed to catch it, not who wrote the code. The output should be a process improvement, not a performance conversation.

The goal of DevSecOps is not to make security the security team's job. It's to make security everyone's job, with the security team as enablers and advisors rather than gatekeepers and auditors. The security team that's involved at design time, that builds the scanning tools into CI, that writes the policy-as-code for IaC — that team makes every engineer more capable of building securely. The security team that reviews completed designs and blocks launches makes everyone less capable and slower.


What goes wrong when security is an afterthought

The compliance scramble. A security review scheduled before launch finds architectural issues that require significant rework. The launch date moves. The rework is rushed. Technical debt is introduced to meet the revised deadline. The product ships with known-but-accepted risks that nobody has time to address afterward.

The dependency blindspot. A critical CVE is published in a library your product uses. Nobody knows. The first you hear about it is from a customer or a security researcher. With an SBOM and CVE scanning in CI, you'd have known within hours of the CVE publication.

The secret that was committed once. A developer committed a database password three years ago in a commit message. It was rotated at the time but the history was never cleaned. A security researcher finds it in the public repository, tests it against the current database, and it works — because the rotation was incomplete. This exact incident has happened to large companies with mature security teams.

The scanner nobody configured. A SAST tool runs in CI and produces findings on every build. The findings are suppressed because the team didn't have time to review them when they were first introduced. Now there are two hundred open findings and nobody knows which ones are real. The scanner runs, the builds pass, and nobody is safer.


If you do one thing from this post

Add secrets scanning to your pre-commit hooks today. Install gitleaks or detect-secrets, add it to your pre-commit configuration, and push the updated configuration to the repository.

It takes under an hour. It's the single highest-ROI security investment you can make because it prevents the most expensive class of security incident — credential exposure — before it happens, at the point where it's cheapest to prevent.


Next up: Post 9 — Infrastructure as Code: Treat Your Cloud Like a Codebase

Post 7: How to Build a CI Pipeline That Engineers Actually Trust

The Modern SDLC

Part 9 of 19

Most engineering content teaches tools in isolation. This series connects them. From conception and architecture through to observability, incident management, and continuous improvement — a practical guide to how modern software is built, delivered, and operated end to end.

Up next

Infrastructure as Code: Treat Your Cloud Like a Codebase

Infrastructure as Code: Treat Your Cloud Like a Codebase Series: The Modern SDLC · Post 9 of 17 ← Post 8: DevSecOps · Post 10: Containers and Kubernetes → There's a class of production incident tha

More from this blog

Cloud Tuned

629 posts

Your starting point for anything cloud: AWS, Azure, GCP, Serverless, Architecture, Hybrid Cloud, Systems Design and other Information Technology topics.