Secure Coding Guide
Input Validation
// Allowlist validation (preferred over blocklist)
func validateEmail(email string) bool {
pattern := `^[a-zA-Z0-9._%+\-]+@[a-zA-Z0-9.\-]+\.[a-zA-Z]{2,}$`
matched, _ := regexp.MatchString(pattern, email)
return matched && len(email) <= 254
}
// Numeric range validation
func validateAge(age int) error {
if age < 0 || age > 150 {
return errors.New("age must be between 0 and 150")
}
return nil
}
// File upload validation
allowedTypes := map[string]bool{
"image/jpeg": true,
"image/png": true,
"image/webp": true,
}
// Always check magic bytes, not just extension or Content-Type
Password Hashing
// Go - bcrypt (recommended)
import "golang.org/x/crypto/bcrypt"
func hashPassword(password string) (string, error) {
bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14)
return string(bytes), err
}
func checkPassword(password, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil
}
// NEVER use:
// md5.Sum([]byte(password))
// sha1.New()
// sha256.Sum256([]byte(password)) // fast hash, unsuitable for passwords
Secrets Management
// NEVER hardcode secrets
// BAD:
apiKey := "sk-live-abc123def456..."
// GOOD: Environment variables
apiKey := os.Getenv("OPENAI_API_KEY")
if apiKey == "" {
log.Fatal("OPENAI_API_KEY not set")
}
// BETTER: Secret manager
// AWS Secrets Manager, HashiCorp Vault, GCP Secret Manager
// .env file (development only, never commit)
// Add to .gitignore:
// .env
// *.pem
// *.key
// credentials.json
HTTP Security Headers
// Go/Gin middleware
func SecurityHeaders() gin.HandlerFunc {
return func(c *gin.Context) {
c.Header("X-Content-Type-Options", "nosniff")
c.Header("X-Frame-Options", "DENY")
c.Header("X-XSS-Protection", "1; mode=block")
c.Header("Referrer-Policy", "strict-origin-when-cross-origin")
c.Header("Permissions-Policy", "geolocation=(), microphone=()")
c.Header("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
c.Header("Content-Security-Policy",
"default-src 'self'; script-src 'self' 'nonce-{nonce}'; style-src 'self' 'unsafe-inline'")
c.Next()
}
}
CSRF Protection
// Synchronizer Token Pattern
// Server generates token, stores in session
csrfToken := generateSecureToken(32) // crypto/rand
// Include in form
// <input type="hidden" name="_csrf" value="">
// Validate on POST
if r.FormValue("_csrf") != session.CSRFToken {
http.Error(w, "Invalid CSRF token", 403)
return
}
// Double Submit Cookie (for APIs)
// Set-Cookie: csrf-token=abc; SameSite=Strict; Secure
// X-CSRF-Token: abc (request header must match cookie)
Secure Coding Principles
| Principle | Description |
|---|---|
| Defense in depth | Multiple security layers; one failure doesn't compromise system |
| Least privilege | Grant minimum permissions needed for each component |
| Fail secure | On error, deny access rather than grant |
| Separation of concerns | Isolate auth, data, business logic |
| Don't trust input | Validate and sanitize all external data |
| Keep it simple | Complex code has more bugs; simple = more secure |