hyperledger / hyperledger/fabric-x-samples
Services bind to 0.0.0.0 with disabled TLS and permissive CORS + exposed Swagger UI
- Dominant language
- Go
- Stars
- 2
- Forks
- 10
- Avg merge
- 5d 21h
- Merged PRs (30d)
- 2
Description
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.yaml` — `tls: 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):
```go
// in samples/tokens/owner/main.go (and others)
s := &http.Server{
Handler: h,
Addr: net.JoinHostPort("0.0.0.0", *port),
}
go s.ListenAndServe()
```
2) Sample TLS disabled (example):
```yaml
# samples/tokens/conf/owner1/core.yaml
fabric:
default:
tls:
enabled: false
```
3) Docker compose exposes Swagger UI (public by default):
```yaml
swagger-ui:
image: swaggerapi/swagger-ui
ports:
- "8080:8080"
environment:
- URL=/swagger.yaml
volumes:
- ./swagger.yaml:/usr/share/nginx/html/swagger.yaml
```
4) Original permissive CORS middleware (reconstructed):
```go
// 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:
```bash
#!/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.
Contributor guide
No contributing guide indexed for this repository
Assessment
This issue has not been assessed yet.