>_
GolangStepByStep
Software Engineer

Secure Coding

Input validation, secrets, crypto pitfalls, SSRF, common vulnerabilities

# The Airport Checkpoint Analogy

Building a web server is like managing the security at a major international airport.

If an airport simply assumes a person inside the terminal is safe because they "look nice," it opens the door to disaster. Secure coding requires the mindset of Zero Trust. Every piece of input, every API request, and every file upload must be aggressively inspected like baggage going through an X-ray scanner before it is allowed to interact with your core business logic.

In Go, we have remarkably powerful tools built directly into the standard library to enforce this strict checkpoint.

# Level 1: Input Validation & Injection (Beginner)

The number one rule in software development: Never trust client input.

If your application takes a username to look up an account, a naive implementation might use string formatting to build the database query. This leads directly to SQL Injection, the most infamous attack on the internet.

// ❌ DISASTER: String Formatting leaves you vulnerable!
// If userInput is: "tarun'; DROP TABLE users;--"
// The DB literally executes the drop command!
query := fmt.Sprintf("SELECT * FROM users WHERE email = '%s'", userInput)
db.Exec(query)

// ✅ SECURE: Parameterized Queries
// The DB engine is smart. It firmly separates CODE from DATA.
// The crazy string is just treated as the 'literal' name.
db.Exec("SELECT * FROM users WHERE email = $1", userInput)

Always use placeholders ($1 for Postgres, ? for MySQL/SQLite). The Go standard library database/sql automatically sanitizes these values for you securely.

# Level 2: Secrets Management (Intermediate)

Often, developers will hardcode an API key directly into their Go file so it's "easy to access."

// ❌ HIGH RISK: Hardcoding secrets
const StripeAPIKey = "sk_live_1234567890abcdef" 

// When pushed to GitHub, bots will scan this in 3 seconds 
// and immediately drain your bank account.

Code (logic) and Configuration (secrets) must be entirely separated. You should inject secrets into the application environment at startup.

// ✅ SECURE PATTERN: Environment Variables
import "os"

func main() {
    // We load it from the secure Server Environment
    stripeKey := os.Getenv("STRIPE_API_KEY")
    if stripeKey == "" {
        log.Fatal("CRITICAL: Stripe key missing. Refusing to boot!")
    }
    
    StartServer(stripeKey)
}

# Level 3: Crypto Pitfalls (Advanced)

Go provides two random packages: math/rand and crypto/rand. It is a common, brutal mistake to use math/rand for generating secure things like Password Reset Tokens or Session IDs.

math/rand is "Pseudo-Random." It uses a predictable mathematical formula given a seed (often the current time). An attacker can reverse-engineer the seed and predict exactly what your next 100 Reset Tokens will be.

// ❌ INSECURE: Do not use for security
import "math/rand"

token := rand.Int() // Predictable and dangerous!

// ✅ SECURE: Unpredictable Cryptographic Randomness
import "crypto/rand"

func generateSecureToken() []byte {
    token := make([]byte, 32)
    // Draws from deeply unpredictable OS hardware entropy
    _, err := rand.Read(token) 
    if err != nil {
        panic(err)
    }
    return token
}

# Level 4: SSRF - Server-Side Request Forgery (Expert)

Imagine you build a feature that lets users import an Avatar via a URL. Your Go server downloads the image, saves it, and sets it.

// Naive Avatar Downloader
url := r.URL.Query().Get("avatar_url")

// ☠️ We blindly fetch whatever the user told us to!
resp, err := http.Get(url) 

The SSRF Attack

What if the attacker provides the URL: http://169.254.169.254/latest/meta-data/? This is the highly sensitive internal AWS server that hands out temporary admin passwords. Because your Go server is inside the AWS network, the internal AWS system trusts it and hands over the credentials, which your server then happily saves as the attacker's Avatar image!

How to fix SSRF:

  • Validate that the URL belongs to a trusted domain before hitting http.Get().
  • Setup custom http.Transport rules forbidding connections to private/internal IPs like 127.0.0.1 or 169.254.x.x.
  • Never reflect raw fetched network data directly back to the user without filtering it.
practice & review