Skip to content

Services bind to 0.0.0.0 with disabled TLS and permissive CORS + exposed Swagger UI #7

Description

@kallal79

Executive summary

This report consolidates a single, prioritized security issue found in the samples/tokens sample stack: a dangerous default configuration that combines services bound to 0.0.0.0, TLS disabled in sample configs, an originally permissive CORS middleware (allowed any origin and exposed Authorization), and an exposed Swagger UI in docker-compose. Together these create a high-severity risk for accidental exposure when developers run samples on public or not-isolated environments.

  • Developers often run samples with defaults. Defaulting to insecure choices (0.0.0.0, TLS disabled, permissive CORS, public Swagger UI) amplifies risk: any misconfiguration or public hosting can expose privileged APIs or state.
  • The impact is broad for this project: these samples demonstrate how to run services and may be copied into deployments or used in demos without adequate hardening.

Files of interest (evidence)

  • samples/tokens/owner/main.go, samples/tokens/issuer/main.go, samples/tokens/endorser/main.go — services bind to 0.0.0.0
  • samples/tokens/conf/*/core.yamltls: enabled: false
  • samples/tokens/compose.yml — publishes swagger-ui (port 8080) and mounts swagger.yaml
  • samples/tokens/common/fsc.go — (original) permissive CORS middleware; replaced in repo with a conservative middleware

Key vulnerable snippets (original state)

  1. Service binding to all interfaces (example from mains):
// in samples/tokens/owner/main.go (and others)
s := &http.Server{
    Handler: h,
    Addr:    net.JoinHostPort("0.0.0.0", *port),
}
go s.ListenAndServe()
  1. Sample TLS disabled (example):
# samples/tokens/conf/owner1/core.yaml
fabric:
  default:
    tls:
      enabled: false
  1. Docker compose exposes Swagger UI (public by default):
  swagger-ui:
    image: swaggerapi/swagger-ui
    ports:
      - "8080:8080"
    environment:
      - URL=/swagger.yaml
    volumes:
      - ./swagger.yaml:/usr/share/nginx/html/swagger.yaml
  1. Original permissive CORS middleware (reconstructed):
// WithAnyCORS adds permissive CORS headers to all responses
func WithAnyCORS(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Access-Control-Allow-Origin", "*")
        w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
        w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
        if r.Method == http.MethodOptions {
            w.WriteHeader(http.StatusNoContent)
            return
        }
        next.ServeHTTP(w, r)
    })
}
  • 0.0.0.0 makes services reachable from network interfaces (including public IPs). If Docker host or VM is public, services are reachable externally.
  • TLS disabled exposes traffic, credentials, or application-specific secrets in cleartext.
  • Permissive CORS plus Authorization in allowed headers means an attacker-controlled page can, from a browser, make authenticated cross-origin requests if user agent stores credentials or on shared machines.
  • Swagger UI documents the API and is often used by developers to explore endpoints — exposing it publicly aids reconnaissance and lowers attacker effort.

Automated check (included)

A small test script was added to assert the conservative default (the fix) rejects unknown origins. Path: tests/check_cors_default.sh.

tests/check_cors_default.sh contents:

#!/usr/bin/env bash
set -euo pipefail
HOST=${1:-localhost:9300}
URL="http://${HOST}/endorser/init"
STATUS=$(curl -s -o /dev/null -w "%{http_code}" -X OPTIONS "$URL" -H "Origin: http://evil.example" -H "Access-Control-Request-Method: POST" || true)
if [ "$STATUS" == "403" ]; then
  echo "PASS: preflight rejected (403)"
  exit 0
else
  echo "FAIL: preflight returned $STATUS"
  exit 2
fi

Expected: With the conservative fix present in this tree (i.e., ALLOWED_ORIGINS not set), the test should pass (preflight returns 403). If ALLOWED_ORIGINS is set to include the origin, preflight will succeed and you'll need to test differently.

  • Replaced permissive WithAnyCORS middleware with a conservative WithCORS middleware that:
    • Rejects cross-origin requests by default when ALLOWED_ORIGINS is not configured.
    • Accepts only explicitly configured origins (comma-separated ALLOWED_ORIGINS).
    • Requires ALLOW_AUTH_HEADER=true to return Authorization in Access-Control-Allow-Headers.
  • Updated owner, issuer, and endorser mains to use the new middleware.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions