DevSecOps Best Practices That Survive Contact With a Deadline
Security practices fail in delivery pipelines for one reason: they cost more time than the deadline allows. The practices that survive are the ones that keep working with no ongoing developer effort and that produce their own audit evidence. In order of payoff: give pipelines short-lived identity instead of static keys, make protected branches and required review your change control, move secrets into a vault, govern dependencies with lockfiles and a scanner, tune container and infrastructure scanning until a failed build means a real problem, and keep required gates fast enough that nobody routes around them. Build those six well and your SOC 2 and ISO 27001 evidence falls out of configuration you already maintain, rather than becoming a separate project every audit cycle.
Pipeline identity: short-lived OIDC beats static keys
Start here because it retires the most damage for the least ongoing effort. Static cloud credentials stored in CI variables are the standard compromise path: they leak into logs, they travel with forks, they outlive the person who created them, and they usually carry more privilege than any single job needs. OIDC federation removes the stored credential entirely. GitHub Actions and GitLab CI can both mint a short-lived signed token for each job, and your cloud provider trusts that token under conditions you define, such as repository, branch, and environment, then issues temporary credentials scoped to one role. The credential expires in minutes, so there is nothing to rotate and nothing worth stealing at rest.
Runner discipline rides along with identity. Use ephemeral runners that are destroyed after each job, keep the runners that deploy to production separate from the ones that execute untrusted pull request code, and give each pipeline the narrowest role that completes its work. The audit evidence writes itself: the trust policies and role definitions live in Terraform, and the cloud audit log records which pipeline assumed which role and when.
Protected branches are your change management control
Branch protection on the default branch is the control auditors lean on hardest, because it enforces change management at the only chokepoint every change passes through. The configuration that matters is short: pull requests required, at least one approval from someone other than the author, required status checks, force pushes and deletions blocked, and administrators included in enforcement. A CODEOWNERS file routes changes to sensitive paths, meaning infrastructure code, authentication code, and the CI configuration itself, to reviewers who understand them.
SOC 2 criterion CC8.1 asks whether changes are authorized, tested, and approved before deployment, and ISO 27001:2022 control 8.32 asks the same in different words. A merged pull request with a named reviewer and passing checks answers both, and the branch protection settings export proves the rule was enforced rather than followed by habit. The discipline to add sits around exceptions: bypass rights held by two named people at most, every use logged with a reason, and the log reviewed. An auditor who finds an undocumented bypass writes a finding, while one who finds a logged, expiring exception writes a sentence.
Move secrets to a vault
CI variable stores are shared by every job in scope, weakly audited, and exposed to anyone who can modify a workflow file. A dedicated secrets manager, whether HashiCorp Vault or the native AWS, Azure, or Google Cloud offerings, gives each secret an access policy, an access log, and a rotation schedule, and in the strongest pattern it issues dynamic credentials generated for the job and expired after it. Pipelines authenticate to the vault with the same OIDC identity from the first section, so a job receives exactly the secrets its role allows and every read is logged per run.
Close the other door at the same time. Secret scanning with push protection blocks credentials from entering the repository, and a gitleaks-class scanner in the pipeline catches what slips past. Treat every blocked push as a save rather than an annoyance, and treat every secret that lands in history as compromised, because git history persists and so do forks. Rehearse rotation before you need it, since a credential you cannot rotate inside a day is a standing incident.
Dependencies and the SBOM
Most of a modern codebase is other people's code, so most vulnerabilities arrive by dependency rather than by your own commits. Two mechanics cover the bulk of the risk. Commit and enforce lockfiles so builds are reproducible and a dependency cannot change underneath you silently. Then run a scanner in the Dependabot and Snyk class that watches manifests, raises fix pull requests, and understands which vulnerabilities are reachable in your dependency graph.
Tune the blocking rule narrowly. Fail the build for a known-exploited vulnerability, or for a critical with an available fix in a direct dependency, and route everything else into your vulnerability management queue with remediation SLAs. Failing builds over transitive advisories with no available patch trains engineers to ignore the gate. The intake, prioritization, and SLA design for that queue is its own discipline, covered in how to build a vulnerability management program.
Generate a software bill of materials at build time, in CycloneDX or SPDX format, and store one per release. The SBOM converts the next log4j-style event from a two-week archaeology project into a database query, and it is increasingly a customer procurement ask, so producing it from the pipeline costs a line of configuration now and answers a questionnaire later.
Container and infrastructure scanning, tuned to fail on what matters
Image scanners in the Trivy and Grype class and infrastructure-as-code scanners in the Checkov and tfsec class ship with hundreds of rules enabled, and running them at full volume is how security gates die. The initial blocking set should be small and unarguable: a storage bucket open to the public, a security group open to the internet on management ports, encryption disabled, a plaintext secret in a Terraform variable, or a critical vulnerability with a fix available in the base image. Everything else warns and gets reviewed on a cadence. A gate that fails builds on findings engineering cannot act on gets bypassed within a sprint, and the bypass takes the unarguable failures down with it.
Base image strategy prevents more findings than scanning reports. Build from minimal images, pin them by digest, and rebuild on a weekly schedule so patches arrive by routine rather than by exception. Teams that rebuild weekly spend their attention on a handful of new findings, while teams that pin a base image for a year meet four hundred findings in the week a customer audit forces the upgrade.
Gates that block without stalling delivery
When security policy and a ship date conflict directly, the ship date wins, so the design goal is to remove the conflict. Keep the required merge checks to what runs in minutes: secret scan, lint, unit tests, the dependency check against the narrow blocking set, and the IaC scan. Move the heavy work, meaning full dynamic scanning, fuzzing, and deep image analysis, to scheduled runs with SLAs on the findings instead of a place in the merge path. A ten-minute wall between a developer and a merge gets respected. A ninety-minute wall gets engineered around, and the workaround becomes permanent infrastructure.
Design the exception path before anyone needs it, as a time-boxed waiver with a named owner, a stated reason, and an expiry date, recorded where reviewers and auditors can see it. Exceptions with owners and expiry dates are a functioning control. Silent bypasses are a finding, and they compound, because each one normalizes the next.
Design every control to emit evidence
Everything above produces its evidence as configuration and logs, which is the property that makes the program survivable. Map it once and the audit becomes an export exercise:
- Change management: branch protection settings and pull request review history map to SOC 2 CC8.1 and ISO 27001:2022 control 8.32.
- Access control: OIDC trust policies, role definitions, and cloud audit logs map to SOC 2 CC6.1 through CC6.3 and ISO control 5.15.
- Vulnerability management: scanner output, the blocking ruleset, and remediation SLA reports map to SOC 2 CC7.1 and ISO control 8.8.
- Authentication information: vault access policies and per-run secret access logs map to SOC 2 CC6.1 and ISO control 5.17.
When the examiner asks how changes reach production, the answer becomes a settings export and a sample of merged pull requests rather than a week of screenshot collection. That is the difference between an audit that costs your team a quarter and one that costs an afternoon.
On our own position: BD Emerson performs SOC 2 examinations directly through its CPA attest arm, so the evidence guidance here describes what an examiner samples, written by a firm that does the sampling. For ISO 27001 we implement and run internal audits, and certification is issued by an accredited registrar, which keeps the certificate independent of the people who prepared you. Independence runs in both directions, and we do not examine environments we built.
Validate the controls, then keep them
A control set tuned on paper still needs contact with an adversary. A penetration test scoped to include the pipeline itself answers the questions that matter: whether a tester with developer-level access can reach production credentials, whether branch protection can be pushed around, and whether a poisoned dependency would have been caught before it shipped. We run those engagements through our penetration testing practice, and we design and build the pipeline controls themselves through our DevSecOps consulting practice. If your next audit is already on the calendar and your pipeline evidence is still assembled by hand, start with the branch protection and pipeline identity work. It takes about two weeks of effort and it carries the most weight with the people who will examine you.
