Go Security Engineering
Protect a defined asset and trust boundary. For review-only requests, report evidence and remediation without changing code. For implementation requests, make the smallest verified change in scope.
Establish the security context
Before judging code, identify:
- the asset, attacker capability, entry point, and trust boundary;
- authentication and authorization assumptions;
- data sensitivity and tenancy boundaries;
- network and deployment controls that are actually present;
- the concrete source-to-sink path;
- existing security policy and accepted risk.
Do not report a vulnerability from a suspicious pattern alone. Confirm attacker influence, reachable behavior, missing controls, and realistic impact. Cite the exact code path and state assumptions. Calibrate severity from impact and exploitability rather than a fixed label attached to an API.
Review high-risk boundaries
Injection and code execution
- Pass SQL values through driver parameters. Map identifiers, clauses, and sort directions from fixed allowlists.
- Pass command arguments directly to exec.Command or an equivalent API. Avoid invoking a shell with attacker-influenced text.
- Use html/template for untrusted HTML content and preserve contextual escaping. Treat template.HTML and related safe types as reviewed trust-boundary escapes.
- Validate structured input before using it to form queries, paths, templates, or interpreter expressions.
Files, archives, and uploads
Resolve paths against an intended root and prove the resulting target stays within that root using path-aware comparison. Lexical cleaning alone is insufficient when symlinks or filesystem races matter.
For archives and uploads:
- reject absolute and escaping paths;
- limit compressed and expanded size, file count, and per-file size;
- create files with appropriate permissions and exclusive behavior where collisions matter;
- avoid following unsafe symlinks;
- stream with bounds instead of trusting declared sizes;
- store user filenames as metadata rather than filesystem authority.
Network and HTTP
- Configure server header and request timeouts appropriate to the service, plus bounded request bodies where endpoints can be abused.
- Configure clients with timeouts and propagated context.
- For SSRF-sensitive fetches, constrain schemes and destinations, account for DNS rebinding and redirects, and protect cloud metadata and internal address ranges according to deployment.
- Do not expose pprof, metrics containing sensitive labels, or debug handlers on an unauthenticated public listener.
- Derive client IP or forwarded scheme only from headers set by explicitly trusted proxies.
- Set cookie Secure, HttpOnly, SameSite, path, domain, and lifetime according to the actual session and cross-site flow.
Authentication, authorization, and secrets
Authenticate the caller, then authorize the requested action on the specific resource. Middleware presence alone does not prove object-level authorization.
Keep credentials out of source, URLs, command arguments, logs, traces, metrics, errors, and generated artifacts. Use the project's secret provider. When a secret is exposed, removal from the latest file is not remediation; coordinate rotation and history handling within explicit scope.
Compare MACs, signatures, and secret-derived values with the appropriate constant-time primitive. Bound login, token, and recovery workflows against replay and brute force.
Cryptography and TLS
Use maintained standard-library or established cryptographic APIs with documented constructions. Do not design custom encryption, nonce schemes, password hashing, or key derivation.
- Use crypto/rand for unpredictable values.
- Use authenticated encryption where confidentiality and integrity are required.
- Never reuse a nonce where the selected construction requires uniqueness.
- Validate certificates and hostnames; do not use InsecureSkipVerify as a production bypass.
- Treat algorithm, key size, password-hashing cost, and rotation as policy decisions informed by current authoritative guidance.
Do not replace a legacy construction without planning compatibility and data migration.
Concurrency and resource exhaustion
Security invariants must survive concurrent requests. Review check-then-act authorization, quota, balance, token, and uniqueness flows for races. Apply limits to goroutines, queues, input size, decompression, recursion, regex work, and external calls according to the threat model.
Use the race detector as evidence when supported, but do not assume a clean race run proves logical atomicity.
Dependencies and verification
Use the official Go vulnerability database and govulncheck when the tool is already available or adding it is explicitly in scope. Confirm affected versions, packages, symbols, and local reachability. A module advisory is not automatically exploitable, and no known advisory is not proof of safety.
Select checks proportional to the change:
- focused unit or integration tests for the exploit condition and fix;
- fuzzing for parsers and boundary-heavy input when useful;
- go test with the race detector for relevant concurrent code;
- go vet and existing repository linters;
- configuration or deployment checks needed to make a code control effective.
Do not install scanners, send proprietary code to external services, rotate secrets, alter production controls, or disclose a finding outside the requested workspace without explicit authorization.
Finding format
For each validated issue, report:
- title and calibrated severity;
- affected code and source-to-sink path;
- attacker prerequisites;
- concrete impact;
- existing controls and why they fail or reduce risk;
- minimal remediation;
- verification method;
- assumptions or unresolved evidence.
Prefer a few validated findings over a long speculative checklist.