Writing secure Go code
Go’s security story is often seen as strong but not automatic: developers highlight that while the language removes many classic “footguns” (like string eval and unsafe deserialization), it’s still easy to introduce issues such as SQL injection, missed error checks, data races, and misconfigured dependencies. Commenters emphasize practical defenses like `gosec`, `govulncheck`, `go vet`, `go test -race`, linters, and keeping Go and its modules updated, noting that Go’s rich standard library and relatively small dependency graphs help in practice. There is ongoing debate over how “memory safe” Go truly is compared to languages like Rust or Java, but most agree that real-world security problems are more often logic and concurrency bugs than low-level memory exploits.
Security tooling for Go
- People highlight
gosec,go vet,go test -race,govulncheck,golangci-lint,capslock, and Semgrep as key tools. govulncheckis praised because it checks whether vulnerable symbols are actually reachable, reducing noise vs CVE-only scanners.golangci-lintand linters in general are seen as important for catching ignored errors and some race patterns.
Is Go a “secure language”?
- Broad agreement: Go is relatively secure compared with C/C++ and similar to other modern, memory-safe languages.
- Common web vulns (SQLi, XSS, authz bugs, DOS) are possible in any language; Go is no exception.
- Go is said to “lack some footguns” (no
eval, no implicit shelling out, safer templates, harder-to-misuse deserialization), but still gives you “knives” to hurt yourself. - Some argue meaningful security differences between modern high-level languages are small; others say ecosystem, tooling, and culture still matter.
Error handling and footguns
- Major debate over Go’s explicit
errorvalues:- Critics: easy to forget to check errors; needs third‑party linters; leads to real security bugs (e.g., ignored permission checks).
- Defenders: idioms and linting mitigate this; most mistakes crash (fail-closed) rather than silently grant access.
- Comparisons drawn to exceptions and sum types in other languages (Java, Rust, Haskell) that make ignoring errors harder.
- Many wish for syntactic sugar (
?‑style chaining), but attempts so far are seen as unsatisfactory or dangerous.
Concurrency, data races, and memory safety
- Go is officially treated as memory-safe but only if there are no data races.
- Discussion of how races on multiword values (interfaces, slices) can break memory safety; contrived demos exist.
- Some argue this is mostly theoretical for security: few or no real-world exploits, and the race detector works well.
- Others say UB from races is inherently concerning, especially as Go adoption and attacker interest grow.
Standard library, dependencies, and ecosystem
- Many praise Go’s rich stdlib (HTTP, crypto, JSON, SQL, RNG, etc.), which reduces dependency sprawl and simplifies upgrades.
- Others argue stdlib stability freezes insecure defaults (e.g.,
tarpath issues, HTTP/TLS timeouts), creating “security traps” that are hard to evolve. - Comparing ecosystems:
- Go is seen as easier to maintain across versions than heavy Java/Spring or JS/Node dependency trees.
- Rust’s smaller stdlib but evolving crates; JS/TS culture criticized for fragile, sprawling dependency graphs.
Language evolution and new pitfalls
- New 3‑clause
forloop semantics in Go 1.22 are called a “footgun” by some; others consider the concern overblown and mostly theoretical so far. - Generics spark mixed reactions:
- Pro: enable reusable containers/iterators and nicer APIs.
- Con: iterator/generic-heavy code is harder to read, raising cognitive load compared with older, simpler Go.