Fix cards for Go and net/http

A01:2025 Broken Access Control

  • Missing object-level authorization (IDOR and BOLA)CWE-862 · CWE-639 · CWE-863

    Build the signed-in user in middleware from the verified session and the users table, never from request data, and put the same scope in every query's WHERE clause: tenant_id = :tenant AND (owner_id = :user OR :is_billing_admin).

  • Open redirectCWE-601

    Refuse a next value containing a backslash, a control character or a space, then url.Parse it: accept a path starting with exactly one slash, or a URL whose scheme and host exactly match an allowlisted origin, without user info; otherwise a fixed page.

  • Path traversalCWE-22 · CWE-23 · CWE-36 · CWE-59

    Open the base directory once with root, err := os.OpenRoot(base) and serve requested files through root.Open(name). os.Root (Go 1.24+) refuses names that leave the directory, whether by ../, an absolute path or a symlink, at the moment it opens the file. filepath.Join is not a check, and a strings.HasPrefix test on its result accepts files-secret and follows symlinks.

  • Server-side request forgery (SSRF)CWE-918

    Attach the policy to the connection, not the URL string. Give http.Transport a DialContext that resolves the host, rejects loopback, private, link-local and other non-public addresses with net/netip, and dials the vetted IP.

A04:2025 Cryptographic Failures

  • Insecure randomness for security tokensCWE-330 · CWE-338

    Generate the token with crypto/rand: rand.Read into 32 bytes, encoded with base64.RawURLEncoding, is 256 bits from the OS CSPRNG (rand.Text() gives at least 128).

  • Weak password hashingCWE-916 · CWE-759 · CWE-327

    Hash new passwords with argon2.IDKey from golang.org/x/crypto at or above OWASP's minimum (memory 19456 KiB, 2 passes, 1 lane) with a 16-byte salt from crypto/rand, and store a PHC string with the parameters.

A05:2025 Injection

  • Cross-site scripting (XSS)CWE-79

    Render HTML with html/template, never text/template: it escapes each value for where it lands: text, attribute or URL.

  • OS command and argument injectionCWE-78 · CWE-88

    Run the program itself with each argument as its own string: exec.CommandContext(ctx, tool, "--", name). os/exec never invokes a shell unless you call sh -c, so metacharacters stay characters, and "--" keeps a name that starts with a dash from posing as an option.

  • SQL injectionCWE-89

    Keep the SQL text constant and pass values as arguments: db.QueryContext(ctx, "... WHERE c.name = ?

A06:2025 Insecure Design

  • Unbounded resource consumptionCWE-770 · CWE-400

    Cap the body with r.Body = http.MaxBytesReader(w, r.Body, limit): it counts bytes as they are read and stops at the limit whatever the client declares, so a chunked or lying request is bounded too.

  • Unrestricted file uploadCWE-434 · CWE-646 · CWE-73 · CWE-770

    Decide the type from the bytes, and never serve uploads from the web root.

A07:2025 Authentication Failures

  • Improper TLS certificate validationCWE-295 · CWE-297 · CWE-296

    Leave verification on and change only the trust anchor: put the partner's CA in x509.NewCertPool(), set it as tls.Config.RootCAs with MinVersion tls.VersionTLS12, and keep InsecureSkipVerify false and ServerName empty so crypto/tls checks the chain, the dates and the host name.

  • JWT validation in APIs (bearer tokens)CWE-347 · CWE-287

    Verify with jwt.ParseWithClaims, a keyfunc that returns the issuer's public key from configuration, and options: jwt.WithValidMethods([]string{"RS256"}), jwt.WithIssuer, jwt.WithAudience, jwt.WithExpirationRequired and jwt.WithLeeway(30*time.Second).

  • Missing authentication for a critical functionCWE-306 · CWE-287 · CWE-288 · CWE-290

    Make every critical route go through one authentication step that verifies a credential the server issued: here a Bearer token whose HMAC-SHA256 is checked with hmac.Equal.

A08:2025 Software or Data Integrity Failures

  • Mass assignment (over-posting)CWE-915

    Decode the body into an input struct that declares only the fields users may edit (here ProfileUpdate with DisplayName and Bio), never into the stored struct, call DisallowUnknownFields, and copy each field across yourself. encoding/json matches keys to fields case-insensitively, so a blocklist that refuses "role" by its exact name lets "Role" through.

Not covered in Go yet

These weaknesses have fix cards in other stacks but not in this one.

Supported versions

net/http, html/template and database/sql ship with the Go toolchain and get security fixes only in Go minor releases. Each major Go release is supported until there are two newer major releases, and Go has a major release every six months, so Go 1.27 and Go 1.26 get fixes and Go 1.25 no longer does. Go standard library support policy.

Go standard library
VersionLatestStatusReleasedEnd of support
1.271.27.2SupportedSupported until Go 1.29 is released; 1.27.2 (2026-10-08) is a security release.19 August 2026No date set
1.261.26.9SupportedSupported until Go 1.28 is released; 1.26.9 (2026-10-08) is a security release.10 February 2026No date set
1.251.25.14End of lifeSupport ended when Go 1.27 was released, so it did not get the October 2026 fixes.12 August 202519 August 2026
Go toolchain
VersionLatestStatusReleasedEnd of support
1.271.27.2SupportedThe cards run in golang:1.27.2-trixie, pinned by digest.19 August 2026No date set
1.261.26.9SupportedEvery card's tests also pass in golang:1.26.9-trixie.10 February 2026No date set
1.251.25.14End of life12 August 202519 August 2026

The go line in go.mod is a minimum: the cards say go 1.26.0, the version Go 1.27's go mod init writes, so both supported lines build them. With GOTOOLCHAIN=auto, the default in Go's own downloads, the go command fetches a newer toolchain when a module asks for one; the golang images set GOTOOLCHAIN=local and use the Go they ship. Go toolchain release schedule

How the fix cards choose versions

Minimum secure versions

The lowest release in each supported line that fixes every published advisory listed here. Anything older is missing a security fix.

PackageAt leastWhy
Go 1.271.27.215 advisories, among them html/template escaping (GO-2026-6599, GO-2026-6600), HTTP/1 desynchronization after CONNECT (GO-2026-6605, GO-2026-6613), unbounded Range headers (GO-2026-6609) and five HTTP/2 issues (GO-2026-6603 and others). Advisory
Go 1.261.26.9The same 15 fixes; in 1.26 the HTTP/2 ones land in net/http's bundled copy of HTTP/2. Advisory
golang.org/x/net0.60.0The five HTTP/2 fixes above (GO-2026-6603 and others), for programs that import golang.org/x/net/http2 directly. Advisory
github.com/golang-jwt/jwt/v55.2.2Excessive memory allocation while parsing a token's header (GO-2025-3553, CVE-2025-30204). Advisory

Middleware and security order

  1. Wrap your own mux: Middleware is a func(http.Handler) http.Handler, and the outermost wrapper sees the request first. Build the chain around your own http.ServeMux, not http.DefaultServeMux, which any imported package can register handlers on.
  2. Server limits: Serve through an http.Server with ReadHeaderTimeout, ReadTimeout, WriteTimeout and IdleTimeout set; http.ListenAndServe cannot set them.
  3. Body size: http.MaxBytesHandler, or http.MaxBytesReader in each handler, before anything reads a request body.
  4. Cross-origin protection: http.CrossOriginProtection (Go 1.25) around routes that change state, when browsers authenticate with cookies; AddTrustedOrigin for other origins you serve.
  5. Authentication, then authorization: Authentication middleware puts the caller in the request context; each handler checks that the caller may use the object whose ID comes from r.PathValue.
  6. Security headers: net/http adds none to normal responses (http.Error sets only X-Content-Type-Options); set Content-Security-Policy, Strict-Transport-Security and X-Content-Type-Options in a middleware.

Secure defaults and settings

SettingValueWhy
http.Server ReadHeaderTimeout, ReadTimeout, WriteTimeout, IdleTimeoutZero: no timeoutsSet them, or a slow client can hold a connection open indefinitely. gosec G112 reports a server without ReadHeaderTimeout, and G114 the http.ListenAndServe helpers.
http.Server MaxHeaderBytesDefaultMaxHeaderBytes, 1 MBLower it if your API needs less; it does not limit the body.
http.Server MaxHeaderValueCountDefaultMaxHeaderValueCount, 500New in Go 1.27: the number of header values a request may carry.
Request body sizeNo limit, except the 10 MB cap ParseForm puts on URL-encoded formsWrap the body with http.MaxBytesReader before json.NewDecoder(r.Body).
http.Client Timeout, including http.DefaultClient and http.GetZero: no timeoutUse your own http.Client with a Timeout, or a request context with a deadline.
tls.Config MinVersionTLS 1.2Raise it to TLS 1.3 when every client supports it.
http.Cookie Secure, HttpOnly, SameSiteOff and unsetSet all three on session cookies; gosec G124 reports cookies without them.
import _ "net/http/pprof"Registers /debug/pprof/ handlers on http.DefaultServeMuxServe the app from its own ServeMux, and pprof, if at all, on a separate listener bound to localhost.

Escape hatches to search for

Framework APIs that switch a protection off. Each one is sometimes needed; search for them in review and make each use justify itself.

APIRiskInstead
tls.Config{InsecureSkipVerify: true}Any certificate for any host is accepted, so anyone on the network path can read and change the traffic.Your CA in an x509.CertPool set as RootCAs.
template.HTML, template.JS, template.URL, template.HTMLAttrhtml/template treats the value as already safe and skips escaping it.Plain strings, which the template escapes for their context.
text/template for HTMLNo escaping at all.html/template.
fmt.Sprintf or + into db.QueryContextSQL injection.Placeholders and arguments.
exec.Command("sh", "-c", ...)A shell parses the request data.exec.Command(program, args...) with a fixed program.
math/rand or math/rand/v2 for tokensNot a cryptographically secure generator.crypto/rand.Text or crypto/rand.Read.
jwt.ParseUnverified (golang-jwt)Returns the claims without checking the signature.jwt.Parse with jwt.WithValidMethods, jwt.WithIssuer and jwt.WithAudience.
filepath.Join(base, name), http.Dir, os.DirFS with request paths.. in a joined name resolves out of base, and http.Dir and os.DirFS follow symlinks out of the tree.os.OpenRoot and Root.Open, or http.FileServerFS(root.FS()).
httputil.ReverseProxy DirectorDeprecated in Go 1.26: a client can name headers the Director adds as hop-by-hop, and the proxy then removes them.ReverseProxy.Rewrite.

Checklist, mapped to ASVS 5.0.0

  • v5.0.0-15.2.1 Builds use Go 1.27.2 or 1.26.9 or later, and govulncheck reports no reachable vulnerabilities.
  • v5.0.0-15.2.2 The http.Server sets its timeouts, and handlers cap request bodies with http.MaxBytesReader.
  • v5.0.0-1.2.4 Every query keeps its SQL constant and passes values as arguments.
  • v5.0.0-1.2.1 HTML comes from html/template, and every template.HTML conversion is reviewed.
  • v5.0.0-8.2.2 Handlers that take an object ID from r.PathValue check that the caller may use that object.
  • v5.0.0-15.3.3 Request bodies decode into input structs that hold only the fields the caller may set.
  • v5.0.0-5.3.2 Files named in requests are opened through an os.Root.
  • v5.0.0-12.3.2 No tls.Config outside tests sets InsecureSkipVerify.
  • v5.0.0-11.5.1 Tokens and identifiers that must not be guessed come from crypto/rand.
  • v5.0.0-13.4.5 net/http/pprof and other debug handlers are not reachable on the public listener.

References

  1. Go: release history and support policy
  2. Go: security policy
  3. Go: security best practices for Go developers
  4. Go: vulnerability management and govulncheck
  5. Go: toolchains (go line and GOTOOLCHAIN)
  6. Go blog: Routing enhancements for Go 1.22
  7. Go blog: Traversal-resistant file APIs (os.Root)
  8. net/http: Server (timeouts and limits)
  9. net/http: CrossOriginProtection
  10. regexp: linear-time matching guarantee
  11. gosec 2.29.0: rule documentation
  12. Docker Official Image: golang

All fix cards