Files
Infosec/Barcode
2026-09-13 20:09:20 +01:00
..
2026-09-13 20:09:20 +01:00
2026-09-13 20:09:20 +01:00
2026-09-13 20:09:20 +01:00
2026-09-13 20:09:20 +01:00
2026-09-13 20:09:20 +01:00
2026-09-13 20:09:20 +01:00
2026-09-13 20:09:20 +01:00
2026-09-13 20:09:20 +01:00
2026-09-13 20:09:20 +01:00

Cipher · Barcode Studio

A self-contained, browser-based barcode generator that converts text or numeric sequences into 22 different barcode symbologies and exports them as downloadable JPG (or PNG) images. Designed to run as a single hardened Docker container with no backend, no database, and no telemetry — everything happens in the user's browser.

status docker license


Table of Contents

  1. Features
  2. Supported Barcode Formats
  3. Architecture
  4. Requirements
  5. Quick Start
  6. Configuration
  7. Project Structure
  8. Security
  9. Dependency Versions & Vulnerability Status
  10. Operations
  11. Updating & Maintenance
  12. Troubleshooting
  13. Development
  14. License

Features

  • 22 barcode symbologies spanning retail/GS1, linear/industrial, pharma, postal and 2D matrix codes.
  • Live example preview — every symbology shows a sample render the moment it's selected.
  • Batch generation — paste lines into the input area and each line becomes its own barcode card.
  • Per-card text toggle — show or hide the human-readable caption beneath each barcode, then download it in that state.
  • JPG and PNG export — individual downloads per barcode plus a one-click "Download All as JPG" for the whole batch.
  • Adjustable scale and JPG quality for print-ready output.
  • Fully offline-capable once loaded — every library is vendored locally, no third-party CDN at runtime (web fonts are the only external request and are optional).
  • No backend — runs entirely as static assets served by nginx. No database, no API, no user data leaves the browser.

Supported Barcode Formats

Group Symbology Notes
Retail · GS1 EAN-13, EAN-8, UPC-A, UPC-E, ISBN-13, GS1 DataBar, GS1-128 ISBN-10 auto-converts to ISBN-13
Linear · Industrial Code-128, Code-39, Code-39 Full ASCII, Code-93, Code-11, Code 2of5 Interleaved, MSI Plessey, Flattermarken, Telepen Alpha
Pharma · Specialty Pharmacode One-Track, Pharmacode Two-Track Laetus binary codes
Postal KIX Dutch PostNL routing
2D · Matrix QR Code, Data Matrix, PDF417

Architecture

┌─────────────────────────────────────────────────────────────┐
│                         Browser                             │
│   ┌───────────────────────────────────────────────────┐     │
│   │ index.html (UI + state)                           │     │
│   │  ├── JsBarcode  (linear codes)                    │     │
│   │  └── bwip-js    (specialty + 2D codes)            │     │
│   │  ─→ HTMLCanvasElement → JPG / PNG Blob            │     │
│   └───────────────────────────────────────────────────┘     │
└──────────────────────────────┬──────────────────────────────┘
                               │ HTTP (static assets only)
                               ▼
┌─────────────────────────────────────────────────────────────┐
│  Docker container · nginx 1.30.1 · alpine 3.23              │
│  • runs as non-root (UID 101)                               │
│  • read-only root filesystem                                │
│  • all caps dropped, no-new-privileges                      │
│  • CSP / HSTS / nosniff / X-Frame-Options / COOP / CORP     │
└─────────────────────────────────────────────────────────────┘

Encoding happens entirely in the browser — the container only ships static HTML/JS. The image cannot leak data because there is no application server to leak through.


Requirements

Component Minimum version Why
Docker Engine 24.0+ Buildkit, compose-v2 features
Docker Compose v2.20+ Modern compose schema (deploy.resources, tmpfs options)
Host OS Linux / macOS / Windows (WSL2) Anything Docker supports
RAM 64 MB free Container reserves 32 MB, caps at 128 MB
Disk ~50 MB Image is ~40 MB
Open port one host port (default 8080) Reconfigurable via .env

Verify your toolchain:

docker --version          # → Docker version 24.x.x or newer
docker compose version    # → Docker Compose version v2.20+ or newer

If you're on an older Docker that only has the standalone docker-compose binary, the file still works — just replace docker compose with docker-compose in the commands below.


Quick Start

# 1. Unzip and enter the project
unzip cipher-barcode-studio.zip
cd cipher-barcode-studio

# 2. (Optional) override the host port
cp .env.example .env       # then edit HOST_PORT if needed

# 3. Build and start
docker compose up -d --build

# 4. Open in browser
#    http://localhost:8080

That's it. To stop:

docker compose down

To stop and remove the built image as well:

docker compose down --rmi local

Configuration

All runtime configuration lives in .env (copied from .env.example):

Variable Default Description
HOST_PORT 8080 Port published on the host machine

The container itself always listens on 8080 internally — only the host-side port is configurable.

Putting it behind a reverse proxy

If you're running this behind Traefik, Caddy, nginx, or similar:

# example with Traefik labels
services:
  cipher:
    # ...existing config...
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.cipher.rule=Host(`barcode.example.com`)"
      - "traefik.http.routers.cipher.tls=true"
      - "traefik.http.routers.cipher.tls.certresolver=letsencrypt"
      - "traefik.http.services.cipher.loadbalancer.server.port=8080"

When fronted by HTTPS, the Strict-Transport-Security header in nginx.conf will kick in automatically.


Project Structure

cipher-barcode-studio/
├── docker-compose.yml      # Orchestration + runtime hardening
├── Dockerfile              # Image build steps
├── .dockerignore           # Keep the build context lean
├── .env.example            # Template for runtime config
├── LICENSE                 # MIT + third-party attributions
├── README.md               # You are here
├── nginx/
│   └── nginx.conf          # Hardened nginx config w/ CSP & security headers
└── web/
    ├── index.html          # The full application (single HTML file)
    └── vendor/
        ├── JsBarcode.all.min.js
        └── bwip-js-min.js

Security

This image is built with a defense-in-depth posture even though it serves static content with no user data:

Container hardening

Control Mechanism
Non-root execution Image's USER is nginx (UID 101). Compose pins user: "101:101".
Read-only rootfs read_only: true in compose; writable paths exposed via tmpfs.
Capability drop cap_drop: [ALL] — no Linux capabilities granted.
No privilege escalation security_opt: no-new-privileges:true.
Resource limits CPU capped at 0.5 cores, memory at 128 MB.
Minimal base nginxinc/nginx-unprivileged:alpine3.23-slim — Alpine, no extra packages, security upgrades applied at build.
Restricted log size json-file driver capped at 5 MB × 3 rotations.

HTTP hardening (in nginx/nginx.conf)

Header Purpose
Content-Security-Policy Restricts scripts/styles/fonts/images to self + Google Fonts only
Strict-Transport-Security One-year HSTS with subdomain coverage (effective behind TLS)
X-Frame-Options: DENY + CSP frame-ancestors 'none' Clickjacking protection
X-Content-Type-Options: nosniff Disables MIME sniffing
Referrer-Policy: strict-origin-when-cross-origin Limits referrer leakage
Permissions-Policy Disables camera, mic, geolocation, USB, payment APIs
Cross-Origin-Opener-Policy: same-origin Window isolation
Cross-Origin-Resource-Policy: same-origin Resource isolation
server_tokens off Hides nginx version

Also: request size capped at 1 KB (this is a static GET-only service), hidden files (/\.) blocked, /healthz lightweight endpoint for orchestrators.

Supply chain

  • JavaScript libraries are vendored under web/vendor/, not loaded from CDN at runtime.
  • Each <script> tag in index.html carries a Subresource Integrity (SRI) hash (sha384), so any tampering with the vendor files will cause the browser to refuse execution.
  • The only runtime third-party connection is to Google Fonts for typography. If you need full air-gap operation, see Removing Google Fonts.

Scanning the image yourself

docker compose build
docker scout cves cipher-barcode-studio:1.0.0        # native Docker scanner
# or
trivy image cipher-barcode-studio:1.0.0              # Trivy
# or
grype cipher-barcode-studio:1.0.0                    # Grype

Expect findings to be limited to base-image OS packages; the application itself has no Node.js, npm, or other runtime dependencies to scan.


Dependency Versions & Vulnerability Status

All versions verified against published CVE feeds at build time.

Component Pinned version Latest as of build Known CVEs Source of truth
nginx 1.30.1 1.30.1 (stable) none active nginx security advisories
Alpine Linux 3.23 3.23 base image kept patched via apk upgrade in Dockerfile Alpine secdb
JsBarcode 3.12.3 3.12.3 0 Snyk advisor, Socket
bwip-js 4.10.1 4.10.1 0 Snyk advisor

The build pulls nginxinc/nginx-unprivileged:1.30.1-alpine3.23-slim and then runs apk upgrade --no-cache so any Alpine package CVEs disclosed after the image was published are still patched.

If you fork this repo, re-run the vulnerability check periodically:

docker compose build --no-cache
trivy image --severity HIGH,CRITICAL cipher-barcode-studio:1.0.0

Operations

View logs

docker compose logs -f cipher

Healthcheck

docker inspect --format='{{json .State.Health}}' cipher-barcode-studio | jq
# or hit it directly:
curl -i http://localhost:8080/healthz

Restart

docker compose restart cipher

Inspect security posture of the running container

docker inspect cipher-barcode-studio | jq '.[0].HostConfig | {ReadonlyRootfs, CapDrop, SecurityOpt, Memory, NanoCpus}'

Expected output should include "ReadonlyRootfs": true, "CapDrop": ["ALL"], and "no-new-privileges:true".


Updating & Maintenance

Rebuild with the latest patches

docker compose build --no-cache --pull
docker compose up -d

--pull forces Docker to refetch the base image, ensuring you pick up new Alpine and nginx releases.

Update a vendored JS library

cd web/vendor

# Example: bump bwip-js
curl -sSL -o bwip-js-min.js "https://cdn.jsdelivr.net/npm/bwip-js@<new-version>/dist/bwip-js-min.js"

# Recompute the SRI hash
openssl dgst -sha384 -binary bwip-js-min.js | openssl base64 -A | xargs -I{} echo "sha384-{}"

# Paste the new hash into the integrity="" attribute in web/index.html
# Rebuild
docker compose up -d --build

Removing Google Fonts

If you need a fully air-gapped install, edit web/index.html:

  1. Delete the three <link rel="preconnect"> and <link rel="stylesheet"> tags pointing at fonts.googleapis.com / fonts.gstatic.com.
  2. The page still works — it just falls back to the next font in each font-family stack (serif / monospace).
  3. Tighten nginx/nginx.conf by removing the https://fonts.googleapis.com and https://fonts.gstatic.com entries from the CSP.

Troubleshooting

Symptom Cause Fix
bind: address already in use Port 8080 is taken Set HOST_PORT=8081 (or any free port) in .env and docker compose up -d
Container restart-loops with "Permission denied" on /var/cache/nginx Older Docker without tmpfs option support Upgrade Docker Engine to 24+
Browser shows blank page, console says "Refused to execute script ... integrity" Vendor file modified locally without updating SRI Recompute SRI hash (see Updating)
Fonts look generic Network blocks Google Fonts Either allow fonts.googleapis.com / fonts.gstatic.com or follow Removing Google Fonts
QR / Data Matrix barcode renders but caption is cut off Output is a 2D code with long text Hide the text via the per-card "Hide text" button, or shorten the input
docker compose not found Only the legacy standalone docker-compose is installed Use docker-compose up -d --build instead; or install the v2 plugin

Resetting from scratch

docker compose down --rmi local --volumes
docker compose up -d --build --force-recreate

Development

To iterate on the UI without rebuilding the image every time, mount web/ over the container's webroot:

# docker-compose.override.yml  (gitignored convention)
services:
  cipher:
    volumes:
      - ./web:/usr/share/nginx/html:ro

Then:

docker compose up -d
# edit web/index.html, refresh the browser — no rebuild needed

When you're done, delete the override file and rebuild for production.


License

This project is released under the MIT License — see LICENSE.

Third-party libraries retain their own licenses:


Built with care. Encode anything. Print everything.