Hardening Docker containers Node.js and Next.js – practical OWASP guide

Step by step we will show how to secure Docker containers for Node.js and Next.js applications, eliminating OWASP Top 10 risks.

Hardening Docker kontenery Node.js i Next.js – praktyczny przewodnik OWASP

Modern engineering teams are increasingly deploying Node.js and Next.js applications in Docker containers. While containers simplify code delivery, they bring specific risks that align perfectly with the OWASP Top 10 categories. In this article we present the threat model, operating mechanisms, and practical hardening steps so that your Docker images meet compliance requirements and the principle of least‑privilege.

Threat model – what can go wrong?

The primary attack vector on a container is a misconfigured base image that contains unnecessary packages, default user accounts, or open ports. These kinds of gaps lead to several threat classes described in OWASP Top 10:

  • A01 – Broken Access Control: running a process as root enables escalation by exploiting system library vulnerabilities.
  • A02 – Cryptographic Failures: lack of encryption for environment variables and secret data in the image.
  • A05 – Security Misconfiguration: default Dockerfile settings, unrestricted file‑system permissions.

This model assumes an attacker has access to the image (e.g., via a public registry) or can inject malicious code during the CI/CD phase. Therefore every stage – from image building to running in a Kubernetes cluster – must be under control.

Hardening mechanism – how does it work?

Container hardening is based on three pillars: image minimalism, privilege reduction, and continuous vulnerability monitoring. Minimalism means using lightweight base images (e.g., node:18‑alpine) and removing all unnecessary tools. Privilege reduction is achieved by defining a non‑root user in the Dockerfile and using the --read-only and --cap-drop flags at runtime. Monitoring involves regularly scanning images with tools such as Trivy, Anchore, or Clair.

Example: vulnerable vs. hardened Dockerfile fragment

# --- VULNERABLE DOCKERFILE ---------------------------------------------------
FROM node:18
WORKDIR /app
COPY . .
RUN npm install
EXPOSE 3000
CMD ["node", "server.js"]
# running as root, full file‑system access

In this example the image includes all project dependencies, including development tools, and runs the process as root – a classic case of A05 – Security Misconfiguration.

# --- HARDENED DOCKERFILE ----------------------------------------------------
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
# ---------- final stage ----------
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app .
# create a non‑root user
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
EXPOSE 3000
# run in read‑only mode, limited capabilities
CMD ["node", "server.js"]

The hardened version uses multi‑stage builds, removes dev‑dependencies, defines a dedicated user, and limits privileges. This directly addresses A01, A02, and A05.

Docker hardening checklist for Node.js / Next.js

  • Use lightweight, official base images (alpine, slim).
  • Multi‑stage builds – separate the build layer from runtime.
  • Install only production dependencies (npm ci --only=production).
  • Define a non‑root user and drop capabilities (--cap-drop ALL).
  • Run the container in read‑only mode if the application does not require writes.
  • Configure resource limits (CPU, memory) in the Kubernetes pod spec.
  • Enable AppArmor/SELinux and seccomp profiles.
  • Regularly scan images for vulnerabilities (OWASP Dependency‑Check, Trivy).
  • Use secrets from a manager (Kubernetes Secrets, HashiCorp Vault) – never store them in the image.
  • Audit container logs and monitor application behavior (Falco, OpenTelemetry).

Common mistakes and trade‑offs

In practice teams often skip certain recommendations for convenience. The most frequent pitfalls are:

  • Keeping a full dev image in production – expands the attack surface and leads to A06 (Vulnerable & Outdated Components).
  • Lack of write restrictions – Next.js apps generate temporary files; developers often disable read‑only mode, opening the door to ransomware.
  • Using the default root user – the easiest escalation vector, especially in multi‑tenant environments.

The solution is to introduce automated security tests into the CI/CD pipeline that enforce checklist criteria. Compromises should be documented and approved by the security team.

References to Standards

Our recommendations are based on recognized frameworks:

  • OWASP Top 10 – classification of the most critical web application threats.
  • CWE‑250 – limiting privileges (principle of least privilege).
  • Docker Bench for Security – a set of tests aligned with the CIS Docker Benchmark.
  • Kubernetes Hardening Guide – best practices for pods and clusters.
“Container security is not a one‑time configuration, but a continuous process of verification and improvement.”

Practical Example of CI/CD Implementation

Below is a simple pipeline diagram in GitHub Actions that builds, scans, and publishes an image:

name: Build & Secure Docker Image
on:
  push:
    branches: [ main ]
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v2
      - name: Build image
        run: |
          docker build -t myapp:${{ github.sha }} .
      - name: Scan image with Trivy
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: myapp:${{ github.sha }}
          exit-code: '1'
          ignore-unfixed: true
      - name: Push to registry
        if: success()
        run: |
          echo ${{ secrets.REGISTRY_PASSWORD }} | docker login -u ${{ secrets.REGISTRY_USER }} --password-stdin
          docker push myapp:${{ github.sha }}

The pipeline enforces that every code change must undergo vulnerability scanning before the image is published, reducing the risk of introducing an A06 type flaw.

In summary, hardening Docker containers for Node.js and Next.js applications requires a conscious approach to image minimization, applying least‑privilege principles, and continuous monitoring. Using the checklist above and integrating scanning into CI/CD effectively eliminates OWASP Top 10 risks. If you need assistance implementing these practices in your organization, contact Coderia.it – we’ll help you build a secure and scalable container infrastructure.

Let’s start

Got a project in mind?

Describe it in a few sentences. I reply within 24 hours with a free quote and a proposed stack.