mirror of
https://github.com/ksyasuda/dotfiles.git
synced 2026-02-27 12:22:43 -08:00
826 lines
38 KiB
Markdown
826 lines
38 KiB
Markdown
# Go (Golang) Security Spec (Go 1.25.x, Standard Library, net/http)
|
||
|
||
This document is designed as a **security spec** that supports:
|
||
1) **Secure-by-default code generation** for new Go code.
|
||
2) **Security review / vulnerability hunting** in existing Go code (passive “notice issues while working” and active “scan the repo and report findings”).
|
||
|
||
It is intentionally written as a set of **normative requirements** (“MUST/SHOULD/MAY”) plus **audit rules** (what bad patterns look like, how to detect them, and how to fix/mitigate them).
|
||
|
||
--------------------------------------------------------------------
|
||
|
||
## 0) Safety, boundaries, and anti-abuse constraints (MUST FOLLOW)
|
||
|
||
- MUST NOT request, output, log, or commit secrets (API keys, passwords, private keys, session cookies, JWTs, database URLs with credentials, signing keys, client secrets).
|
||
- MUST NOT “fix” security by disabling protections (e.g., `InsecureSkipVerify`, `GOSUMDB=off` for public modules, wildcard CORS + credentials, removing auth checks, disabling CSRF defenses on cookie-auth apps).
|
||
- MUST provide **evidence-based findings** during audits: cite file paths, code snippets, build/deploy configs, and concrete values that justify the claim.
|
||
- MUST treat uncertainty honestly: if a control might exist in infrastructure (reverse proxy, WAF, service mesh, platform config), report it as “not visible in app code; verify at runtime/config.”
|
||
- MUST keep fixes minimal, correct, and production-safe; avoid introducing breaking changes without warning (especially around auth/session flows, and proxies).
|
||
|
||
--------------------------------------------------------------------
|
||
|
||
## 1) Operating modes
|
||
|
||
### 1.1 Generation mode (default)
|
||
When asked to write new Go code or modify existing code:
|
||
- MUST follow every **MUST** requirement in this spec.
|
||
- SHOULD follow every **SHOULD** requirement unless the user explicitly says otherwise.
|
||
- MUST prefer safe-by-default APIs and proven libraries over custom security code.
|
||
- MUST avoid introducing new risky sinks (shell execution, dynamic template execution, serving user files as HTML, unsafe redirects, weak crypto, unbounded parsing, etc.).
|
||
|
||
### 1.2 Passive review mode (always on while editing)
|
||
While working anywhere in a Go repo (even if the user did not ask for a security scan):
|
||
- MUST “notice” violations of this spec in touched/nearby code.
|
||
- SHOULD mention issues as they come up, with a brief explanation + safe fix.
|
||
|
||
### 1.3 Active audit mode (explicit scan request)
|
||
When the user asks to “scan”, “audit”, or “hunt for vulns”:
|
||
- MUST systematically search the codebase for violations of this spec.
|
||
- MUST output findings in a structured format (see §2.3).
|
||
|
||
Recommended audit order:
|
||
1) Build/deploy entrypoints: `main.go`, `cmd/*`, Dockerfiles, Kubernetes manifests, systemd units, CI workflows.
|
||
2) Go toolchain & dependency policy: Go version, modules, `go.mod/go.sum`, proxy/sumdb settings, govulncheck usage.
|
||
3) Secret management and config loading (env, files, secret stores) + logging patterns.
|
||
4) HTTP server configuration (timeouts, body limits, proxy trust, security headers).
|
||
5) AuthN/AuthZ boundaries, session/cookie settings, token validation.
|
||
6) CSRF protections for cookie-authenticated state-changing endpoints.
|
||
7) Template usage and output encoding (XSS), and any “render template from string” behavior (SSTI).
|
||
8) File handling (uploads/downloads/path traversal/temp files), static file serving.
|
||
9) Injection sinks: SQL, OS command execution, SSRF/outbound fetch, open redirects.
|
||
10) Concurrency/resource exhaustion (unbounded goroutines/queues, missing timeouts/contexts).
|
||
11) Use of `unsafe` / `cgo` / `reflect` in security-sensitive paths.
|
||
12) Debug/diagnostic endpoints (pprof/expvar/metrics) exposure.
|
||
13) Cryptography usage (randomness, password hashing).
|
||
|
||
--------------------------------------------------------------------
|
||
|
||
## 2) Definitions and review guidance
|
||
|
||
### 2.1 Untrusted input (treat as attacker-controlled unless proven otherwise)
|
||
Examples include:
|
||
- `*http.Request` fields: `r.URL.Path`, `r.URL.RawQuery`, `r.Form`, `r.PostForm`, headers, cookies, `r.Body`
|
||
- Path parameters from routers (including values extracted from URL paths)
|
||
- JSON/XML/YAML bodies, multipart form parts, uploaded files
|
||
- Any data from external systems (webhooks, third-party APIs, message queues)
|
||
- Any persisted user content (DB rows) that originated from users
|
||
- Configuration values that might be attacker-influenced in some deployments (headers set by upstream proxies, environment variables in multi-tenant systems)
|
||
|
||
### 2.2 State-changing request
|
||
A request is state-changing if it can create/update/delete data, change auth/session state, trigger side effects (purchase, email send, webhook send), or initiate privileged actions.
|
||
|
||
### 2.3 Required audit finding format
|
||
For each issue found, output:
|
||
|
||
- Rule ID:
|
||
- Severity: Critical / High / Medium / Low
|
||
- Location: file path + function/handler name + line(s)
|
||
- Evidence: the exact code/config snippet
|
||
- Impact: what could go wrong, who can exploit it
|
||
- Fix: safe change (prefer minimal diff)
|
||
- Mitigation: defense-in-depth if immediate fix is hard
|
||
- False positive notes: what to verify if uncertain (edge configs, proxy behavior, auth assumptions)
|
||
|
||
--------------------------------------------------------------------
|
||
|
||
## 3) Secure baseline: minimum production configuration (MUST in production)
|
||
|
||
This is the smallest “production baseline” that prevents common Go misconfigurations.
|
||
|
||
### 3.1 Toolchain, patching, and dependency hygiene (MUST)
|
||
- MUST run a supported Go major version and keep to the latest patch releases.
|
||
- MUST treat Go standard library patch releases as security-relevant (many security fixes land in stdlib components like `net/http`, `crypto/*`, parsing packages).
|
||
- MUST use Go modules with committed `go.mod` and `go.sum`.
|
||
- MUST NOT disable module authenticity mechanisms for public modules (checksum DB) unless you have a controlled, documented replacement.
|
||
- MUST run `govulncheck` (source scan and/or binary scan) in CI and address findings.
|
||
|
||
### 3.2 HTTP server baseline (MUST for network-facing services)
|
||
If the program serves HTTP (directly or via a framework built on `net/http`):
|
||
- MUST configure an `http.Server` with explicit timeouts and header limits.
|
||
- MUST set request body size limits (global and per-route as needed).
|
||
- MUST avoid exposing diagnostic endpoints (pprof/expvar) publicly.
|
||
- SHOULD set a consistent set of security headers (or verify they are set at the edge).
|
||
- MUST set cookie security attributes for any cookies you issue.
|
||
- SHOULD implement rate limiting and abuse controls for auth and expensive endpoints.
|
||
|
||
Illustrative baseline skeleton (adjust to your project):
|
||
- Create a dedicated mux (avoid implicit global defaults unless intentionally managed).
|
||
- Wrap handlers with: panic-safe error handling, request ID, logging, auth, and limits.
|
||
|
||
--------------------------------------------------------------------
|
||
|
||
## 4) Rules (generation + audit)
|
||
|
||
Each rule contains: required practice, insecure patterns, detection hints, and remediation.
|
||
|
||
### GO-DEPLOY-001: Keep the Go toolchain and standard library updated (security releases)
|
||
Severity: Medium
|
||
|
||
NOTE: Upgrading dependencies and the core Go version can break projects in unexpected ways. Focus on only security-critical dependencies and if noticed, let the user know rather than upgrading automatically.
|
||
|
||
Required:
|
||
- MUST run a supported Go major release and apply patch releases promptly.
|
||
- SHOULD treat patch releases as security-relevant, even if your application code didn’t change.
|
||
|
||
Insecure patterns:
|
||
- Production builds pinned to old Go versions without a patching process.
|
||
- Docker images like `golang:1.xx` or custom base images that are not updated regularly.
|
||
- CI pipelines that intentionally suppress Go updates.
|
||
|
||
Detection hints:
|
||
- Inspect CI (`.github/workflows`, `gitlab-ci.yml`, etc.) for `go-version:` or toolchain setup.
|
||
- Inspect Dockerfiles for `FROM golang:` tags.
|
||
- Inspect `go.mod` `go` directive and any toolchain pinning.
|
||
|
||
Fix:
|
||
- Upgrade to the latest patch of a supported Go version.
|
||
- Add an automated check (CI) that fails when Go is below an approved minimum.
|
||
|
||
Notes:
|
||
- Go publishes regular minor releases that frequently include security fixes across standard library packages.
|
||
|
||
---
|
||
|
||
### GO-SUPPLY-001: Go module authenticity MUST NOT be disabled for public dependencies
|
||
Severity: High
|
||
|
||
Required:
|
||
- MUST keep module checksum verification enabled for public modules.
|
||
- SHOULD commit `go.sum` and treat changes as security-sensitive.
|
||
- MUST NOT use insecure module fetching settings for public modules.
|
||
- MAY configure private module behavior using `GOPRIVATE`/`GONOSUMDB` for private repos, but must do so narrowly and intentionally.
|
||
|
||
Insecure patterns:
|
||
- `GOSUMDB=off` in CI or production build environments for public modules.
|
||
- `GONOSUMDB=*` or overly broad patterns that effectively disable verification.
|
||
- `GOINSECURE=*` or broad `GOINSECURE` patterns for public modules.
|
||
- `GOPROXY=direct` everywhere without a clear policy.
|
||
|
||
Detection hints:
|
||
- Search build configs for `GOSUMDB`, `GONOSUMDB`, `GOINSECURE`, `GOPROXY`, `GOPRIVATE`.
|
||
- Look for documentation/scripts that recommend disabling checksum DB “to make builds work”.
|
||
|
||
Fix:
|
||
- Restore defaults for public module verification.
|
||
- For private modules:
|
||
- Set `GOPRIVATE=your.private.domain/*`
|
||
- Configure an internal proxy or direct fetching, and restrict `GONOSUMDB` to private patterns only.
|
||
|
||
Notes:
|
||
- Disabling checksum verification removes an important integrity layer against targeted or compromised upstream delivery.
|
||
|
||
---
|
||
|
||
### GO-CONFIG-001: Secrets must be externalized and never logged or committed
|
||
Severity: High (Critical if credentials are committed)
|
||
|
||
Required:
|
||
- MUST load secrets from environment variables, secret managers, or secure config files with restricted permissions.
|
||
- MUST NOT hard-code secrets in Go source, test fixtures that may reach production, or build args.
|
||
- MUST NOT log secrets or full credential-bearing connection strings.
|
||
- SHOULD fail closed in production if required secrets are missing.
|
||
|
||
Insecure patterns:
|
||
- String constants containing tokens/keys/passwords.
|
||
- `.env` files or config files with secrets committed to repo.
|
||
- Logging `os.Environ()`, dumping full configs, or printing DSNs.
|
||
|
||
Detection hints:
|
||
- Search for suspicious literals (`API_KEY`, `SECRET`, `PASSWORD`, `Authorization:`).
|
||
- Inspect config loaders and logging statements.
|
||
- Inspect CI logs or debug print paths.
|
||
|
||
Fix:
|
||
- Move secrets to a secret store / environment variables.
|
||
- Redact sensitive fields in logs.
|
||
- Add secret scanning to CI and pre-commit.
|
||
|
||
---
|
||
|
||
### GO-HTTP-001: HTTP servers MUST set timeouts and MaxHeaderBytes
|
||
Severity: High (DoS risk)
|
||
|
||
Required:
|
||
- MUST set: `ReadHeaderTimeout`, and SHOULD set `ReadTimeout`, `WriteTimeout`, `IdleTimeout` as appropriate for the service.
|
||
- MUST set `MaxHeaderBytes` to a justified limit for your application.
|
||
- MUST NOT rely on default zero-values for timeouts in production for internet-facing servers.
|
||
|
||
Insecure patterns:
|
||
- `http.ListenAndServe(":8080", handler)` with a default `http.Server` (no explicit timeouts).
|
||
- `&http.Server{}` with timeouts left at zero.
|
||
- Missing `MaxHeaderBytes`.
|
||
|
||
Detection hints:
|
||
- Search for `http.ListenAndServe(`, `ListenAndServeTLS(`, `Server{` and inspect configured fields.
|
||
- Check for reverse proxies; even with a proxy, app-level timeouts still matter.
|
||
|
||
Fix:
|
||
- Use `http.Server{ReadHeaderTimeout: ..., ReadTimeout: ..., WriteTimeout: ..., IdleTimeout: ..., MaxHeaderBytes: ...}`.
|
||
- Calibrate timeouts per endpoint type (streaming vs JSON APIs).
|
||
|
||
Notes:
|
||
- Net/http documents that these timeouts exist and that zero/negative values mean “no timeout”; production services should choose explicit values.
|
||
|
||
---
|
||
|
||
### GO-HTTP-002: Request body and multipart parsing MUST be size-bounded
|
||
Severity: Medium (DoS risk; can be High for upload-heavy apps)
|
||
|
||
Required:
|
||
- MUST enforce a global maximum request body size for endpoints that accept bodies.
|
||
- MUST enforce strict multipart upload limits and avoid unbounded form parsing.
|
||
- SHOULD enforce per-route limits when some endpoints legitimately need larger bodies.
|
||
- SHOULD set upstream (proxy) limits as defense-in-depth.
|
||
|
||
Insecure patterns:
|
||
- Reading `r.Body` with `io.ReadAll(r.Body)` without a size cap.
|
||
- Calling `r.ParseMultipartForm(...)` with overly large limits (or forgetting size controls).
|
||
- Accepting file uploads with no limits on file size, number of parts, or total body size.
|
||
|
||
Detection hints:
|
||
- Search for `io.ReadAll(r.Body)`, `json.NewDecoder(r.Body)`, `ParseMultipartForm`, `FormFile`, `multipart`.
|
||
- Look for missing `http.MaxBytesReader` or equivalent per-handler limiting.
|
||
- Look for “upload” endpoints and check limits.
|
||
|
||
Fix:
|
||
- Wrap request bodies with `http.MaxBytesReader(w, r.Body, maxBytes)` before parsing.
|
||
- For multipart, set conservative limits and validate file sizes/part counts explicitly.
|
||
- Set proxy limits (e.g., at ingress) in addition to app limits.
|
||
|
||
Notes:
|
||
- There are known vulnerability classes and advisories related to excessive resource consumption in multipart/form parsing; treat unbounded parsing as a security issue.
|
||
|
||
---
|
||
|
||
### GO-DEPLOY-002: Diagnostic endpoints (pprof/expvar/metrics) MUST NOT be publicly exposed
|
||
Severity: High
|
||
|
||
NOTE: This only applies to production configurations. These endpoints are often used for debug or dev endpoints. If found, confirm that it would be reachable from the actual production deployment.
|
||
|
||
Required:
|
||
- MUST NOT expose `net/http/pprof` handlers on a public internet-facing listener without strong access controls.
|
||
- SHOULD run diagnostics on a separate, internal-only listener (loopback/VPC-only) and require auth.
|
||
- MUST review what diagnostic endpoints reveal (stack traces, memory, command lines, environment, internal URLs).
|
||
|
||
Insecure patterns:
|
||
- Side-effect import `import _ "net/http/pprof"` in a server binary with a public mux.
|
||
- `/debug/pprof/*` reachable without auth.
|
||
- `/debug/vars` (expvar) reachable without auth.
|
||
|
||
Detection hints:
|
||
- Search for `net/http/pprof` imports (including blank imports).
|
||
- Search for route prefixes `/debug/pprof`, `/debug/vars`.
|
||
- Check whether `http.DefaultServeMux` is used and whether any debug handlers register globally.
|
||
|
||
Fix:
|
||
- Remove diagnostics from production builds, or bind them to an internal-only listener.
|
||
- Add strong authentication/authorization (and ideally network-level restrictions).
|
||
|
||
Notes:
|
||
- pprof is typically imported for its side effect of registering HTTP handlers under `/debug/pprof/`.
|
||
|
||
---
|
||
|
||
### GO-HTTP-003: Reverse proxy and forwarded header trust MUST be explicit
|
||
Severity: High (auth, URL generation, logging/auditing correctness)
|
||
|
||
Required:
|
||
- If behind a reverse proxy, MUST define which proxy is trusted and how client IP/scheme/host are derived.
|
||
- MUST NOT trust `X-Forwarded-For`, `X-Forwarded-Proto`, `Forwarded`, or similar headers from the open internet.
|
||
- MUST ensure “secure cookie” logic, redirects, and absolute URL generation do not rely on spoofable headers.
|
||
|
||
Insecure patterns:
|
||
- Using `r.Header.Get("X-Forwarded-For")` as the client IP without validating the proxy boundary.
|
||
- Deriving “is HTTPS” from `X-Forwarded-Proto` without confirming it came from a trusted proxy.
|
||
- Using forwarded `Host` values for password reset links without allowlisting.
|
||
|
||
Detection hints:
|
||
- Search for `X-Forwarded-For`, `X-Forwarded-Proto`, `Forwarded`, `Real-IP`, and any custom “client IP” helpers.
|
||
- Inspect ingress/proxy configs; if not visible, mark as “verify at edge”.
|
||
|
||
Fix:
|
||
- Enforce proxy trust at the edge and in app:
|
||
- Accept forwarded headers only from known proxy IP ranges.
|
||
- Prefer platform-provided mechanisms where available.
|
||
- If generating external links, use a configured allowlisted canonical origin (not the request’s Host header).
|
||
|
||
---
|
||
|
||
### GO-HTTP-004: Security headers SHOULD be set (in app or at the edge)
|
||
Severity: Medium
|
||
|
||
Required (typical web app serving browsers):
|
||
- SHOULD set:
|
||
- `Content-Security-Policy` (CSP) appropriate to the app. NOTE: It is most important to set the CSP's script-src. All other directives are not as important and can generally be excluded for the ease of development.
|
||
- `X-Content-Type-Options: nosniff`
|
||
- Clickjacking protection (`X-Frame-Options` and/or CSP `frame-ancestors`)
|
||
- `Referrer-Policy` and `Permissions-Policy` where appropriate
|
||
- MUST ensure cookies have secure attributes (see GO-HTTP-005).
|
||
|
||
NOTE:
|
||
- These headers may be set via reverse proxy/CDN; if not visible in app code, report as “verify at edge”.
|
||
|
||
Insecure patterns:
|
||
- No security headers anywhere (app or edge) for a browser-facing app.
|
||
- CSP missing for apps rendering untrusted content.
|
||
|
||
Detection hints:
|
||
- Search for middleware setting headers: `w.Header().Set("Content-Security-Policy", ...)`, etc.
|
||
- Search for reverse proxy config that sets headers.
|
||
|
||
Fix:
|
||
- Add centralized header middleware in Go, or configure at the edge.
|
||
- Keep CSP realistic; avoid `unsafe-inline` where possible.
|
||
|
||
---
|
||
|
||
### GO-HTTP-005: Cookies MUST use secure attributes in production
|
||
Severity: Medium
|
||
|
||
Required (production, HTTPS):
|
||
- MUST set `Secure` on cookies that carry auth/session state. IMPORTANT NOTE: Only set `Secure` in production environment when TLS is configured. When running in a local dev environment over HTTP, do not set `Secure` property on cookies. You should do this conditionally based on if the app is running in production mode. You should also include a property like `SESSION_COOKIE_SECURE` which can be used to disable `Secure` cookies when testing over HTTP.
|
||
- MUST set `HttpOnly` on auth/session cookies.
|
||
- SHOULD set `SameSite=Lax` by default (or `Strict` if compatible), and only use `None` when necessary (and only with `Secure`).
|
||
- SHOULD set bounded lifetimes (`Max-Age`/`Expires`) appropriate to the app.
|
||
|
||
Insecure patterns:
|
||
- Setting auth/session cookies without `Secure` in HTTPS deployments.
|
||
- Cookies without `HttpOnly` for session identifiers.
|
||
- `SameSite=None` for cookie-authenticated apps without a strong CSRF strategy.
|
||
|
||
Detection hints:
|
||
- Search for `http.SetCookie`, `&http.Cookie{`, `Set-Cookie`.
|
||
- Inspect cookie flags in auth/session code.
|
||
|
||
Fix:
|
||
- Set the correct fields on `http.Cookie` and centralize cookie creation.
|
||
|
||
Notes:
|
||
- SameSite is defense-in-depth and does not replace CSRF protections for cookie-auth apps.
|
||
|
||
---
|
||
|
||
### GO-HTTP-006: Cookie-authenticated state-changing endpoints MUST be CSRF-protected
|
||
Severity: High
|
||
|
||
- IMPORTANT NOTE: If cookies are not used for auth (e.g., pure bearer token in Authorization header with no ambient cookies), CSRF is not a risk for those endpoints.
|
||
|
||
Required:
|
||
- MUST protect all state-changing endpoints (POST/PUT/PATCH/DELETE) that rely on cookies for authentication.
|
||
- SHOULD use a well-tested CSRF library/middleware rather than rolling your own.
|
||
- MAY use additional defenses (Origin/Referer checks, Fetch Metadata, SameSite cookies), but tokens remain the primary defense for cookie-authenticated apps.
|
||
If tokens are impractical, or for small applications:
|
||
* MUST at a minimum require a custom header to be set and set the session cookie SESSION_COOKIE_SAMESITE=lax, as this is the strongest method besides requiring a form token, and may be much easier to implement.
|
||
|
||
|
||
Insecure patterns:
|
||
- Cookie-authenticated JSON endpoints that mutate state with no CSRF checks.
|
||
- Using GET for state-changing actions.
|
||
|
||
Detection hints:
|
||
- Enumerate all non-GET routes and identify auth mechanism.
|
||
- Look for CSRF middleware usage; if absent, treat as suspicious in browser-facing apps.
|
||
|
||
Fix:
|
||
- Add CSRF middleware and ensure it covers all state-changing routes.
|
||
- If the service is an API intended for non-browser clients, avoid cookie auth; use Authorization headers.
|
||
|
||
---
|
||
|
||
### GO-HTTP-007: CORS must be explicit and least-privilege
|
||
Severity: Medium (High if misconfigured with credentials)
|
||
|
||
Required:
|
||
- If CORS is not needed, MUST keep it disabled.
|
||
- If CORS is needed:
|
||
- MUST allowlist trusted origins (do not reflect arbitrary origins)
|
||
- MUST be careful with credentialed requests; do not combine broad origins with cookies
|
||
- SHOULD restrict allowed methods/headers
|
||
|
||
Insecure patterns:
|
||
- `Access-Control-Allow-Origin: *` paired with cookies (`Access-Control-Allow-Credentials: true`).
|
||
- Reflecting `Origin` without validation.
|
||
|
||
Detection hints:
|
||
- Search for `Access-Control-Allow-` header setting.
|
||
- Search for CORS middleware configuration.
|
||
|
||
Fix:
|
||
- Implement strict origin allowlists and minimal methods/headers.
|
||
- Ensure cookie-auth endpoints are not exposed cross-origin unless required.
|
||
|
||
---
|
||
|
||
### GO-XSS-001: Use html/template and avoid bypassing auto-escaping with untrusted data
|
||
Severity: High
|
||
|
||
Required:
|
||
- MUST use `html/template` for HTML rendering (not `text/template`).
|
||
- MUST NOT convert untrusted data into “trusted” template types (`template.HTML`, `template.JS`, `template.URL`, etc.).
|
||
- SHOULD keep templates static and controlled by developers; treat dynamic templates as high risk.
|
||
- MUST NOT serve user-uploaded HTML/JS as active content unless explicitly intended and safely sandboxed.
|
||
|
||
Insecure patterns:
|
||
- `text/template` used to generate HTML.
|
||
- Using `template.HTML(userInput)` or similar typed wrappers.
|
||
- Directly writing unescaped user content into HTML responses.
|
||
|
||
Detection hints:
|
||
- Search for `text/template`, `template.New(...).Parse(...)`, and typed wrappers like `template.HTML(`.
|
||
- Inspect handlers that return HTML with string concatenation.
|
||
|
||
Fix:
|
||
- Use `html/template` and pass untrusted data as data, not markup.
|
||
- If you must allow limited HTML, use a vetted HTML sanitizer and still be careful with attributes/URLs.
|
||
|
||
---
|
||
|
||
### GO-SSTI-001: Never parse/execute templates from untrusted input (SSTI)
|
||
Severity: Critical
|
||
|
||
Required:
|
||
- MUST NOT call `template.Parse` / `template.ParseFiles` / `template.New(...).Parse(...)` on template text influenced by untrusted input.
|
||
- MUST treat “user-defined templates” as a special high-risk design:
|
||
- MUST use heavy sandboxing and strict allowlists
|
||
- MUST isolate execution (process/container boundary) if truly required
|
||
|
||
Insecure patterns:
|
||
- `tmpl := template.Must(template.New("x").Parse(r.FormValue("tmpl")))`
|
||
- Reading templates from uploads / DB entries and executing them in the same trust domain as server code.
|
||
|
||
Detection hints:
|
||
- Search for `.Parse(` and trace the origin of the template string.
|
||
- Look for “custom email templates”, “user theming templates”, etc.
|
||
|
||
Fix:
|
||
- Replace with safe substitution mechanisms (no code execution).
|
||
- If templates must be user-controlled, isolate and sandbox aggressively.
|
||
|
||
---
|
||
|
||
### GO-PATH-001: Prevent path traversal and unsafe file serving
|
||
Severity: High
|
||
|
||
Required:
|
||
- MUST NOT pass user-controlled paths to `os.Open`, `os.ReadFile`, `http.ServeFile`, or `http.FileServer` without strict validation and base-dir enforcement.
|
||
- MUST treat `..`, absolute paths, and OS-specific path tricks as hostile input.
|
||
- SHOULD store user uploads outside any static web root; serve through controlled handlers.
|
||
- MUST avoid directory listing for sensitive file trees.
|
||
|
||
Insecure patterns:
|
||
- `http.ServeFile(w, r, r.URL.Query().Get("path"))`
|
||
- `os.Open(filepath.Join(baseDir, userPath))` without checking that the result stays under `baseDir`
|
||
- `http.FileServer(http.Dir("."))` serving the project root or user-writable directories
|
||
|
||
Detection hints:
|
||
- Search for `ServeFile(`, `FileServer(`, `http.Dir(`, `os.Open(`, `ReadFile(`, `filepath.Join(`.
|
||
- Trace whether path components come from request/DB.
|
||
|
||
Fix:
|
||
- Use an allowlist of file identifiers (e.g., database IDs) mapped to server-side paths.
|
||
- Enforce base directory containment after cleaning and joining.
|
||
- Serve active formats as downloads (`Content-Disposition: attachment`) unless explicitly intended.
|
||
|
||
---
|
||
|
||
### GO-UPLOAD-001: File uploads must be validated, stored safely, and served safely
|
||
Severity: High
|
||
|
||
Required:
|
||
- MUST enforce upload size limits (app + edge).
|
||
- MUST validate file type using allowlists and content checks (not only extensions).
|
||
- MUST store uploads outside executable/static roots when possible.
|
||
- SHOULD generate server-side filenames (random IDs) and avoid trusting original names.
|
||
- MUST serve potentially active formats safely (download attachment) unless explicitly intended.
|
||
|
||
Insecure patterns:
|
||
- Accepting arbitrary file types and serving them back inline.
|
||
- Using user-supplied filename as storage path.
|
||
- Missing size/type validation.
|
||
|
||
Detection hints:
|
||
- Search for `multipart`, `FormFile`, `ParseMultipartForm`, `io.Copy` to disk.
|
||
- Check where files are stored and how they are served.
|
||
|
||
Fix:
|
||
- Implement allowlist validation + safe storage + safe serving.
|
||
- Add scanning/quarantine workflows where applicable.
|
||
|
||
---
|
||
|
||
### GO-INJECT-001: Prevent SQL injection (parameterized queries / ORM)
|
||
Severity: High
|
||
|
||
Required:
|
||
- MUST use parameterized queries or an ORM that parameterizes under the hood.
|
||
- MUST NOT build SQL by string concatenation / `fmt.Sprintf` / string interpolation with untrusted input.
|
||
|
||
Insecure patterns:
|
||
- `fmt.Sprintf("SELECT ... WHERE id=%s", r.URL.Query().Get("id"))`
|
||
- `query := "UPDATE users SET role='" + role + "' WHERE id=" + id`
|
||
|
||
Detection hints:
|
||
- Grep for `SELECT`, `INSERT`, `UPDATE`, `DELETE` and check how query strings are built.
|
||
- Trace untrusted data into `db.Query`, `db.Exec`, `QueryRow`, etc.
|
||
|
||
Fix:
|
||
- Replace with placeholders (`?`, `$1`, etc.) and pass parameters separately.
|
||
- Validate and type-check IDs before use.
|
||
|
||
---
|
||
|
||
### GO-INJECT-002: Prevent OS command injection; avoid shelling out with untrusted input
|
||
Severity: Critical to High (depends on exposure)
|
||
|
||
Required:
|
||
- MUST avoid executing external commands with attacker-controlled strings.
|
||
- If subprocess is necessary:
|
||
- MUST use `exec.CommandContext` with an argument list (not `sh -c`).
|
||
- MUST NOT pass untrusted input to a shell (`bash -c`, `sh -c`, PowerShell).
|
||
- SHOULD use strict allowlists for any variable component (subcommand, flags, filenames).
|
||
- MUST assume CLI tools may interpret attacker-controlled args as flags or special values.
|
||
|
||
Insecure patterns:
|
||
- `exec.Command("sh", "-c", userString)`
|
||
- `exec.Command("bash", "-c", fmt.Sprintf("tool %s", user))`
|
||
- Calling the shell to get glob expansion for user-supplied globs.
|
||
|
||
Detection hints:
|
||
- Search for `os/exec`, `exec.Command(`, `CommandContext(`, `"sh"`, `"bash"`, `"-c"`.
|
||
- Trace untrusted input into command name/args.
|
||
|
||
Fix:
|
||
- Use library APIs instead of subprocesses.
|
||
- Hardcode command and allowlist/validate args.
|
||
- If a shell is unavoidable, escape robustly and treat as high risk (prefer avoiding).
|
||
|
||
Notes:
|
||
- The Go `os/exec` package intentionally does invoke a shell; introducing `sh -c` reintroduces shell injection hazards.
|
||
|
||
---
|
||
|
||
### GO-SSRF-001: Prevent SSRF in outbound HTTP requests
|
||
Severity: Medium (High in cloud/LAN environments)
|
||
|
||
- Note: For small stand alone projects this is less important. It is most important when deploying into an LAN or with other services listening on the same server.
|
||
|
||
Required:
|
||
- MUST treat outbound requests to user-provided URLs as high risk.
|
||
- SHOULD allowlist hosts/domains for any user-influenced URL fetch.
|
||
- SHOULD block access to localhost/private IP ranges/link-local addresses and cloud metadata endpoints.
|
||
- MUST restrict schemes to `http`/`https` (no `file:`, `gopher:`, etc.).
|
||
- MUST set client timeouts and restrict redirects.
|
||
|
||
Insecure patterns:
|
||
- `http.Get(r.URL.Query().Get("url"))`
|
||
- “URL preview” / “webhook test” endpoints that fetch arbitrary URLs.
|
||
|
||
Detection hints:
|
||
- Search for `http.Get`, `client.Do`, and URL values derived from requests/DB.
|
||
- Identify features that fetch remote resources.
|
||
|
||
Fix:
|
||
- Parse URLs strictly; enforce scheme and allowlisted hostnames.
|
||
- Resolve DNS and enforce IP-range restrictions (with care for DNS rebinding).
|
||
- Set timeouts, disable redirects unless needed, and cap response sizes.
|
||
|
||
---
|
||
|
||
### GO-HTTPCLIENT-001: Outbound HTTP clients MUST set timeouts and close bodies
|
||
Severity: High (DoS and resource exhaustion)
|
||
|
||
Required:
|
||
- MUST set an overall timeout on `http.Client` usage (or equivalent per-request deadlines via context + transport timeouts).
|
||
- MUST ensure `resp.Body.Close()` is called for all successful requests (typically `defer resp.Body.Close()` immediately after error check).
|
||
- SHOULD limit response body reads (do not `io.ReadAll` unbounded responses).
|
||
- SHOULD restrict redirects for security-sensitive fetches (SSRF, auth flows).
|
||
|
||
Insecure patterns:
|
||
- Using `http.DefaultClient` / `http.Get` for user-influenced destinations with no timeout policy.
|
||
- Missing `defer resp.Body.Close()` leading to resource leaks.
|
||
- `io.ReadAll(resp.Body)` with no limit.
|
||
|
||
Detection hints:
|
||
- Search for `http.Get(`, `http.Post(`, `client := &http.Client{}` without `Timeout`, `client.Do(` and missing closes.
|
||
- Search for `io.ReadAll(resp.Body)`.
|
||
|
||
Fix:
|
||
- Use a configured client with timeouts.
|
||
- Always close response bodies.
|
||
- Use bounded readers (`io.LimitReader`) for large/untrusted responses.
|
||
|
||
Notes:
|
||
- The net/http package exposes `DefaultClient` as a zero-valued `http.Client`, which can easily lead to “no timeout” behavior unless configured.
|
||
|
||
---
|
||
|
||
### GO-REDIRECT-001: Prevent open redirects
|
||
Severity: Medium (can be High with auth flows)
|
||
|
||
Required:
|
||
- MUST validate redirect targets derived from untrusted input (`next`, `redirect`, `return_to`).
|
||
- SHOULD prefer only same-site relative paths.
|
||
- SHOULD fall back to a safe default on validation failure.
|
||
|
||
Insecure patterns:
|
||
- `http.Redirect(w, r, r.URL.Query().Get("next"), http.StatusFound)` with no validation.
|
||
|
||
Detection hints:
|
||
- Search for `http.Redirect(` and check origin of the location.
|
||
|
||
Fix:
|
||
- Allowlist internal paths or known domains.
|
||
- Reject absolute URLs unless explicitly needed and allowlisted.
|
||
|
||
---
|
||
|
||
### GO-CRYPTO-001: Cryptographic randomness MUST come from crypto/rand
|
||
Severity: High (Critical if used for auth/session tokens or keys)
|
||
|
||
Required:
|
||
- MUST use `crypto/rand` for:
|
||
- session IDs, password reset tokens, API keys, CSRF tokens, nonces
|
||
- encryption keys, signing keys, salts when required
|
||
- MUST NOT use `math/rand` for any security-sensitive value.
|
||
- SHOULD use built-in helpers that produce appropriately strong tokens when available.
|
||
|
||
Insecure patterns:
|
||
- `math/rand.Seed(time.Now().UnixNano())` followed by token generation for auth or sessions.
|
||
- Using UUIDv4-like constructs built from `math/rand`.
|
||
|
||
Detection hints:
|
||
- Search for `math/rand`, `rand.Seed`, `rand.Intn` in code that touches auth/session/token flows.
|
||
- Search for custom token generators.
|
||
|
||
Fix:
|
||
- Switch to `crypto/rand` (`rand.Reader`, `rand.Read`, or secure token helpers).
|
||
- Ensure sufficient entropy and use URL-safe encoding.
|
||
|
||
Notes:
|
||
- The crypto/rand package provides secure randomness APIs and token generation helpers.
|
||
|
||
---
|
||
|
||
### GO-AUTH-001: Password storage MUST use adaptive hashing (bcrypt/argon2id) and safe comparisons
|
||
Severity: High
|
||
|
||
Required:
|
||
- MUST hash passwords using an adaptive password hashing function (bcrypt or argon2id).
|
||
- MUST NOT store plaintext passwords or reversible encryption of passwords.
|
||
- MUST compare secrets in constant time when relevant (tokens, MACs, API keys) to reduce timing leaks.
|
||
- SHOULD ensure password policies do not exceed algorithm constraints (e.g., bcrypt has input length limits; handle long passphrases appropriately).
|
||
|
||
Insecure patterns:
|
||
- `sha256(password)` stored as password hash.
|
||
- Plaintext password storage.
|
||
- Comparing secrets with `==` in timing-sensitive contexts.
|
||
|
||
Detection hints:
|
||
- Search for `sha1`, `sha256`, `md5` used on passwords.
|
||
- Search for `bcrypt`/`argon2` usage; if absent, suspect.
|
||
- Search for `==` comparisons on tokens/API keys.
|
||
|
||
Fix:
|
||
- Use `bcrypt.GenerateFromPassword` / `CompareHashAndPassword` or argon2id with recommended parameters.
|
||
- Use constant-time compare helpers when comparing MACs/tokens.
|
||
|
||
Notes:
|
||
- Go provides bcrypt in `golang.org/x/crypto/bcrypt`, and constant-time comparisons in `crypto/subtle`.
|
||
|
||
---
|
||
|
||
### GO-CONC-001: Data races and concurrency hazards MUST be treated as security-relevant
|
||
Severity: Medium to High (depends on what races affect)
|
||
|
||
Required:
|
||
- MUST run tests with the race detector (`go test -race`) in CI for security-sensitive services.
|
||
- MUST fix detected races; do not suppress without deep justification.
|
||
- SHOULD treat shared mutable state in handlers as high risk; enforce synchronization or avoid shared mutability.
|
||
|
||
Insecure patterns:
|
||
- Global maps/slices mutated from multiple goroutines without a mutex.
|
||
- Caches or auth/session state stored in globals without concurrency protection.
|
||
- Racy access to authorization state (can lead to bypasses or inconsistent enforcement).
|
||
|
||
Detection hints:
|
||
- Search for `var someMap = map[...]...` used in handlers.
|
||
- Look for missing `sync.Mutex`, `sync.Map`, channels, or other synchronization.
|
||
- Ensure CI includes `-race` and that it runs relevant tests.
|
||
|
||
Fix:
|
||
- Add proper synchronization or redesign to avoid shared mutable state.
|
||
- Add race tests and run them continuously.
|
||
|
||
Notes:
|
||
- The Go race detector only finds races that occur in executed code paths; improve test coverage and run realistic workloads with `-race` where feasible.
|
||
|
||
---
|
||
|
||
### GO-UNSAFE-001: Use of unsafe/cgo MUST be minimized and audited like memory-unsafe code
|
||
Severity: High (Critical in high-risk code paths)
|
||
|
||
Required:
|
||
- SHOULD avoid importing `unsafe` in application code unless absolutely necessary.
|
||
- If `unsafe` is used, MUST treat it as “manual memory safety” requiring careful review and test coverage.
|
||
- If `cgo` is used, MUST treat the C/C++ boundary as memory-unsafe; apply secure coding practices on the C side and isolate where possible.
|
||
|
||
Insecure patterns:
|
||
- Widespread `unsafe.Pointer` casts in parsing, serialization, auth, or network code.
|
||
- `cgo` used for parsing or security boundaries without sandboxing.
|
||
|
||
Detection hints:
|
||
- Search for `import "unsafe"`, `unsafe.Pointer`, `// #cgo`, `import "C"`.
|
||
- Prioritize review where unsafe touches untrusted input.
|
||
|
||
Fix:
|
||
- Replace unsafe/cgo usage with safe standard library alternatives where possible.
|
||
- Isolate unsafe code in small, well-tested modules with fuzz/race tests.
|
||
|
||
Notes:
|
||
- The unsafe package explicitly provides operations that step around Go’s type safety guarantees.
|
||
|
||
--------------------------------------------------------------------
|
||
|
||
## 5) Practical scanning heuristics (how to “hunt”)
|
||
|
||
When actively scanning, use these high-signal patterns:
|
||
|
||
Toolchain & dependencies:
|
||
- `FROM golang:` (Dockerfiles), `go-version:` (CI), `toolchain go` (go.mod), pinned old versions
|
||
- `GOSUMDB=off`, `GOINSECURE`, `GONOSUMDB`, `GOPROXY=direct`
|
||
- `replace` directives in `go.mod` to forks/paths
|
||
- `govulncheck` missing in CI
|
||
|
||
HTTP server hardening:
|
||
- `http.ListenAndServe(`, `ListenAndServeTLS(`, `&http.Server{` with missing timeouts
|
||
- `ReadHeaderTimeout: 0`, `ReadTimeout: 0`, `WriteTimeout: 0`, `IdleTimeout: 0`, missing `MaxHeaderBytes`
|
||
|
||
Body parsing / DoS:
|
||
- `io.ReadAll(r.Body)`, `json.NewDecoder(r.Body)` without size cap
|
||
- `ParseMultipartForm`, `FormFile`, `multipart.NewReader` without explicit limits
|
||
- Missing `http.MaxBytesReader`
|
||
|
||
Debug exposure:
|
||
- `import _ "net/http/pprof"`
|
||
- `/debug/pprof`, `/debug/vars`
|
||
|
||
Templates / XSS / SSTI:
|
||
- `text/template` used for HTML output
|
||
- `template.HTML(`, `template.JS(`, `template.URL(` with user-controlled data
|
||
- `.Parse(` on user-controlled strings
|
||
|
||
Files:
|
||
- `http.ServeFile(` with user path
|
||
- `http.FileServer(http.Dir(` pointing at repo root or uploads
|
||
- `os.Open(filepath.Join(base, user))` without containment checks
|
||
|
||
Injection:
|
||
- SQL building with `fmt.Sprintf`, string concatenation near `db.Query/Exec`
|
||
- `exec.Command("sh","-c", ...)`, `exec.Command("bash","-c", ...)`
|
||
|
||
SSRF / outbound HTTP:
|
||
- `http.Get(userURL)`, `client.Do(req)` where URL comes from request/DB
|
||
- Missing client timeout, missing `resp.Body.Close()`, unbounded `io.ReadAll(resp.Body)`
|
||
|
||
Crypto:
|
||
- `math/rand` in token/session generation
|
||
- `InsecureSkipVerify: true`
|
||
- Password hashing with `sha256`/`md5` instead of bcrypt/argon2
|
||
|
||
Concurrency:
|
||
- Shared maps/slices mutated from handlers without locks
|
||
- CI lacking `go test -race`
|
||
|
||
Always try to confirm:
|
||
- data origin (untrusted vs trusted)
|
||
- sink type (template/SQL/subprocess/files/http)
|
||
- protective controls present (limits, validation, allowlists, middleware, network controls)
|
||
|
||
--------------------------------------------------------------------
|
||
|
||
## 6) Sources (accessed 2026-01-28)
|
||
|
||
Primary Go documentation:
|
||
- Go Security Policy — https://go.dev/doc/security/policy
|
||
- Go Release History (security fixes in patch releases) — https://go.dev/doc/devel/release
|
||
- Go 1.25 Release Notes — https://go.dev/doc/go1.25
|
||
- net/http (server timeouts, MaxHeaderBytes, DefaultClient) — https://pkg.go.dev/net/http
|
||
- html/template (auto-escaping and trusted-template assumptions) — https://pkg.go.dev/html/template
|
||
- crypto/tls (MinVersion defaults, InsecureSkipVerify warnings) — https://pkg.go.dev/crypto/tls
|
||
- crypto/rand (secure randomness, token helpers) — https://pkg.go.dev/crypto/rand
|
||
- crypto/subtle (constant-time comparisons) — https://pkg.go.dev/crypto/subtle
|
||
- os/exec (no shell by default; command execution guidance) — https://pkg.go.dev/os/exec
|
||
- unsafe (bypasses type safety) — https://go.dev/src/unsafe/unsafe.go
|
||
- net/http/pprof (debug endpoints) — https://pkg.go.dev/net/http/pprof
|
||
- cmd/go (module authentication via go.sum/checksum DB; env vars like GOINSECURE) — https://pkg.go.dev/cmd/go
|
||
- Module Mirror and Checksum Database Launched (Go blog) — https://go.dev/blog/module-mirror-launch
|
||
- govulncheck documentation — https://pkg.go.dev/golang.org/x/vuln/cmd/govulncheck
|
||
- Go Race Detector documentation — https://go.dev/doc/articles/race_detector
|
||
- bcrypt (password hashing) — https://pkg.go.dev/golang.org/x/crypto/bcrypt
|
||
- Go vulnerability entry example (multipart resource consumption) — https://pkg.go.dev/vuln/GO-2023-1569
|
||
|
||
OWASP Cheat Sheet Series (general web security):
|
||
- Session Management — https://cheatsheetseries.owasp.org/cheatsheets/Session_Management_Cheat_Sheet.html
|
||
- CSRF Prevention — https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html
|
||
- SSRF Prevention — https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html
|
||
- XSS Prevention — https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html
|
||
- HTTP Security Response Headers — https://cheatsheetseries.owasp.org/cheatsheets/HTTP_Headers_Cheat_Sheet.html |