# 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