Traditional security is a gate at the end: build the application, then have security review it. This creates bottlenecks, adversarial relationships, and vulnerabilities that are expensive to fix late. DevSecOps integrates security throughout the development lifecycle.
Here’s how to make security part of how you build software.
The Shift Left
From Gate to Continuous
Traditional:
Dev ──► Build ──► Test ──► Security Review ──► Deploy
│
(bottleneck, late findings)
DevSecOps:
Dev ──► Build ──► Test ──► Deploy
│ │ │ │
▼ ▼ ▼ ▼
Secure Secure Secure Secure
Code Build Test Deploy
Why It Matters
cost_of_finding_bugs:
design_phase: $1
development: $10
testing: $100
production: $1000+
security_is_the_same:
- Find early → fix easily
- Find late → expensive, risky
- Find in production → breach potential
Security in Development
IDE Integration
# Developer sees issues immediately
ide_security:
tools:
- SonarLint (code quality + security)
- Snyk (dependency vulnerabilities)
- GitLens (commit signing)
catches:
- SQL injection patterns
- XSS vulnerabilities
- Hardcoded secrets
- Insecure dependencies
Pre-Commit Hooks
# .pre-commit-config.yaml
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
hooks:
- id: detect-private-key
- id: detect-aws-credentials
- repo: https://github.com/zricethezav/gitleaks
hooks:
- id: gitleaks
- repo: https://github.com/hadolint/hadolint
hooks:
- id: hadolint-docker
Secure Coding Standards
training:
- OWASP Top 10 awareness
- Secure coding practices
- Language-specific security
review_checklist:
authentication:
- [ ] No hardcoded credentials
- [ ] Proper password hashing
- [ ] Session management secure
authorization:
- [ ] Access controls verified
- [ ] No direct object references
- [ ] Least privilege applied
input_handling:
- [ ] All input validated
- [ ] Output encoded
- [ ] SQL parameterized
CI/CD Security
Automated Scanning
# GitHub Actions security pipeline
name: Security Scan
on: [push, pull_request]
jobs:
secrets:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- uses: gitleaks/gitleaks-action@v2
sast:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Run Semgrep
uses: returntocorp/semgrep-action@v1
with:
config: p/security-audit
dependencies:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Snyk Dependencies
uses: snyk/actions/node@master
with:
command: test
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
container:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build image
run: docker build -t myapp:${{ github.sha }} .
- name: Scan image
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:${{ github.sha }}
severity: HIGH,CRITICAL
exit-code: 1
Security Gates
pipeline_gates:
pr_merge:
required:
- No critical vulnerabilities in dependencies
- No high-severity SAST findings
- Secrets scan passed
blocking: true
deploy_staging:
required:
- All PR gates passed
- Container scan passed
- No new critical issues
deploy_production:
required:
- All staging gates passed
- DAST scan completed
- Security review for sensitive changes
Dependency Management
# Automated dependency updates
# Dependabot or Renovate
updates:
- package-ecosystem: npm
schedule:
interval: daily
open-pull-requests-limit: 10
security-updates-only: true
# Vulnerability policy
vulnerability_policy:
critical: Block deployment, fix immediately
high: Fix within 7 days
medium: Fix within 30 days
low: Fix when convenient
Infrastructure Security
Infrastructure as Code Scanning
# Checkov for Terraform
steps:
- name: Checkov
uses: bridgecrewio/checkov-action@master
with:
directory: terraform/
framework: terraform
soft_fail: false
# Example findings:
# - S3 bucket without encryption
# - Security group open to 0.0.0.0/0
# - IAM policy too permissive
Policy as Code
# OPA/Rego policy
package terraform
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_s3_bucket"
not resource.change.after.server_side_encryption_configuration
msg := sprintf("S3 bucket %s must have encryption", [resource.address])
}
deny[msg] {
resource := input.resource_changes[_]
resource.type == "aws_security_group_rule"
resource.change.after.cidr_blocks[_] == "0.0.0.0/0"
resource.change.after.from_port == 22
msg := "SSH must not be open to the world"
}
Container Security
# Secure Dockerfile practices
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
FROM node:18-alpine
# Non-root user
RUN addgroup -g 1001 appgroup && \
adduser -u 1001 -G appgroup -s /bin/sh -D appuser
USER appuser
WORKDIR /app
COPY --from=builder /app/node_modules ./node_modules
COPY . .
# Read-only filesystem where possible
# No unnecessary tools
EXPOSE 3000
CMD ["node", "server.js"]
Runtime Security
Kubernetes Security
# Pod Security Standards
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
containers:
- name: app
image: myapp:v1
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
resources:
limits:
memory: "128Mi"
cpu: "500m"
Runtime Monitoring
# Falco rules for suspicious activity
- rule: Unexpected Outbound Connection
condition: >
outbound and
container and
not fd.sip in (allowed_outbound)
output: >
Unexpected outbound connection
(command=%proc.cmdline connection=%fd.name container=%container.name)
priority: WARNING
- rule: Write to Binary Directory
condition: >
open_write and
container and
fd.directory in (/bin, /sbin, /usr/bin)
output: >
Write to binary directory
(user=%user.name command=%proc.cmdline file=%fd.name)
priority: CRITICAL
Security Culture
Shared Responsibility
roles:
developers:
- Write secure code
- Fix vulnerabilities in their code
- Understand security basics
security_team:
- Set policies and standards
- Provide tools and training
- Review high-risk changes
- Respond to incidents
platform_team:
- Secure infrastructure
- Provide secure defaults
- Maintain security tooling
Metrics and Visibility
metrics:
process:
- Mean time to remediation (MTTR)
- Vulnerability aging
- Security coverage (% of repos scanned)
findings:
- Critical vulnerabilities open
- Trend over time
- By team/application
compliance:
- Policy compliance rate
- Audit findings
- Training completion
Continuous Improvement
improvement_loop:
incidents:
- Post-mortem for security incidents
- Identify systemic issues
- Improve detection/prevention
feedback:
- Developer pain points
- False positive rates
- Tool effectiveness
training:
- Regular security training
- Share lessons learned
- Security champions program
Key Takeaways
- DevSecOps integrates security throughout the development lifecycle
- Shift left: find security issues early when they’re cheap to fix
- IDE integration catches issues before commit
- Pre-commit hooks prevent secrets and obvious issues from entering repos
- CI/CD pipelines automate SAST, dependency scanning, and container scanning
- Security gates can block deployment for critical issues
- Infrastructure as code and policy as code enforce consistent security
- Runtime monitoring catches issues static analysis misses
- Security is shared responsibility: developers, security, and platform
- Measure and improve: MTTR, vulnerability trends, coverage
Security isn’t a phase; it’s a practice. DevSecOps makes security part of how you build, not something you do after.