Frontend Labs
⋯

Part VI — Security-focused Go

A different angle on everything so far: the same language, aimed specifically at not shipping vulnerabilities. Secure coding practices first, then three services that put them into practice — password hashing and JWT issuance, a WAF-style rate-limiting middleware, and a full mutual-TLS setup — the security guide in this Wiki's companion volume, applied to Go's own standard library (crypto/tls, net/http middleware) rather than covered in the abstract.

47. Secure coding practices in Go

47.1 Input validation at the boundary

Validate and normalize every external input (HTTP body, query params, CLI args, file uploads) at the point it enters your system — not deep inside business logic where it's easy to forget a path. Go's explicit error returns make "validate, then return early" natural:

func validateEmail(s string) error {
	if len(s) == 0 || len(s) > 254 {
		return fmt.Errorf("email length invalid")
	}
	if _, err := mail.ParseAddress(s); err != nil {
		return fmt.Errorf("invalid email format: %w", err)
	}
	return nil
}

47.2 SQL injection: always use placeholders

// NEVER — string-formats untrusted input directly into SQL
query := fmt.Sprintf("SELECT * FROM users WHERE email = '%s'", userInput)

// ALWAYS — the driver escapes/parameterizes the value safely
row := db.QueryRow("SELECT * FROM users WHERE email = ?", userInput)

database/sql's placeholder syntax (? for MySQL/SQLite, $1 for Postgres) sends the value separately from the query text — the database driver never treats it as SQL syntax, closing the injection vector entirely. There is no legitimate reason to string-format untrusted input into a query in Go.

47.3 Command injection: avoid the shell entirely

// DANGEROUS if userInput is attacker-controlled — invokes a shell that
// interprets ;, |, `` etc.
exec.Command("sh", "-c", "ping "+userInput)

// SAFE — arguments are passed directly to execve, never interpreted by a shell
exec.Command("ping", "-c", "4", userInput)

exec.Command's variadic arguments go straight to the OS's process-exec call, bypassing shell parsing entirely, as long as you don't explicitly route through sh -c.

47.4 Secrets: never in source, never logged

// Read from environment or a secrets manager, never hardcode
apiKey := os.Getenv("API_KEY")

// Redact before logging anything that might contain a secret
logger.Info("api call", "key_prefix", apiKey[:4]+"...")

Go's go vet and third-party tools (gitleaks, trufflehog) catch committed secrets; the cheaper fix is simply never letting them near source control or structured logs in the first place.

47.5 Real-world example

Chapter 48–50 build three of the most common security-adjacent Go services from scratch: an auth service issuing/verifying JWTs, a WAF-style rate-limiting middleware, and a full mutual-TLS handshake — the same territory covered conceptually in this Wiki's networking/security field guides, here implemented directly in Go.

Applied in the series: the HTTPS guide's SSRF guard blocks internal addresses at connect time, and its Slowloris lab shows why server timeouts are a security control.


Generated from real-life-golang-guide.md · 8 Oct 2026 16:36