Security Best Practices

Why Security Matters

Think of security like building a house. You wouldn't leave your doors unlocked or windows open when you go on vacation. Yet many developers do exactly this with their applications - leaving digital "doors and windows" wide open for attackers.

Security isn't optionalβ€”one vulnerability can compromise your entire system, leak user data, or cost millions in damages. Go provides strong security features by default, but developers must use them correctly. This guide covers essential security practices for building production-ready Go applications.

Real-World Impact:

Equifax Breach - 147 million records stolen:

1// Vulnerable: Outdated Apache Struts
2// Fix: Regular dependency updates
3go get -u ./...
4go mod tidy

A single unpatched vulnerability led to one of the largest data breaches in history. Cost: $1.4 billion in settlements, massive reputation damage.

Capital One Breach - 100 million records exposed:

1// Vulnerable: SSRF via misconfigured WAF
2// Fix: Proper input validation and least privilege
3if !isValidURL(input) {
4    return errors.New("invalid URL")
5}

A hacker exploited a misconfigured firewall to access sensitive data. Cost: $80 million fine, criminal charges.

Log4Shell - Critical RCE in logging library:

1// Vulnerable: Unsafe deserialization
2// Fix: Input validation and safe parsing
3log.Printf("User: %s", sanitize(userInput))

A simple string in log messages allowed attackers to execute code remotely. Affected: Millions of applications worldwide, months of remediation.

πŸ’‘ Key Takeaway: Security vulnerabilities aren't just technical problems - they're business risks that can destroy customer trust, incur massive costs, and even put companies out of business.

OWASP Top 10 for Go

The OWASP Top 10 represents the most critical web application security risks. Think of this as the "Most Wanted" list for vulnerabilities that attackers actively exploit:

Rank Vulnerability Go Relevance Prevention
A01 Broken Access Control ⭐⭐⭐⭐⭐ Critical Authorization middleware, RBAC
A02 Cryptographic Failures ⭐⭐⭐⭐ High Use crypto/*, avoid MD5/SHA1
A03 Injection ⭐⭐⭐⭐⭐ Critical Parameterized queries, input validation
A04 Insecure Design ⭐⭐⭐ Medium Threat modeling, security reviews
A05 Security Misconfiguration ⭐⭐⭐⭐ High Secure defaults, hardening guides
A06 Vulnerable Components ⭐⭐⭐⭐ High Dependency scanning, updates
A07 Authentication Failures ⭐⭐⭐⭐⭐ Critical MFA, secure session management
A08 Data Integrity Failures ⭐⭐⭐ Medium Signed JWTs, integrity checks
A09 Logging/Monitoring Failures ⭐⭐⭐⭐ High Structured logging, audit trails
A10 SSRF ⭐⭐⭐ Medium URL validation, allowlists

Real-World Examples of OWASP Top 10:

  • A01 Broken Access Control: Facebook's 2018 bug that let users view private photos of anyone
  • A03 Injection: The 2014 TalkTalk breach where SQL injection exposed customer data
  • A07 Authentication Failures: The 2019 Twitter breach where employees were socially engineered

When to Prioritize Security

Think about security like insurance - you hope you never need it, but when you do, you're really glad you have it.

βœ… Security is Critical For:

  • Financial applications - Handle money, PCI compliance, trust is everything
  • Healthcare systems - HIPAA, patient data, lives at stake
  • Authentication services - OAuth, SSO providers, identity theft risks
  • Public APIs - Exposed to internet threats, 24/7 attacks
  • Enterprise software - Corporate data protection, intellectual property
  • IoT/embedded systems - Physical security implications, device tampering

⚠️ Security Still Matters:

  • Internal tools - Insider threats exist
  • Prototypes - May become production code faster than expected
  • Open source - Public scrutiny, reputation damage affects career

Security Decision Tree:

Does your app handle sensitive data?
β”œβ”€ Yes β†’ Does it handle PII?
β”‚   β”œβ”€ Yes β†’ βœ… MAXIMUM SECURITY
β”‚   └─ No β†’ Does it handle auth credentials?
β”‚       β”œβ”€ Yes β†’ βœ… HIGH SECURITY
β”‚       └─ No β†’ βœ… MEDIUM SECURITY
└─ No β†’ Is it exposed to the internet?
    β”œβ”€ Yes β†’ βœ… MEDIUM SECURITY
    └─ No β†’ βœ… BASIC SECURITY

Now let's dive into the most common and dangerous vulnerabilities, starting with input validation - the foundation of application security.

Input Validation and Sanitization

Rule #1 of Security: Never trust user input!

Think of input validation like airport security. They don't just trust that you're safe - they check your bags, scan you, and verify your documents. Your application should be just as careful about every piece of data it receives.

Real-World Consequences of Poor Input Validation:

  • SQL Injection: Attackers can steal or destroy your entire database
  • XSS: Hackers can steal user cookies and session data
  • Command Injection: Attackers can execute system commands on your server
  • Path Traversal: Hackers can access files they shouldn't see

Input Validation Strategies

The key principle is "whitelist over blacklist" - instead of blocking bad input, only allow good input.

  1package main
  2
  3import (
  4    "errors"
  5    "fmt"
  6    "net/mail"
  7    "regexp"
  8    "strconv"
  9    "strings"
 10    "unicode"
 11)
 12
 13// Validator provides input validation
 14type Validator struct {
 15    errors []string
 16}
 17
 18// NewValidator creates a validator
 19func NewValidator() *Validator {
 20    return &Validator{errors: make([]string, 0)}
 21}
 22
 23// AddError adds a validation error
 24func (v *Validator) AddError(msg string) {
 25    v.errors = append(v.errors, msg)
 26}
 27
 28// Valid returns true if no errors
 29func (v *Validator) Valid() bool {
 30    return len(v.errors) == 0
 31}
 32
 33// Errors returns all validation errors
 34func (v *Validator) Errors() []string {
 35    return v.errors
 36}
 37
 38// String validators
 39
 40// ValidateRequired checks if string is not empty
 41func ValidateRequired(v *Validator, field, value string) {
 42    if strings.TrimSpace(value) == "" {
 43        v.AddError(fmt.Sprintf("%s is required", field))
 44    }
 45}
 46
 47// ValidateMinLength checks minimum length
 48func ValidateMinLength(v *Validator, field, value string, min int) {
 49    if len(value) < min {
 50        v.AddError(fmt.Sprintf("%s must be at least %d characters", field, min))
 51    }
 52}
 53
 54// ValidateMaxLength checks maximum length
 55func ValidateMaxLength(v *Validator, field, value string, max int) {
 56    if len(value) > max {
 57        v.AddError(fmt.Sprintf("%s must be at most %d characters", field, max))
 58    }
 59}
 60
 61// ValidateEmail checks if value is valid email
 62func ValidateEmail(v *Validator, field, value string) {
 63    if _, err := mail.ParseAddress(value); err != nil {
 64        v.AddError(fmt.Sprintf("%s is not a valid email", field))
 65    }
 66}
 67
 68// ValidateAlphanumeric checks if only letters and numbers
 69func ValidateAlphanumeric(v *Validator, field, value string) {
 70    for _, r := range value {
 71        if !unicode.IsLetter(r) && !unicode.IsNumber(r) {
 72            v.AddError(fmt.Sprintf("%s must be alphanumeric", field))
 73            return
 74        }
 75    }
 76}
 77
 78// ValidatePattern checks if value matches regex
 79func ValidatePattern(v *Validator, field, value, pattern string) {
 80    matched, err := regexp.MatchString(pattern, value)
 81    if err != nil || !matched {
 82        v.AddError(fmt.Sprintf("%s does not match required pattern", field))
 83    }
 84}
 85
 86// Number validators
 87
 88// ValidateIntRange checks if number is in range
 89func ValidateIntRange(v *Validator, field string, value, min, max int) {
 90    if value < min || value > max {
 91        v.AddError(fmt.Sprintf("%s must be between %d and %d", field, min, max))
 92    }
 93}
 94
 95// ValidatePositive checks if number is positive
 96func ValidatePositive(v *Validator, field string, value int) {
 97    if value <= 0 {
 98        v.AddError(fmt.Sprintf("%s must be positive", field))
 99    }
100}
101
102// Example: User registration validation
103type UserRegistration struct {
104    Username string
105    Email    string
106    Password string
107    Age      int
108}
109
110func (u *UserRegistration) Validate() error {
111    v := NewValidator()
112
113    // Username validation
114    ValidateRequired(v, "username", u.Username)
115    ValidateMinLength(v, "username", u.Username, 3)
116    ValidateMaxLength(v, "username", u.Username, 20)
117    ValidateAlphanumeric(v, "username", u.Username)
118
119    // Email validation
120    ValidateRequired(v, "email", u.Email)
121    ValidateEmail(v, "email", u.Email)
122
123    // Password validation
124    ValidateRequired(v, "password", u.Password)
125    ValidateMinLength(v, "password", u.Password, 8)
126    ValidatePattern(v, "password", u.Password, `[A-Z]`) // At least one uppercase
127    ValidatePattern(v, "password", u.Password, `[a-z]`) // At least one lowercase
128    ValidatePattern(v, "password", u.Password, `[0-9]`) // At least one digit
129
130    // Age validation
131    ValidateIntRange(v, "age", u.Age, 13, 120)
132
133    if !v.Valid() {
134        return fmt.Errorf("validation failed: %s", strings.Join(v.Errors(), ", "))
135    }
136
137    return nil
138}
139
140func main() {
141    // Valid registration
142    user1 := &UserRegistration{
143        Username: "alice123",
144        Email:    "alice@example.com",
145        Password: "SecurePass123",
146        Age:      25,
147    }
148
149    if err := user1.Validate(); err != nil {
150        fmt.Printf("Validation failed: %v\n", err)
151    } else {
152        fmt.Println("User 1: Valid!")
153    }
154
155    // Invalid registration
156    user2 := &UserRegistration{
157        Username: "ab",           // Too short
158        Email:    "invalid",      // Invalid email
159        Password: "weak",         // Too short, no uppercase/digit
160        Age:      10,             // Too young
161    }
162
163    if err := user2.Validate(); err != nil {
164        fmt.Printf("User 2: %v\n", err)
165    }
166}

⚠️ Common Input Validation Mistakes:

  • Client-side only validation: Users can bypass JavaScript validation
  • Blacklisting dangerous characters: Attackers always find creative ways around
  • Trusting headers: HTTP headers can be easily spoofed
  • Not validating file uploads: Attackers can upload malicious files

Advanced Input Validation Patterns

In production systems, you need more sophisticated validation strategies that handle edge cases, internationalization, and business logic constraints.

  1// run
  2package main
  3
  4import (
  5    "errors"
  6    "fmt"
  7    "net"
  8    "net/url"
  9    "path/filepath"
 10    "regexp"
 11    "strings"
 12    "time"
 13)
 14
 15// AdvancedValidator provides production-ready validation
 16type AdvancedValidator struct {
 17    errors map[string][]string
 18}
 19
 20func NewAdvancedValidator() *AdvancedValidator {
 21    return &AdvancedValidator{
 22        errors: make(map[string][]string),
 23    }
 24}
 25
 26func (av *AdvancedValidator) AddError(field, message string) {
 27    av.errors[field] = append(av.errors[field], message)
 28}
 29
 30func (av *AdvancedValidator) HasErrors() bool {
 31    return len(av.errors) > 0
 32}
 33
 34func (av *AdvancedValidator) GetErrors() map[string][]string {
 35    return av.errors
 36}
 37
 38// ValidateURL checks if URL is valid and safe
 39func (av *AdvancedValidator) ValidateURL(field, value string, allowedSchemes []string) {
 40    parsed, err := url.Parse(value)
 41    if err != nil {
 42        av.AddError(field, "invalid URL format")
 43        return
 44    }
 45
 46    // Check scheme
 47    schemeAllowed := false
 48    for _, scheme := range allowedSchemes {
 49        if parsed.Scheme == scheme {
 50            schemeAllowed = true
 51            break
 52        }
 53    }
 54
 55    if !schemeAllowed {
 56        av.AddError(field, fmt.Sprintf("URL scheme must be one of: %v", allowedSchemes))
 57    }
 58
 59    // Prevent SSRF attacks
 60    if parsed.Host == "localhost" || parsed.Host == "127.0.0.1" || strings.HasPrefix(parsed.Host, "192.168.") {
 61        av.AddError(field, "internal URLs are not allowed")
 62    }
 63}
 64
 65// ValidateIP checks if IP address is valid and not private
 66func (av *AdvancedValidator) ValidateIP(field, value string) {
 67    ip := net.ParseIP(value)
 68    if ip == nil {
 69        av.AddError(field, "invalid IP address")
 70        return
 71    }
 72
 73    // Check for private/internal IPs
 74    if ip.IsLoopback() || ip.IsPrivate() {
 75        av.AddError(field, "private IP addresses are not allowed")
 76    }
 77}
 78
 79// ValidateFilePath checks if file path is safe
 80func (av *AdvancedValidator) ValidateFilePath(field, value, baseDir string) {
 81    // Clean the path
 82    cleaned := filepath.Clean(value)
 83
 84    // Check for path traversal
 85    if strings.Contains(cleaned, "..") {
 86        av.AddError(field, "path traversal detected")
 87        return
 88    }
 89
 90    // Ensure path is within base directory
 91    absPath, err := filepath.Abs(cleaned)
 92    if err != nil {
 93        av.AddError(field, "invalid file path")
 94        return
 95    }
 96
 97    absBase, err := filepath.Abs(baseDir)
 98    if err != nil {
 99        av.AddError(field, "invalid base directory")
100        return
101    }
102
103    if !strings.HasPrefix(absPath, absBase) {
104        av.AddError(field, "path must be within base directory")
105    }
106}
107
108// ValidateDate checks if date is valid and within range
109func (av *AdvancedValidator) ValidateDate(field, value, layout string, minDate, maxDate time.Time) {
110    parsed, err := time.Parse(layout, value)
111    if err != nil {
112        av.AddError(field, "invalid date format")
113        return
114    }
115
116    if parsed.Before(minDate) {
117        av.AddError(field, fmt.Sprintf("date must be after %s", minDate.Format(layout)))
118    }
119
120    if parsed.After(maxDate) {
121        av.AddError(field, fmt.Sprintf("date must be before %s", maxDate.Format(layout)))
122    }
123}
124
125// ValidateJSON checks if string is valid JSON
126func (av *AdvancedValidator) ValidateJSON(field, value string) {
127    // Simple check for JSON structure
128    trimmed := strings.TrimSpace(value)
129    if !strings.HasPrefix(trimmed, "{") && !strings.HasPrefix(trimmed, "[") {
130        av.AddError(field, "value must be valid JSON")
131    }
132}
133
134// ValidateEnum checks if value is in allowed set
135func (av *AdvancedValidator) ValidateEnum(field, value string, allowed []string) {
136    for _, a := range allowed {
137        if value == a {
138            return
139        }
140    }
141    av.AddError(field, fmt.Sprintf("value must be one of: %v", allowed))
142}
143
144// Example: API request validation
145type APIRequest struct {
146    Endpoint  string
147    IPAddress string
148    FilePath  string
149    StartDate string
150    Status    string
151    Metadata  string
152}
153
154func (r *APIRequest) Validate() error {
155    validator := NewAdvancedValidator()
156
157    // Validate URL
158    validator.ValidateURL("endpoint", r.Endpoint, []string{"https"})
159
160    // Validate IP
161    validator.ValidateIP("ip_address", r.IPAddress)
162
163    // Validate file path
164    validator.ValidateFilePath("file_path", r.FilePath, "/var/uploads")
165
166    // Validate date
167    minDate := time.Now().AddDate(-1, 0, 0) // 1 year ago
168    maxDate := time.Now().AddDate(1, 0, 0)  // 1 year from now
169    validator.ValidateDate("start_date", r.StartDate, "2006-01-02", minDate, maxDate)
170
171    // Validate enum
172    validator.ValidateEnum("status", r.Status, []string{"pending", "active", "completed", "failed"})
173
174    // Validate JSON
175    validator.ValidateJSON("metadata", r.Metadata)
176
177    if validator.HasErrors() {
178        return fmt.Errorf("validation errors: %v", validator.GetErrors())
179    }
180
181    return nil
182}
183
184func main() {
185    fmt.Println("=== Advanced Input Validation ===\n")
186
187    // Valid request
188    validReq := &APIRequest{
189        Endpoint:  "https://api.example.com/data",
190        IPAddress: "8.8.8.8",
191        FilePath:  "/var/uploads/file.txt",
192        StartDate: time.Now().Format("2006-01-02"),
193        Status:    "active",
194        Metadata:  `{"key": "value"}`,
195    }
196
197    if err := validReq.Validate(); err != nil {
198        fmt.Printf("Valid request failed: %v\n", err)
199    } else {
200        fmt.Println("βœ“ Valid request passed validation")
201    }
202
203    // Invalid request
204    fmt.Println("\n=== Testing Invalid Inputs ===\n")
205
206    invalidReq := &APIRequest{
207        Endpoint:  "http://localhost:8080/admin", // Wrong scheme and localhost
208        IPAddress: "127.0.0.1",                   // Loopback
209        FilePath:  "../../etc/passwd",            // Path traversal
210        StartDate: "2030-01-01",                  // Future date beyond range
211        Status:    "unknown",                     // Invalid enum
212        Metadata:  "not json",                    // Invalid JSON
213    }
214
215    if err := invalidReq.Validate(); err != nil {
216        fmt.Printf("βœ“ Invalid request properly rejected:\n%v\n", err)
217    }
218}

Now let's look at Cross-Site Scripting prevention - one of the most common and dangerous web vulnerabilities.

HTML/JavaScript Sanitization

Cross-Site Scripting is like leaving your front door unlocked with a sign that says "come in and take whatever you want." Attackers can inject malicious JavaScript that steals user data, hijacks sessions, or even takes over user accounts.

Real-World XSS Examples:

  • Twitter: The "onmouseover" worm that spread automatically when users hovered over tweets
  • MySpace: The "Samy" worm that added over 1 million friends in just 20 hours
  • Yahoo: Advertisers exploited XSS to serve malware through legitimate ads

Types of XSS:

  1. Stored XSS: Malicious code stored in database, affects all users who view it
  2. Reflected XSS: Malicious code in URL parameters, executes when link is clicked
  3. DOM-based XSS: Vulnerability exists in client-side JavaScript
  1package main
  2
  3import (
  4    "fmt"
  5    "html"
  6    "html/template"
  7    "os"
  8    "regexp"
  9    "strings"
 10)
 11
 12// Sanitizer provides XSS protection
 13type Sanitizer struct{}
 14
 15// SanitizeHTML escapes HTML special characters
 16func (s *Sanitizer) SanitizeHTML(input string) string {
 17    return html.EscapeString(input)
 18}
 19
 20// SanitizeJS escapes JavaScript special characters
 21func (s *Sanitizer) SanitizeJS(input string) string {
 22    // Escape quotes and backslashes
 23    input = strings.ReplaceAll(input, "\\", "\\\\")
 24    input = strings.ReplaceAll(input, "'", "\\'")
 25    input = strings.ReplaceAll(input, "\"", "\\\"")
 26    input = strings.ReplaceAll(input, "\n", "\\n")
 27    input = strings.ReplaceAll(input, "\r", "\\r")
 28    return input
 29}
 30
 31// StripTags removes all HTML tags
 32func (s *Sanitizer) StripTags(input string) string {
 33    re := regexp.MustCompile(`<[^>]*>`)
 34    return re.ReplaceAllString(input, "")
 35}
 36
 37// AllowSafeTags allows only safe HTML tags
 38func (s *Sanitizer) AllowSafeTags(input string) string {
 39    // Whitelist of safe tags
 40    safeTags := []string{"b", "i", "u", "em", "strong", "p", "br"}
 41
 42    // Remove all tags except safe ones
 43    re := regexp.MustCompile(`</?([a-z]+)[^>]*>`)
 44    return re.ReplaceAllStringFunc(input, func(match string) string {
 45        tagMatch := regexp.MustCompile(`</?([a-z]+)`).FindStringSubmatch(match)
 46        if len(tagMatch) > 1 {
 47            tag := tagMatch[1]
 48            for _, safeTag := range safeTags {
 49                if tag == safeTag {
 50                    return match
 51                }
 52            }
 53        }
 54        return ""
 55    })
 56}
 57
 58// SanitizeURL validates and sanitizes URLs
 59func (s *Sanitizer) SanitizeURL(input string) (string, error) {
 60    // Remove javascript: protocol
 61    if strings.HasPrefix(strings.ToLower(input), "javascript:") {
 62        return "", fmt.Errorf("dangerous URL protocol")
 63    }
 64
 65    // Remove data: protocol
 66    if strings.HasPrefix(strings.ToLower(input), "data:") {
 67        return "", fmt.Errorf("data URLs not allowed")
 68    }
 69
 70    // Allow only http/https
 71    if !strings.HasPrefix(input, "http://") && !strings.HasPrefix(input, "https://") {
 72        return "", fmt.Errorf("only http/https URLs allowed")
 73    }
 74
 75    return input, nil
 76}
 77
 78func main() {
 79    sanitizer := &Sanitizer{}
 80
 81    // XSS attempt
 82    malicious := `<script>alert('XSS')</script>Hello`
 83    safe := sanitizer.SanitizeHTML(malicious)
 84    fmt.Printf("Original: %s\n", malicious)
 85    fmt.Printf("Sanitized: %s\n", safe)
 86
 87    // Strip all tags
 88    htmlInput := `<p>Hello <b>World</b></p>`
 89    stripped := sanitizer.StripTags(htmlInput)
 90    fmt.Printf("Stripped: %s\n", stripped)
 91
 92    // Allow safe tags only
 93    mixedInput := `<p>Safe</p> <script>alert('XSS')</script> <b>Bold</b>`
 94    safeTags := sanitizer.AllowSafeTags(mixedInput)
 95    fmt.Printf("Safe tags only: %s\n", safeTags)
 96
 97    // Template rendering
 98    tmpl := template.Must(template.New("test").Parse(`<div>{{.}}</div>`))
 99    tmpl.Execute(os.Stdout, malicious)
100}

πŸ’‘ XSS Prevention Best Practices:

  • Always escape output: Never trust data coming from users or databases
  • Use Content Security Policy: Adds an extra layer of protection
  • HttpOnly cookies: Prevents JavaScript from accessing sensitive cookies
  • Validate input: Stop malicious input before it enters your system

⚠️ Common XSS Mistakes:

  • Using innerHTML: Instead of textContent for setting element content
  • DOM-based XSS: Assuming client-side code is safe
  • User-generated content: Allowing users to upload HTML/JS files
  • Third-party content: Not sanitizing content from external APIs

Now let's tackle SQL Injection - one of the most devastating vulnerabilities that can compromise your entire database.

SQL Injection Prevention

SQL injection is like giving a stranger the keys to your house and letting them rearrange the furniture however they want. Attackers can manipulate your database queries to steal data, delete records, or even take over your entire database.

Real-World SQL Injection Disasters:

  • TalkTalk: Β£60 million fine after 157,000 customer records stolen
  • Heartland Payment Systems: 130 million credit card numbers exposed
  • Sony Pictures: 77 million accounts compromised, massive data breach
  • Adult FriendFinder: 412 million accounts exposed

How SQL Injection Works:

 1-- Normal query
 2SELECT * FROM users WHERE username = 'alice'
 3
 4-- Malicious input: alice' OR '1'='1
 5-- Becomes: SELECT * FROM users WHERE username = 'alice' OR '1'='1'
 6-- Returns ALL users!
 7
 8-- Even worse: alice'; DROP TABLE users; --
 9-- Becomes: SELECT * FROM users WHERE username = 'alice'; DROP TABLE users; --'
10-- Deletes the entire users table!

Parameterized Queries

Parameterized queries are like having a bouncer at your club who checks IDs carefully. No matter what someone shows them, they only look for the valid format and reject everything else.

  1package main
  2
  3import (
  4    "database/sql"
  5    "fmt"
  6    "log"
  7
  8    _ "github.com/mattn/go-sqlite3"
  9)
 10
 11// Safe database operations
 12type UserDB struct {
 13    db *sql.DB
 14}
 15
 16func NewUserDB(db *sql.DB) *UserDB {
 17    return &UserDB{db: db}
 18}
 19
 20// βœ… SAFE: Parameterized query
 21func (udb *UserDB) GetUserSafe(username string) (*User, error) {
 22    query := "SELECT id, username, email FROM users WHERE username = ?"
 23
 24    var user User
 25    err := udb.db.QueryRow(query, username).Scan(&user.ID, &user.Username, &user.Email)
 26    if err != nil {
 27        return nil, err
 28    }
 29
 30    return &user, nil
 31}
 32
 33// ❌ DANGEROUS: SQL injection vulnerable
 34func (udb *UserDB) GetUserUnsafe(username string) (*User, error) {
 35    // NEVER DO THIS!
 36    query := fmt.Sprintf("SELECT id, username, email FROM users WHERE username = '%s'", username)
 37
 38    var user User
 39    err := udb.db.QueryRow(query).Scan(&user.ID, &user.Username, &user.Email)
 40    if err != nil {
 41        return nil, err
 42    }
 43
 44    return &user, nil
 45}
 46
 47// βœ… SAFE: Multiple parameters
 48func (udb *UserDB) SearchUsersSafe(username, email string, minAge int) ([]User, error) {
 49    query := `
 50        SELECT id, username, email, age
 51        FROM users
 52        WHERE username LIKE ?
 53          AND email LIKE ?
 54          AND age >= ?
 55    `
 56
 57    rows, err := udb.db.Query(query, "%"+username+"%", "%"+email+"%", minAge)
 58    if err != nil {
 59        return nil, err
 60    }
 61    defer rows.Close()
 62
 63    var users []User
 64    for rows.Next() {
 65        var user User
 66        if err := rows.Scan(&user.ID, &user.Username, &user.Email, &user.Age); err != nil {
 67            return nil, err
 68        }
 69        users = append(users, user)
 70    }
 71
 72    return users, nil
 73}
 74
 75// βœ… SAFE: INSERT with parameters
 76func (udb *UserDB) CreateUserSafe(username, email, password string) (int64, error) {
 77    query := "INSERT INTO users (username, email, password) VALUES (?, ?, ?)"
 78
 79    result, err := udb.db.Exec(query, username, email, password)
 80    if err != nil {
 81        return 0, err
 82    }
 83
 84    return result.LastInsertId()
 85}
 86
 87// βœ… SAFE: UPDATE with parameters
 88func (udb *UserDB) UpdateUserSafe(id int, username, email string) error {
 89    query := "UPDATE users SET username = ?, email = ? WHERE id = ?"
 90
 91    _, err := udb.db.Exec(query, username, email, id)
 92    return err
 93}
 94
 95// βœ… SAFE: DELETE with parameters
 96func (udb *UserDB) DeleteUserSafe(id int) error {
 97    query := "DELETE FROM users WHERE id = ?"
 98
 99    _, err := udb.db.Exec(query, id)
100    return err
101}
102
103type User struct {
104    ID       int
105    Username string
106    Email    string
107    Age      int
108}
109
110func main() {
111    // Create in-memory database
112    db, err := sql.Open("sqlite3", ":memory:")
113    if err != nil {
114        log.Fatal(err)
115    }
116    defer db.Close()
117
118    // Create table
119    _, err = db.Exec(`
120        CREATE TABLE users (
121            id INTEGER PRIMARY KEY AUTOINCREMENT,
122            username TEXT NOT NULL,
123            email TEXT NOT NULL,
124            password TEXT NOT NULL,
125            age INTEGER
126        )
127    `)
128    if err != nil {
129        log.Fatal(err)
130    }
131
132    userDB := NewUserDB(db)
133
134    // Insert test users
135    userDB.CreateUserSafe("alice", "alice@example.com", "hash123")
136    userDB.CreateUserSafe("bob", "bob@example.com", "hash456")
137
138    // Safe query
139    user, err := userDB.GetUserSafe("alice")
140    if err != nil {
141        log.Fatal(err)
142    }
143    fmt.Printf("Found user: %+v\n", user)
144
145    // SQL injection attempt
146    maliciousInput := "admin' OR '1'='1"
147    user, err = userDB.GetUserSafe(maliciousInput)
148    if err != nil {
149        fmt.Printf("Injection blocked: %v\n", err) // sql: no rows in result set
150    }
151}

πŸ’‘ SQL Injection Prevention Rules:

  1. NEVER concatenate user input into SQL queries
  2. ALWAYS use parameterized queries
  3. Use ORM libraries when possible
  4. Validate input before it reaches the database
  5. Use least privilege database accounts

⚠️ Common SQL Injection Mistakes:

  • String formatting: Using fmt.Sprintf or + for queries
  • ORM raw queries: Using raw SQL without parameters
  • Dynamic column names: Not validating table/column names
  • Stored procedures: Still vulnerable if not parameterized

Dynamic Queries

When you need dynamic column names or table names, use whitelisting:

  1package main
  2
  3import (
  4    "database/sql"
  5    "errors"
  6    "fmt"
  7    "strings"
  8)
  9
 10// QueryBuilder builds safe dynamic queries
 11type QueryBuilder struct {
 12    allowedTables  map[string]bool
 13    allowedColumns map[string]bool
 14}
 15
 16func NewQueryBuilder() *QueryBuilder {
 17    return &QueryBuilder{
 18        allowedTables: map[string]bool{
 19            "users":    true,
 20            "products": true,
 21            "orders":   true,
 22        },
 23        allowedColumns: map[string]bool{
 24            "id":       true,
 25            "username": true,
 26            "email":    true,
 27            "age":      true,
 28            "name":     true,
 29            "price":    true,
 30        },
 31    }
 32}
 33
 34// ValidateIdentifier checks if identifier is allowed
 35func (qb *QueryBuilder) ValidateIdentifier(identifier string, allowed map[string]bool) error {
 36    if !allowed[identifier] {
 37        return fmt.Errorf("invalid identifier: %s", identifier)
 38    }
 39    return nil
 40}
 41
 42// BuildSelect creates a safe SELECT query
 43func (qb *QueryBuilder) BuildSelect(table string, columns []string, where map[string]interface{}) (string, []interface{}, error) {
 44    // Validate table name
 45    if err := qb.ValidateIdentifier(table, qb.allowedTables); err != nil {
 46        return "", nil, err
 47    }
 48
 49    // Validate column names
 50    for _, col := range columns {
 51        if err := qb.ValidateIdentifier(col, qb.allowedColumns); err != nil {
 52            return "", nil, err
 53        }
 54    }
 55
 56    // Build column list
 57    columnList := strings.Join(columns, ", ")
 58
 59    // Build WHERE clause
 60    var whereClauses []string
 61    var args []interface{}
 62
 63    for col, val := range where {
 64        if err := qb.ValidateIdentifier(col, qb.allowedColumns); err != nil {
 65            return "", nil, err
 66        }
 67        whereClauses = append(whereClauses, fmt.Sprintf("%s = ?", col))
 68        args = append(args, val)
 69    }
 70
 71    whereClause := ""
 72    if len(whereClauses) > 0 {
 73        whereClause = " WHERE " + strings.Join(whereClauses, " AND ")
 74    }
 75
 76    query := fmt.Sprintf("SELECT %s FROM %s%s", columnList, table, whereClause)
 77    return query, args, nil
 78}
 79
 80func main() {
 81    qb := NewQueryBuilder()
 82
 83    // Safe dynamic query
 84    query, args, err := qb.BuildSelect(
 85        "users",
 86        []string{"id", "username", "email"},
 87        map[string]interface{}{
 88            "username": "alice",
 89            "age":      25,
 90        },
 91    )
 92
 93    if err != nil {
 94        fmt.Printf("Error: %v\n", err)
 95    } else {
 96        fmt.Printf("Query: %s\n", query)
 97        fmt.Printf("Args: %v\n", args)
 98    }
 99
100    // Attempt with invalid table
101    _, _, err = qb.BuildSelect(
102        "admin_secrets", // Not in whitelist
103        []string{"password"},
104        nil,
105    )
106    fmt.Printf("Invalid table: %v\n", err)
107}

Authentication and Password Security

Authentication is like the front door to your application. If the lock is weak, attackers can easily break in and steal everything inside. Password security is critical because weak or poorly stored passwords are one of the most common ways attackers gain access.

Real-World Authentication Disasters:

  • LinkedIn: 6.5 million passwords leaked because they used unsalted SHA1 hashes
  • Adobe: 150 million passwords stored in plain text
  • RockYou: 32 million passwords stored in plain text, showed "123456" was the most common password

Password Hashing with bcrypt

Think of password hashing like a one-way meat grinder. You can put meat in and get ground meat out, but you can never reverse the process to get the original meat back. bcrypt is designed to be slow and computationally expensive, making brute force attacks impractical.

Why bcrypt is superior to other methods:

  • MD5/SHA1: Too fast, vulnerable to rainbow tables and GPU cracking
  • Plain text: Obviously terrible - anyone who sees the database has all passwords
  • bcrypt: Slow, includes salt, resistant to GPU/ASIC attacks
 1package main
 2
 3import (
 4    "errors"
 5    "fmt"
 6
 7    "golang.org/x/crypto/bcrypt"
 8)
 9
10// AuthService handles authentication
11type AuthService struct {
12    cost int // bcrypt cost
13}
14
15func NewAuthService() *AuthService {
16    return &AuthService{cost: 12}
17}
18
19// HashPassword securely hashes a password
20func (as *AuthService) HashPassword(password string) (string, error) {
21    // Validate password strength
22    if len(password) < 8 {
23        return "", errors.New("password too short")
24    }
25
26    // Hash with bcrypt
27    hash, err := bcrypt.GenerateFromPassword([]byte(password), as.cost)
28    if err != nil {
29        return "", err
30    }
31
32    return string(hash), nil
33}
34
35// VerifyPassword checks if password matches hash
36func (as *AuthService) VerifyPassword(hash, password string) error {
37    return bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
38}
39
40// βœ… SAFE: Registration
41func (as *AuthService) Register(username, password string) error {
42    // Hash password
43    hash, err := as.HashPassword(password)
44    if err != nil {
45        return err
46    }
47
48    // Store hash in database
49    fmt.Printf("Storing user %s with hash: %s\n", username, hash[:20]+"...")
50
51    return nil
52}
53
54// βœ… SAFE: Login
55func (as *AuthService) Login(username, password string) error {
56    // Retrieve hash from database
57    storedHash := "$2a$12$..." // From database
58
59    // Verify password
60    if err := as.VerifyPassword(storedHash, password); err != nil {
61        return errors.New("invalid credentials")
62    }
63
64    fmt.Printf("User %s logged in successfully\n", username)
65    return nil
66}
67
68func main() {
69    auth := NewAuthService()
70
71    // Register user
72    password := "SecurePassword123"
73    hash, err := auth.HashPassword(password)
74    if err != nil {
75        panic(err)
76    }
77
78    fmt.Printf("Password: %s\n", password)
79    fmt.Printf("Hash: %s\n", hash)
80
81    // Verify correct password
82    if err := auth.VerifyPassword(hash, password); err != nil {
83        fmt.Println("Verification failed")
84    } else {
85        fmt.Println("Password verified!")
86    }
87
88    // Verify incorrect password
89    if err := auth.VerifyPassword(hash, "WrongPassword"); err != nil {
90        fmt.Println("Wrong password rejected")
91    }
92}

πŸ’‘ Password Security Best Practices:

  1. Use bcrypt with cost factor 10-12 for production
  2. Never store plain text passwords - ever!
  3. Use unique salts (bcrypt handles this automatically)
  4. Implement rate limiting to prevent brute force attacks
  5. Require strong passwords

⚠️ Common Password Mistakes:

  • Weak algorithms: MD5, SHA1, or custom hashing functions
  • No salt: Same password = same hash
  • Low cost factors: bcrypt cost < 10 is too fast for modern hardware
  • Plain text storage: Shockingly common in legacy systems
  • Password complexity rules: "Password123!" meets complexity but is still weak

Now let's explore token-based authentication with JWTs, which is essential for modern APIs and microservices.

JWT Authentication

  1package main
  2
  3import (
  4    "errors"
  5    "fmt"
  6    "time"
  7
  8    "github.com/golang-jwt/jwt/v5"
  9)
 10
 11// JWTService handles JWT tokens
 12type JWTService struct {
 13    secret []byte
 14    issuer string
 15}
 16
 17func NewJWTService(secret, issuer string) *JWTService {
 18    return &JWTService{
 19        secret: []byte(secret),
 20        issuer: issuer,
 21    }
 22}
 23
 24// Claims represents JWT claims
 25type Claims struct {
 26    UserID   int    `json:"user_id"`
 27    Username string `json:"username"`
 28    Role     string `json:"role"`
 29    jwt.RegisteredClaims
 30}
 31
 32// GenerateToken creates a new JWT token
 33func (js *JWTService) GenerateToken(userID int, username, role string) (string, error) {
 34    claims := &Claims{
 35        UserID:   userID,
 36        Username: username,
 37        Role:     role,
 38        RegisteredClaims: jwt.RegisteredClaims{
 39            ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
 40            IssuedAt:  jwt.NewNumericDate(time.Now()),
 41            NotBefore: jwt.NewNumericDate(time.Now()),
 42            Issuer:    js.issuer,
 43        },
 44    }
 45
 46    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
 47    return token.SignedString(js.secret)
 48}
 49
 50// ValidateToken verifies and parses a JWT token
 51func (js *JWTService) ValidateToken(tokenString string) (*Claims, error) {
 52    token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
 53        // Verify signing method
 54        if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
 55            return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
 56        }
 57        return js.secret, nil
 58    })
 59
 60    if err != nil {
 61        return nil, err
 62    }
 63
 64    if claims, ok := token.Claims.(*Claims); ok && token.Valid {
 65        return claims, nil
 66    }
 67
 68    return nil, errors.New("invalid token")
 69}
 70
 71// RefreshToken generates a new token from an existing valid token
 72func (js *JWTService) RefreshToken(tokenString string) (string, error) {
 73    claims, err := js.ValidateToken(tokenString)
 74    if err != nil {
 75        return "", err
 76    }
 77
 78    // Generate new token with same claims but new expiry
 79    return js.GenerateToken(claims.UserID, claims.Username, claims.Role)
 80}
 81
 82func main() {
 83    jwtService := NewJWTService("your-secret-key", "myapp.com")
 84
 85    // Generate token
 86    token, err := jwtService.GenerateToken(1, "alice", "admin")
 87    if err != nil {
 88        panic(err)
 89    }
 90
 91    fmt.Printf("Token: %s\n", token)
 92
 93    // Validate token
 94    claims, err := jwtService.ValidateToken(token)
 95    if err != nil {
 96        panic(err)
 97    }
 98
 99    fmt.Printf("Claims: UserID=%d, Username=%s, Role=%s\n",
100        claims.UserID, claims.Username, claims.Role)
101
102    // Refresh token
103    newToken, err := jwtService.RefreshToken(token)
104    if err != nil {
105        panic(err)
106    }
107
108    fmt.Printf("Refreshed token: %s\n", newToken[:50]+"...")
109}

Secrets Management

NEVER hardcode secrets in source code!

Environment Variables

 1package main
 2
 3import (
 4    "fmt"
 5    "log"
 6    "os"
 7
 8    "github.com/joho/godotenv"
 9)
10
11// Config holds application configuration
12type Config struct {
13    DatabaseURL string
14    APIKey      string
15    JWTSecret   string
16    Environment string
17}
18
19// LoadConfig loads configuration from environment
20func LoadConfig() (*Config, error) {
21    // Load .env file in development
22    if os.Getenv("ENVIRONMENT") != "production" {
23        if err := godotenv.Load(); err != nil {
24            log.Println("No .env file found")
25        }
26    }
27
28    config := &Config{
29        DatabaseURL: os.Getenv("DATABASE_URL"),
30        APIKey:      os.Getenv("API_KEY"),
31        JWTSecret:   os.Getenv("JWT_SECRET"),
32        Environment: os.Getenv("ENVIRONMENT"),
33    }
34
35    // Validate required secrets
36    if config.DatabaseURL == "" {
37        return nil, fmt.Errorf("DATABASE_URL is required")
38    }
39    if config.JWTSecret == "" {
40        return nil, fmt.Errorf("JWT_SECRET is required")
41    }
42
43    return config, nil
44}
45
46func main() {
47    config, err := LoadConfig()
48    if err != nil {
49        log.Fatal(err)
50    }
51
52    fmt.Printf("Environment: %s\n", config.Environment)
53    fmt.Printf("Database URL: %s\n", maskSecret(config.DatabaseURL))
54    fmt.Printf("JWT Secret: %s\n", maskSecret(config.JWTSecret))
55}
56
57func maskSecret(secret string) string {
58    if len(secret) <= 4 {
59        return "****"
60    }
61    return secret[:4] + "****"
62}

.env file:

DATABASE_URL=postgres://user:pass@localhost/db
API_KEY=sk_live_abcdef123456
JWT_SECRET=super-secret-key-change-me
ENVIRONMENT=development

.gitignore:

.env
.env.local
.env.production
*.pem
*.key
secrets/

HashiCorp Vault Integration

 1package main
 2
 3import (
 4    "context"
 5    "fmt"
 6    "log"
 7
 8    vault "github.com/hashicorp/vault/api"
 9)
10
11// VaultClient wraps Vault API
12type VaultClient struct {
13    client *vault.Client
14}
15
16// NewVaultClient creates a Vault client
17func NewVaultClient(address, token string) (*VaultClient, error) {
18    config := vault.DefaultConfig()
19    config.Address = address
20
21    client, err := vault.NewClient(config)
22    if err != nil {
23        return nil, err
24    }
25
26    client.SetToken(token)
27
28    return &VaultClient{client: client}, nil
29}
30
31// GetSecret retrieves a secret from Vault
32func (vc *VaultClient) GetSecret(path, key string) (string, error) {
33    secret, err := vc.client.Logical().Read(path)
34    if err != nil {
35        return "", err
36    }
37
38    if secret == nil {
39        return "", fmt.Errorf("secret not found: %s", path)
40    }
41
42    value, ok := secret.Data[key].(string)
43    if !ok {
44        return "", fmt.Errorf("key not found: %s", key)
45    }
46
47    return value, nil
48}
49
50// SetSecret stores a secret in Vault
51func (vc *VaultClient) SetSecret(path string, data map[string]interface{}) error {
52    _, err := vc.client.Logical().Write(path, data)
53    return err
54}
55
56func main() {
57    // Connect to Vault
58    vaultClient, err := NewVaultClient("http://localhost:8200", "root-token")
59    if err != nil {
60        log.Fatal(err)
61    }
62
63    // Store secret
64    err = vaultClient.SetSecret("secret/myapp/db", map[string]interface{}{
65        "username": "dbuser",
66        "password": "dbpass123",
67    })
68    if err != nil {
69        log.Fatal(err)
70    }
71
72    // Retrieve secret
73    password, err := vaultClient.GetSecret("secret/myapp/db", "password")
74    if err != nil {
75        log.Fatal(err)
76    }
77
78    fmt.Printf("Retrieved password: %s\n", maskSecret(password))
79}

Security Headers

Essential HTTP security headers for web applications:

 1package main
 2
 3import (
 4    "net/http"
 5)
 6
 7// SecurityHeaders middleware adds security headers
 8func SecurityHeaders(next http.Handler) http.Handler {
 9    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
10        // Prevent clickjacking
11        w.Header().Set("X-Frame-Options", "DENY")
12
13        // XSS protection
14        w.Header().Set("X-XSS-Protection", "1; mode=block")
15
16        // Content type sniffing protection
17        w.Header().Set("X-Content-Type-Options", "nosniff")
18
19        // Referrer policy
20        w.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin")
21
22        // Content Security Policy
23        csp := "default-src 'self'; " +
24            "script-src 'self' 'unsafe-inline' 'unsafe-eval'; " +
25            "style-src 'self' 'unsafe-inline'; " +
26            "img-src 'self' data: https:; " +
27            "font-src 'self'; " +
28            "connect-src 'self'; " +
29            "frame-ancestors 'none'"
30        w.Header().Set("Content-Security-Policy", csp)
31
32        // HSTS
33        w.Header().Set("Strict-Transport-Security", "max-age=31536000; includeSubDomains")
34
35        // Permissions policy
36        w.Header().Set("Permissions-Policy", "geolocation=(), microphone=(), camera=()")
37
38        next.ServeHTTP(w, r)
39    })
40}
41
42// CORS middleware
43func CORS(allowedOrigins []string) func(http.Handler) http.Handler {
44    return func(next http.Handler) http.Handler {
45        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
46            origin := r.Header.Get("Origin")
47
48            // Check if origin is allowed
49            allowed := false
50            for _, allowedOrigin := range allowedOrigins {
51                if origin == allowedOrigin {
52                    allowed = true
53                    break
54                }
55            }
56
57            if allowed {
58                w.Header().Set("Access-Control-Allow-Origin", origin)
59                w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
60                w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
61                w.Header().Set("Access-Control-Max-Age", "3600")
62            }
63
64            // Handle preflight
65            if r.Method == "OPTIONS" {
66                w.WriteHeader(http.StatusNoContent)
67                return
68            }
69
70            next.ServeHTTP(w, r)
71        })
72    }
73}
74
75func main() {
76    mux := http.NewServeMux()
77
78    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
79        w.Write([]byte("Secure response"))
80    })
81
82    // Apply security middleware
83    handler := SecurityHeaders(mux)
84    handler = CORS([]string{"https://example.com"})(handler)
85
86    http.ListenAndServe(":8080", handler)
87}

Threat Modeling

Threat modeling is the process of identifying potential security threats before they become real vulnerabilities. Think of it like a security review before launching a product.

STRIDE Framework

STRIDE is a widely-used threat modeling framework developed by Microsoft:

  • Spoofing - Impersonating someone or something
  • Tampering - Modifying data or code
  • Repudiation - Claiming not to have performed an action
  • Information Disclosure - Exposing information to unauthorized parties
  • Denial of Service - Preventing legitimate users from accessing the system
  • Elevation of Privilege - Gaining unauthorized capabilities
  1// run
  2package main
  3
  4import (
  5    "fmt"
  6    "strings"
  7)
  8
  9// ThreatCategory represents a STRIDE category
 10type ThreatCategory string
 11
 12const (
 13    Spoofing             ThreatCategory = "Spoofing"
 14    Tampering            ThreatCategory = "Tampering"
 15    Repudiation          ThreatCategory = "Repudiation"
 16    InformationDisclosure ThreatCategory = "Information Disclosure"
 17    DenialOfService      ThreatCategory = "Denial of Service"
 18    ElevationOfPrivilege ThreatCategory = "Elevation of Privilege"
 19)
 20
 21// Threat represents a security threat
 22type Threat struct {
 23    Category    ThreatCategory
 24    Description string
 25    Impact      string
 26    Likelihood  string
 27    Mitigation  string
 28}
 29
 30// ThreatModel represents a complete threat model
 31type ThreatModel struct {
 32    Component string
 33    Threats   []Threat
 34}
 35
 36// NewThreatModel creates a new threat model
 37func NewThreatModel(component string) *ThreatModel {
 38    return &ThreatModel{
 39        Component: component,
 40        Threats:   make([]Threat, 0),
 41    }
 42}
 43
 44// AddThreat adds a threat to the model
 45func (tm *ThreatModel) AddThreat(threat Threat) {
 46    tm.Threats = append(tm.Threats, threat)
 47}
 48
 49// GenerateReport generates a threat model report
 50func (tm *ThreatModel) GenerateReport() string {
 51    var report strings.Builder
 52
 53    report.WriteString(fmt.Sprintf("=== Threat Model: %s ===\n\n", tm.Component))
 54
 55    for i, threat := range tm.Threats {
 56        report.WriteString(fmt.Sprintf("Threat #%d: %s\n", i+1, threat.Category))
 57        report.WriteString(fmt.Sprintf("Description: %s\n", threat.Description))
 58        report.WriteString(fmt.Sprintf("Impact: %s\n", threat.Impact))
 59        report.WriteString(fmt.Sprintf("Likelihood: %s\n", threat.Likelihood))
 60        report.WriteString(fmt.Sprintf("Mitigation: %s\n", threat.Mitigation))
 61        report.WriteString("\n")
 62    }
 63
 64    return report.String()
 65}
 66
 67func main() {
 68    // Example: User Authentication System
 69    authModel := NewThreatModel("User Authentication System")
 70
 71    // Spoofing threats
 72    authModel.AddThreat(Threat{
 73        Category:    Spoofing,
 74        Description: "Attacker impersonates legitimate user with stolen credentials",
 75        Impact:      "High - Unauthorized access to user account",
 76        Likelihood:  "Medium - Phishing and credential stuffing attacks common",
 77        Mitigation:  "Implement MFA, monitor for anomalous login patterns, use strong password policies",
 78    })
 79
 80    // Tampering threats
 81    authModel.AddThreat(Threat{
 82        Category:    Tampering,
 83        Description: "JWT token modified to elevate privileges",
 84        Impact:      "Critical - Attacker gains admin access",
 85        Likelihood:  "Low - Requires knowledge of signing algorithm",
 86        Mitigation:  "Use signed JWTs with strong secrets, validate signature on every request",
 87    })
 88
 89    // Repudiation threats
 90    authModel.AddThreat(Threat{
 91        Category:    Repudiation,
 92        Description: "User denies performing malicious actions",
 93        Impact:      "Medium - Unable to trace security incidents",
 94        Likelihood:  "Medium - Common in insider threat scenarios",
 95        Mitigation:  "Implement comprehensive audit logging with tamper-proof storage",
 96    })
 97
 98    // Information Disclosure threats
 99    authModel.AddThreat(Threat{
100        Category:    InformationDisclosure,
101        Description: "Password hashes exposed through database breach",
102        Impact:      "High - User passwords may be cracked",
103        Likelihood:  "Medium - Database breaches occur regularly",
104        Mitigation:  "Use bcrypt with high cost factor, implement encryption at rest",
105    })
106
107    // Denial of Service threats
108    authModel.AddThreat(Threat{
109        Category:    DenialOfService,
110        Description: "Brute force attack overwhelms authentication service",
111        Impact:      "High - Legitimate users unable to login",
112        Likelihood:  "High - Automated tools make brute force easy",
113        Mitigation:  "Implement rate limiting, account lockout after failed attempts, CAPTCHA",
114    })
115
116    // Elevation of Privilege threats
117    authModel.AddThreat(Threat{
118        Category:    ElevationOfPrivilege,
119        Description: "Regular user exploits vulnerability to gain admin access",
120        Impact:      "Critical - Full system compromise",
121        Likelihood:  "Low - Requires finding specific vulnerability",
122        Mitigation:  "Implement principle of least privilege, regular security audits, input validation",
123    })
124
125    // Generate and print report
126    fmt.Println(authModel.GenerateReport())
127
128    // Print summary statistics
129    fmt.Printf("Total threats identified: %d\n", len(authModel.Threats))
130
131    criticalCount := 0
132    for _, threat := range authModel.Threats {
133        if strings.Contains(threat.Impact, "Critical") {
134            criticalCount++
135        }
136    }
137    fmt.Printf("Critical threats: %d\n", criticalCount)
138}

Security Testing

Security testing should be integrated into your development workflow, not treated as an afterthought.

Static Analysis with gosec

1# Install gosec
2go install github.com/securego/gosec/v2/cmd/gosec@latest
3
4# Run security scan
5gosec ./...
6
7# Generate report
8gosec -fmt=json -out=security-report.json ./...

Dependency Scanning

1# Install nancy
2go install github.com/sonatype-nexus-community/nancy@latest
3
4# Scan dependencies
5go list -json -m all | nancy sleuth
6
7# Check for known vulnerabilities
8go mod download
9nancy sleuth

Penetration Testing Checklist

Before Production:

  • Run automated security scanners (gosec, nancy)
  • Review all authentication and authorization code
  • Test all input validation thoroughly
  • Verify secrets are not in source code
  • Check security headers are properly set
  • Test rate limiting on all endpoints
  • Review error messages for information leakage
  • Verify encryption is used for sensitive data
  • Test session management and timeout
  • Perform SQL injection testing
  • Test XSS vulnerabilities
  • Verify CSRF protection
  • Check for path traversal vulnerabilities
  • Test file upload security
  • Verify API authentication
  • Test privilege escalation scenarios

Compliance and Standards

Different industries have different compliance requirements:

PCI DSS (Payment Card Industry)

Key requirements:

  • Encrypt transmission of cardholder data
  • Maintain vulnerability management program
  • Implement strong access control measures
  • Regularly monitor and test networks
  • Maintain information security policy
  1// run
  2package main
  3
  4import (
  5    "crypto/aes"
  6    "crypto/cipher"
  7    "crypto/rand"
  8    "encoding/base64"
  9    "fmt"
 10    "io"
 11)
 12
 13// CreditCardEncryption handles PCI DSS compliant encryption
 14type CreditCardEncryption struct {
 15    key []byte
 16}
 17
 18func NewCreditCardEncryption(key []byte) *CreditCardEncryption {
 19    return &CreditCardEncryption{key: key}
 20}
 21
 22// Encrypt encrypts credit card data
 23func (cce *CreditCardEncryption) Encrypt(plaintext string) (string, error) {
 24    block, err := aes.NewCipher(cce.key)
 25    if err != nil {
 26        return "", err
 27    }
 28
 29    // Generate random IV
 30    ciphertext := make([]byte, aes.BlockSize+len(plaintext))
 31    iv := ciphertext[:aes.BlockSize]
 32    if _, err := io.ReadFull(rand.Reader, iv); err != nil {
 33        return "", err
 34    }
 35
 36    stream := cipher.NewCFBEncrypter(block, iv)
 37    stream.XORKeyStream(ciphertext[aes.BlockSize:], []byte(plaintext))
 38
 39    return base64.URLEncoding.EncodeToString(ciphertext), nil
 40}
 41
 42// Decrypt decrypts credit card data
 43func (cce *CreditCardEncryption) Decrypt(ciphertext string) (string, error) {
 44    data, err := base64.URLEncoding.DecodeString(ciphertext)
 45    if err != nil {
 46        return "", err
 47    }
 48
 49    block, err := aes.NewCipher(cce.key)
 50    if err != nil {
 51        return "", err
 52    }
 53
 54    if len(data) < aes.BlockSize {
 55        return "", fmt.Errorf("ciphertext too short")
 56    }
 57
 58    iv := data[:aes.BlockSize]
 59    data = data[aes.BlockSize:]
 60
 61    stream := cipher.NewCFBDecrypter(block, iv)
 62    stream.XORKeyStream(data, data)
 63
 64    return string(data), nil
 65}
 66
 67// MaskCardNumber masks credit card number for display
 68func MaskCardNumber(cardNumber string) string {
 69    if len(cardNumber) < 4 {
 70        return "****"
 71    }
 72    return "************" + cardNumber[len(cardNumber)-4:]
 73}
 74
 75func main() {
 76    fmt.Println("=== PCI DSS Compliant Credit Card Handling ===\n")
 77
 78    // Generate or load encryption key (must be 32 bytes for AES-256)
 79    key := make([]byte, 32)
 80    if _, err := rand.Read(key); err != nil {
 81        panic(err)
 82    }
 83
 84    cce := NewCreditCardEncryption(key)
 85
 86    // Credit card number (example)
 87    cardNumber := "4532015112830366"
 88
 89    // Encrypt for storage
 90    encrypted, err := cce.Encrypt(cardNumber)
 91    if err != nil {
 92        panic(err)
 93    }
 94
 95    fmt.Printf("Original: %s\n", cardNumber)
 96    fmt.Printf("Encrypted: %s\n", encrypted[:20]+"...")
 97    fmt.Printf("Masked for display: %s\n", MaskCardNumber(cardNumber))
 98
 99    // Decrypt when needed
100    decrypted, err := cce.Decrypt(encrypted)
101    if err != nil {
102        panic(err)
103    }
104
105    fmt.Printf("Decrypted: %s\n", decrypted)
106    fmt.Printf("\nβœ“ Credit card data encrypted with AES-256\n")
107    fmt.Printf("βœ“ Only last 4 digits shown to users\n")
108    fmt.Printf("βœ“ Encryption key stored securely (not in code)\n")
109}

GDPR (General Data Protection Regulation)

Key principles:

  • Lawfulness, fairness, and transparency
  • Purpose limitation
  • Data minimization
  • Accuracy
  • Storage limitation
  • Integrity and confidentiality
  • Accountability
  1// run
  2package main
  3
  4import (
  5    "crypto/sha256"
  6    "encoding/hex"
  7    "fmt"
  8    "time"
  9)
 10
 11// PersonalData represents GDPR-regulated personal data
 12type PersonalData struct {
 13    UserID    string
 14    Email     string
 15    Name      string
 16    Address   string
 17    Phone     string
 18    CreatedAt time.Time
 19    ConsentGiven bool
 20    ConsentDate  time.Time
 21}
 22
 23// GDPRCompliance handles GDPR requirements
 24type GDPRCompliance struct {
 25    retentionPeriod time.Duration
 26}
 27
 28func NewGDPRCompliance(retentionDays int) *GDPRCompliance {
 29    return &GDPRCompliance{
 30        retentionPeriod: time.Duration(retentionDays) * 24 * time.Hour,
 31    }
 32}
 33
 34// AnonymizeData pseudonymizes personal data
 35func (gc *GDPRCompliance) AnonymizeData(data *PersonalData) *PersonalData {
 36    // Hash identifiable information
 37    emailHash := sha256.Sum256([]byte(data.Email))
 38    phoneHash := sha256.Sum256([]byte(data.Phone))
 39
 40    return &PersonalData{
 41        UserID:       data.UserID,
 42        Email:        hex.EncodeToString(emailHash[:]),
 43        Name:         "REDACTED",
 44        Address:      "REDACTED",
 45        Phone:        hex.EncodeToString(phoneHash[:]),
 46        CreatedAt:    data.CreatedAt,
 47        ConsentGiven: data.ConsentGiven,
 48        ConsentDate:  data.ConsentDate,
 49    }
 50}
 51
 52// ShouldDelete checks if data should be deleted per retention policy
 53func (gc *GDPRCompliance) ShouldDelete(data *PersonalData) bool {
 54    return time.Since(data.CreatedAt) > gc.retentionPeriod
 55}
 56
 57// RightToErasure implements GDPR right to be forgotten
 58func (gc *GDPRCompliance) RightToErasure(userID string) error {
 59    fmt.Printf("Deleting all personal data for user %s\n", userID)
 60    // In production: delete from all systems, databases, backups
 61    return nil
 62}
 63
 64// DataPortability exports user data per GDPR requirements
 65func (gc *GDPRCompliance) DataPortability(userID string) (map[string]interface{}, error) {
 66    // Export all user data in machine-readable format
 67    export := map[string]interface{}{
 68        "user_id": userID,
 69        "email":   "user@example.com",
 70        "name":    "John Doe",
 71        "created_at": time.Now(),
 72        "data": map[string]interface{}{
 73            "preferences": "...",
 74            "history":     "...",
 75        },
 76    }
 77    return export, nil
 78}
 79
 80func main() {
 81    fmt.Println("=== GDPR Compliance System ===\n")
 82
 83    gdpr := NewGDPRCompliance(30) // 30-day retention
 84
 85    // Original personal data
 86    userData := &PersonalData{
 87        UserID:       "user123",
 88        Email:        "john@example.com",
 89        Name:         "John Doe",
 90        Address:      "123 Main St",
 91        Phone:        "+1234567890",
 92        CreatedAt:    time.Now().AddDate(0, -2, 0), // 2 months ago
 93        ConsentGiven: true,
 94        ConsentDate:  time.Now().AddDate(0, -2, 0),
 95    }
 96
 97    fmt.Println("Original Data:")
 98    fmt.Printf("Email: %s\n", userData.Email)
 99    fmt.Printf("Name: %s\n", userData.Name)
100    fmt.Printf("Phone: %s\n\n", userData.Phone)
101
102    // Anonymize data
103    anonymized := gdpr.AnonymizeData(userData)
104    fmt.Println("Anonymized Data:")
105    fmt.Printf("Email Hash: %s\n", anonymized.Email[:20]+"...")
106    fmt.Printf("Name: %s\n", anonymized.Name)
107    fmt.Printf("Phone Hash: %s\n\n", anonymized.Phone[:20]+"...")
108
109    // Check retention policy
110    if gdpr.ShouldDelete(userData) {
111        fmt.Println("βœ“ Data exceeds retention period - should be deleted")
112    }
113
114    // Export user data
115    export, _ := gdpr.DataPortability(userData.UserID)
116    fmt.Printf("\nβœ“ Data export available for user: %v\n", export["user_id"])
117
118    // Right to erasure
119    gdpr.RightToErasure(userData.UserID)
120    fmt.Println("βœ“ User data deleted per GDPR right to erasure")
121}

HIPAA (Healthcare)

Key requirements:

  • Protect electronic health information
  • Implement physical safeguards
  • Implement technical safeguards
  • Ensure confidentiality, integrity, and availability
  • Audit and monitor access

Further Reading

Standards

Go-Specific

Tools

  • gosec - Go security scanner
  • nancy - Dependency vulnerability scanner

Practice Exercises

Exercise 1: Build a Rate Limiter

Learning Objective: Design and implement robust rate limiting systems to protect APIs from brute-force attacks and abuse.

Context: Rate limiting is essential for protecting APIs from abuse and ensuring fair usage. Major breaches like the 2016 Dyn DDoS attack were exacerbated by insufficient rate limiting, causing widespread internet outages. Companies like Cloudflare process billions of requests daily through sophisticated rate limiting systems.

Difficulty: Intermediate | Time: 20-25 minutes

Implement a comprehensive rate limiting system to prevent brute-force attacks and protect API endpoints:

  • Design token bucket algorithm with configurable rates
  • Implement sliding window counters for precise rate control
  • Handle IPv4 and IPv6 addresses correctly
  • Support multiple rate limits per user/IP
  • Include proper cleanup and memory management
  • Provide metrics for monitoring rate limit effectiveness

Real-world application: This pattern protects login endpoints, API gateways, and public services from credential stuffing attacks, DDoS attempts, and resource abuse.

Solution
  1package main
  2
  3import (
  4    "fmt"
  5    "net/http"
  6    "sync"
  7    "time"
  8)
  9
 10// RateLimiter implements token bucket rate limiting
 11type RateLimiter struct {
 12    mu       sync.Mutex
 13    buckets  map[string]*bucket
 14    rate     int           // requests per window
 15    window   time.Duration
 16    cleanup  time.Duration
 17}
 18
 19type bucket struct {
 20    tokens     int
 21    lastRefill time.Time
 22}
 23
 24func NewRateLimiter(rate int, window time.Duration) *RateLimiter {
 25    rl := &RateLimiter{
 26        buckets: make(map[string]*bucket),
 27        rate:    rate,
 28        window:  window,
 29        cleanup: time.Minute,
 30    }
 31
 32    go rl.cleanupBuckets()
 33    return rl
 34}
 35
 36func (rl *RateLimiter) Allow(key string) bool {
 37    rl.mu.Lock()
 38    defer rl.mu.Unlock()
 39
 40    b, exists := rl.buckets[key]
 41    if !exists {
 42        b = &bucket{
 43            tokens:     rl.rate,
 44            lastRefill: time.Now(),
 45        }
 46        rl.buckets[key] = b
 47    }
 48
 49    // Refill tokens based on elapsed time
 50    now := time.Now()
 51    elapsed := now.Sub(b.lastRefill)
 52
 53    if elapsed >= rl.window {
 54        b.tokens = rl.rate
 55        b.lastRefill = now
 56    }
 57
 58    if b.tokens > 0 {
 59        b.tokens--
 60        return true
 61    }
 62
 63    return false
 64}
 65
 66func (rl *RateLimiter) cleanupBuckets() {
 67    ticker := time.NewTicker(rl.cleanup)
 68    defer ticker.Stop()
 69
 70    for range ticker.C {
 71        rl.mu.Lock()
 72        now := time.Now()
 73
 74        for key, b := range rl.buckets {
 75            if now.Sub(b.lastRefill) > rl.window*2 {
 76                delete(rl.buckets, key)
 77            }
 78        }
 79
 80        rl.mu.Unlock()
 81    }
 82}
 83
 84// Middleware
 85func RateLimitMiddleware(limiter *RateLimiter) func(http.Handler) http.Handler {
 86    return func(next http.Handler) http.Handler {
 87        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 88            // Use IP address as key
 89            key := r.RemoteAddr
 90
 91            if !limiter.Allow(key) {
 92                http.Error(w, "Rate limit exceeded", http.StatusTooManyRequests)
 93                return
 94            }
 95
 96            next.ServeHTTP(w, r)
 97        })
 98    }
 99}
100
101func main() {
102    limiter := NewRateLimiter(5, time.Minute)
103
104    mux := http.NewServeMux()
105    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
106        w.Write([]byte("Request successful"))
107    })
108
109    handler := RateLimitMiddleware(limiter)(mux)
110
111    fmt.Println("Server running on :8080")
112    http.ListenAndServe(":8080", handler)
113}

Exercise 2: Implement CSRF Protection

Learning Objective: Build comprehensive CSRF protection middleware to prevent state-changing requests from unauthorized websites.

Context: CSRF attacks have been responsible for major security breaches, including the 2009 Gmail CSRF vulnerability that allowed attackers to read user emails. The OWASP Top 10 consistently ranks CSRF as a critical web application vulnerability that every developer must understand and prevent.

Difficulty: Advanced | Time: 25-30 minutes

Build production-grade CSRF protection middleware that prevents malicious websites from making unauthorized requests on behalf of authenticated users:

  • Generate cryptographically secure CSRF tokens
  • Implement double submit cookie pattern
  • Support both cookie-based and session-based token storage
  • Handle AJAX requests with proper header validation
  • Include token refresh and expiration mechanisms
  • Provide proper error handling and logging

Real-world application: This protection is essential for banking applications, social media platforms, and any web application that handles user authentication and sensitive operations.

Solution
  1package main
  2
  3import (
  4    "crypto/rand"
  5    "encoding/base64"
  6    "fmt"
  7    "net/http"
  8    "sync"
  9    "time"
 10)
 11
 12// CSRFProtection implements CSRF token validation
 13type CSRFProtection struct {
 14    mu     sync.RWMutex
 15    tokens map[string]time.Time
 16    maxAge time.Duration
 17}
 18
 19func NewCSRFProtection(maxAge time.Duration) *CSRFProtection {
 20    csrf := &CSRFProtection{
 21        tokens: make(map[string]time.Time),
 22        maxAge: maxAge,
 23    }
 24
 25    go csrf.cleanup()
 26    return csrf
 27}
 28
 29// GenerateToken creates a new CSRF token
 30func (csrf *CSRFProtection) GenerateToken() (string, error) {
 31    bytes := make([]byte, 32)
 32    if _, err := rand.Read(bytes); err != nil {
 33        return "", err
 34    }
 35
 36    token := base64.URLEncoding.EncodeToString(bytes)
 37
 38    csrf.mu.Lock()
 39    csrf.tokens[token] = time.Now().Add(csrf.maxAge)
 40    csrf.mu.Unlock()
 41
 42    return token, nil
 43}
 44
 45// ValidateToken checks if token is valid
 46func (csrf *CSRFProtection) ValidateToken(token string) bool {
 47    csrf.mu.RLock()
 48    defer csrf.mu.RUnlock()
 49
 50    expiry, exists := csrf.tokens[token]
 51    if !exists {
 52        return false
 53    }
 54
 55    return time.Now().Before(expiry)
 56}
 57
 58// DeleteToken removes a token
 59func (csrf *CSRFProtection) DeleteToken(token string) {
 60    csrf.mu.Lock()
 61    delete(csrf.tokens, token)
 62    csrf.mu.Unlock()
 63}
 64
 65func (csrf *CSRFProtection) cleanup() {
 66    ticker := time.NewTicker(time.Minute)
 67    defer ticker.Stop()
 68
 69    for range ticker.C {
 70        csrf.mu.Lock()
 71        now := time.Now()
 72
 73        for token, expiry := range csrf.tokens {
 74            if now.After(expiry) {
 75                delete(csrf.tokens, token)
 76            }
 77        }
 78
 79        csrf.mu.Unlock()
 80    }
 81}
 82
 83// Middleware
 84func CSRFMiddleware(csrf *CSRFProtection) func(http.Handler) http.Handler {
 85    return func(next http.Handler) http.Handler {
 86        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 87            // Skip CSRF for safe methods
 88            if r.Method == "GET" || r.Method == "HEAD" || r.Method == "OPTIONS" {
 89                next.ServeHTTP(w, r)
 90                return
 91            }
 92
 93            // Validate CSRF token
 94            token := r.Header.Get("X-CSRF-Token")
 95            if token == "" {
 96                token = r.FormValue("csrf_token")
 97            }
 98
 99            if !csrf.ValidateToken(token) {
100                http.Error(w, "Invalid CSRF token", http.StatusForbidden)
101                return
102            }
103
104            next.ServeHTTP(w, r)
105        })
106    }
107}
108
109func main() {
110    csrf := NewCSRFProtection(1 * time.Hour)
111
112    mux := http.NewServeMux()
113
114    // GET endpoint
115    mux.HandleFunc("/form", func(w http.ResponseWriter, r *http.Request) {
116        token, _ := csrf.GenerateToken()
117
118        html := fmt.Sprintf(`
119            <form method="POST" action="/submit">
120                <input type="hidden" name="csrf_token" value="%s">
121                <input type="text" name="data" placeholder="Enter data">
122                <button type="submit">Submit</button>
123            </form>
124        `, token)
125
126        w.Header().Set("Content-Type", "text/html")
127        w.Write([]byte(html))
128    })
129
130    // POST endpoint
131    mux.HandleFunc("/submit", func(w http.ResponseWriter, r *http.Request) {
132        w.Write([]byte("Form submitted successfully"))
133    })
134
135    handler := CSRFMiddleware(csrf)(mux)
136
137    fmt.Println("Server running on :8080")
138    http.ListenAndServe(":8080", handler)
139}

Exercise 3: Content Security Policy Builder

Learning Objective: Create a flexible Content Security Policy builder that prevents XSS attacks and other client-side security vulnerabilities.

Context: CSP is a critical defense layer against XSS attacks, which affect over 70% of web applications. The 2017 British Airways breach, which compromised 380,000 customer records, could have been prevented with proper CSP implementation. Modern browsers enforce CSP as a primary security mechanism.

Difficulty: Advanced | Time: 30-35 minutes

Build a production-ready CSP builder that provides fine-grained control over resource loading and prevents various client-side attacks:

  • Design fluent API for constructing complex CSP policies
  • Support all major CSP directives with proper validation
  • Implement both enforcement and report-only modes
  • Include nonce and hash-based CSP generation
  • Handle browser compatibility and fallback strategies
  • Provide CSP violation monitoring and logging capabilities

Requirements:

  1. Fluent API for constructing CSP headers
  2. Support all major CSP directives
  3. Validate directive values against CSP specification
  4. Generate CSP header string with proper formatting
  5. Support both Content-Security-Policy and Report-Only modes
  6. Include nonce generation for dynamic content
  7. Provide CSP violation reporting endpoint

Real-world application: CSP implementation is mandatory for financial applications, healthcare systems, and any web application handling sensitive user data or requiring PCI DSS compliance.

Solution with Explanation
  1// run
  2package main
  3
  4import (
  5    "fmt"
  6    "net/http"
  7    "strings"
  8)
  9
 10// CSPBuilder builds Content Security Policy headers
 11type CSPBuilder struct {
 12    directives map[string][]string
 13    reportOnly bool
 14}
 15
 16// NewCSPBuilder creates a new CSP builder
 17func NewCSPBuilder() *CSPBuilder {
 18    return &CSPBuilder{
 19        directives: make(map[string][]string),
 20        reportOnly: false,
 21    }
 22}
 23
 24// ReportOnly sets the policy to report-only mode
 25func (csb *CSPBuilder) ReportOnly() *CSPBuilder {
 26    csb.reportOnly = true
 27    return csb
 28}
 29
 30// DefaultSrc sets the default-src directive
 31func (csb *CSPBuilder) DefaultSrc(sources ...string) *CSPBuilder {
 32    csb.directives["default-src"] = sources
 33    return csb
 34}
 35
 36// ScriptSrc sets the script-src directive
 37func (csb *CSPBuilder) ScriptSrc(sources ...string) *CSPBuilder {
 38    csb.directives["script-src"] = sources
 39    return csb
 40}
 41
 42// StyleSrc sets the style-src directive
 43func (csb *CSPBuilder) StyleSrc(sources ...string) *CSPBuilder {
 44    csb.directives["style-src"] = sources
 45    return csb
 46}
 47
 48// ImgSrc sets the img-src directive
 49func (csb *CSPBuilder) ImgSrc(sources ...string) *CSPBuilder {
 50    csb.directives["img-src"] = sources
 51    return csb
 52}
 53
 54// ConnectSrc sets the connect-src directive
 55func (csb *CSPBuilder) ConnectSrc(sources ...string) *CSPBuilder {
 56    csb.directives["connect-src"] = sources
 57    return csb
 58}
 59
 60// FrameAncestors sets the frame-ancestors directive
 61func (csb *CSPBuilder) FrameAncestors(sources ...string) *CSPBuilder {
 62    csb.directives["frame-ancestors"] = sources
 63    return csb
 64}
 65
 66// ObjectSrc sets the object-src directive
 67func (csb *CSPBuilder) ObjectSrc(sources ...string) *CSPBuilder {
 68    csb.directives["object-src"] = sources
 69    return csb
 70}
 71
 72// UpgradeInsecureRequests adds the upgrade-insecure-requests directive
 73func (csb *CSPBuilder) UpgradeInsecureRequests() *CSPBuilder {
 74    csb.directives["upgrade-insecure-requests"] = []string{}
 75    return csb
 76}
 77
 78// ReportURI sets the report-uri directive
 79func (csb *CSPBuilder) ReportURI(uri string) *CSPBuilder {
 80    csb.directives["report-uri"] = []string{uri}
 81    return csb
 82}
 83
 84// Build generates the CSP header string
 85func (csb *CSPBuilder) Build() string {
 86    var parts []string
 87
 88    // Ensure consistent ordering
 89    order := []string{
 90        "default-src", "script-src", "style-src", "img-src",
 91        "connect-src", "object-src", "frame-ancestors",
 92        "upgrade-insecure-requests", "report-uri",
 93    }
 94
 95    for _, directive := range order {
 96        if sources, ok := csb.directives[directive]; ok {
 97            if len(sources) == 0 {
 98                // Boolean directive
 99                parts = append(parts, directive)
100            } else {
101                // Directive with sources
102                parts = append(parts, directive+" "+strings.Join(sources, " "))
103            }
104        }
105    }
106
107    return strings.Join(parts, "; ")
108}
109
110// HeaderName returns the appropriate CSP header name
111func (csb *CSPBuilder) HeaderName() string {
112    if csb.reportOnly {
113        return "Content-Security-Policy-Report-Only"
114    }
115    return "Content-Security-Policy"
116}
117
118// Apply applies the CSP to an HTTP response
119func (csb *CSPBuilder) Apply(w http.ResponseWriter) {
120    w.Header().Set(csb.HeaderName(), csb.Build())
121}
122
123// Common CSP source values
124const (
125    Self         = "'self'"
126    None         = "'none'"
127    UnsafeInline = "'unsafe-inline'"
128    UnsafeEval   = "'unsafe-eval'"
129    Data         = "data:"
130    HTTPS        = "https:"
131)
132
133func main() {
134    // Example 1: Strict CSP for maximum security
135    strictCSP := NewCSPBuilder().
136        DefaultSrc(Self).
137        ScriptSrc(Self).
138        StyleSrc(Self).
139        ImgSrc(Self, Data, HTTPS).
140        ConnectSrc(Self).
141        ObjectSrc(None).
142        FrameAncestors(None).
143        UpgradeInsecureRequests()
144
145    fmt.Println("=== Strict CSP ===")
146    fmt.Printf("Header: %s\n", strictCSP.HeaderName())
147    fmt.Printf("Policy: %s\n\n", strictCSP.Build())
148
149    // Example 2: Development CSP
150    devCSP := NewCSPBuilder().
151        DefaultSrc(Self).
152        ScriptSrc(Self, UnsafeInline, UnsafeEval).
153        StyleSrc(Self, UnsafeInline).
154        ImgSrc("*", Data).
155        ConnectSrc(Self, "ws://localhost:*").
156        ReportURI("/csp-violations")
157
158    fmt.Println("=== Development CSP ===")
159    fmt.Printf("Policy: %s\n\n", devCSP.Build())
160
161    // Example 3: Production CSP with CDN
162    prodCSP := NewCSPBuilder().
163        DefaultSrc(Self).
164        ScriptSrc(Self, "https://cdn.example.com").
165        StyleSrc(Self, "https://cdn.example.com", "https://fonts.googleapis.com").
166        ImgSrc(Self, "https://cdn.example.com", Data).
167        ConnectSrc(Self, "https://api.example.com").
168        ObjectSrc(None).
169        UpgradeInsecureRequests().
170        ReportURI("/csp-violations")
171
172    fmt.Println("=== Production CSP ===")
173    fmt.Printf("Policy: %s\n\n", prodCSP.Build())
174
175    // Example 4: Report-Only mode
176    reportOnlyCSP := NewCSPBuilder().
177        ReportOnly().
178        DefaultSrc(Self).
179        ScriptSrc(Self).
180        ReportURI("/csp-violations")
181
182    fmt.Println("=== Report-Only CSP ===")
183    fmt.Printf("Header: %s\n", reportOnlyCSP.HeaderName())
184    fmt.Printf("Policy: %s\n\n", reportOnlyCSP.Build())
185
186    // Example 5: API-only CSP
187    apiCSP := NewCSPBuilder().
188        DefaultSrc(None).
189        ConnectSrc(Self).
190        FrameAncestors(None)
191
192    fmt.Println("=== API-Only CSP ===")
193    fmt.Printf("Policy: %s\n", apiCSP.Build())
194}

Explanation:

Content Security Policy prevents XSS attacks by controlling which resources can load and execute.

Key Directives:

  1. default-src: Fallback for other directives
  2. script-src: JavaScript sources
  3. style-src: CSS sources
  4. img-src: Image sources
  5. connect-src: AJAX, WebSocket, EventSource
  6. object-src: <object>, <embed>, <applet>
  7. frame-ancestors: Clickjacking protection

Common Source Values:

  • 'self': Same origin only
  • 'none': Block all sources
  • 'unsafe-inline': Allow inline scripts/styles
  • 'unsafe-eval': Allow eval()
  • data:: Data URIs
  • https:: Any HTTPS source
  • Specific domains: e.g., https://cdn.example.com

Security Benefits:

  1. XSS Prevention: Blocks injected scripts
  2. Clickjacking Protection: frame-ancestors 'none'
  3. Data Exfiltration: connect-src limits AJAX targets
  4. Mixed Content: upgrade-insecure-requests

Best Practices:

  • Start strict, relax as needed
  • Avoid 'unsafe-inline' and 'unsafe-eval'
  • Use 'none' for unused resources
  • Test with Report-Only mode first
  • Monitor violation reports

Real-World CSP Examples:

GitHub:

default-src 'none';
script-src github.githubassets.com;
style-src github.githubassets.com;
img-src *;

Google:

default-src 'self';
script-src 'self' https://www.google.com;
report-uri /csp-report

Attack Prevention:

1// Attacker tries XSS:
2<script>alert(document.cookie)</script>
3
4// CSP blocks with:
5script-src 'self'  // Inline scripts blocked!

Violation Reporting:

CSP violations sent to report-uri:

1{
2  "csp-report": {
3    "document-uri": "https://example.com",
4    "violated-directive": "script-src 'self'",
5    "blocked-uri": "https://evil.com/bad.js"
6  }
7}

Exercise 4: Secure Password Hashing System

Learning Objective: Implement production-grade password hashing with proper security practices, rate limiting, and user experience considerations.

Context: Weak password hashing has been responsible for some of the largest data breaches in history. The 2012 LinkedIn breach exposed 167 million passwords because they used unsalted SHA1 hashes. Modern applications must use industry-standard hashing algorithms with proper configuration.

Difficulty: Intermediate | Time: 25-30 minutes

Build a comprehensive password hashing system that protects user credentials against modern attack vectors:

  • Implement secure password hashing using bcrypt with proper cost configuration
  • Add rate limiting for login attempts to prevent brute-force attacks
  • Include password strength validation and breach checking
  • Support password reset functionality with secure token generation
  • Implement secure password storage and validation practices
  • Handle edge cases like migration from legacy hashing systems

Real-world application: This system protects user accounts in authentication systems, preventing credential stuffing attacks and minimizing damage if password databases are compromised.

Solution
  1// run
  2package main
  3
  4import (
  5	"crypto/rand"
  6	"encoding/base64"
  7	"fmt"
  8	"strings"
  9	"time"
 10
 11	"golang.org/x/crypto/bcrypt"
 12)
 13
 14// PasswordHasher handles secure password hashing and validation
 15type PasswordHasher struct {
 16	minCost  int
 17	maxCost  int
 18	minLength int
 19}
 20
 21func NewPasswordHasher() *PasswordHasher {
 22	return &PasswordHasher{
 23		minCost:   12, // bcrypt cost for production
 24		maxCost:   14, // maximum acceptable cost
 25		minLength: 8,  // minimum password length
 26	}
 27}
 28
 29// HashPassword securely hashes a password with bcrypt
 30func (ph *PasswordHasher) HashPassword(password string) (string, error) {
 31	// Validate password strength
 32	if err := ph.ValidatePasswordStrength(password); err != nil {
 33		return "", err
 34	}
 35
 36	// Generate hash with adaptive cost based on current system load
 37	cost := ph.calculateOptimalCost()
 38
 39	hash, err := bcrypt.GenerateFromPassword([]byte(password), cost)
 40	if err != nil {
 41		return "", fmt.Errorf("failed to hash password: %w", err)
 42	}
 43
 44	return string(hash), nil
 45}
 46
 47// ValidatePassword verifies a password against its hash
 48func (ph *PasswordHasher) ValidatePassword(password, hash string) bool {
 49	err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
 50	return err == nil
 51}
 52
 53// ValidatePasswordStrength checks if password meets security requirements
 54func (ph *PasswordHasher) ValidatePasswordStrength(password string) error {
 55	if len(password) < ph.minLength {
 56		return fmt.Errorf("password must be at least %d characters", ph.minLength)
 57	}
 58
 59	// Check for common weak passwords
 60	weakPasswords := []string{
 61		"password", "12345678", "qwerty", "abc123", "password123",
 62		"admin", "letmein", "welcome", "monkey", "dragon",
 63	}
 64
 65	for _, weak := range weakPasswords {
 66		if strings.EqualFold(password, weak) {
 67			return fmt.Errorf("password is too common and weak")
 68		}
 69	}
 70
 71	// Basic complexity checks
 72	var hasUpper, hasLower, hasDigit, hasSpecial bool
 73
 74	for _, char := range password {
 75		switch {
 76		case char >= 'A' && char <= 'Z':
 77			hasUpper = true
 78		case char >= 'a' && char <= 'z':
 79			hasLower = true
 80		case char >= '0' && char <= '9':
 81			hasDigit = true
 82		case strings.ContainsRune("!@#$%^&*()_+-=[]{}|;:,.<>?", char):
 83			hasSpecial = true
 84		}
 85	}
 86
 87	complexityScore := 0
 88	if hasUpper {
 89		complexityScore++
 90	}
 91	if hasLower {
 92		complexityScore++
 93	}
 94	if hasDigit {
 95		complexityScore++
 96	}
 97	if hasSpecial {
 98		complexityScore++
 99	}
100
101	if complexityScore < 3 {
102		return fmt.Errorf("password must contain at least 3 of: uppercase, lowercase, digits, special characters")
103	}
104
105	return nil
106}
107
108// calculateOptimalCost determines bcrypt cost based on system performance
109func (ph *PasswordHasher) calculateOptimalCost() int {
110	// In production, benchmark system performance
111	// For demo, return fixed cost
112	return ph.minCost
113}
114
115// GeneratePasswordResetToken creates a secure token for password resets
116func (ph *PasswordHasher) GeneratePasswordResetToken() (string, error) {
117	bytes := make([]byte, 32) // 256-bit token
118	if _, err := rand.Read(bytes); err != nil {
119		return "", fmt.Errorf("failed to generate reset token: %w", err)
120	}
121
122	return base64.URLEncoding.EncodeToString(bytes), nil
123}
124
125func main() {
126	fmt.Println("=== Secure Password Hashing System ===\n")
127
128	hasher := NewPasswordHasher()
129
130	// Test password hashing
131	fmt.Println("--- Password Hashing ---")
132	password := "SecurePassword123!"
133	hash, err := hasher.HashPassword(password)
134	if err != nil {
135		fmt.Printf("Hashing failed: %v\n", err)
136	} else {
137		fmt.Printf("Original: %s\n", password)
138		fmt.Printf("Hash: %s\n", hash[:20]+"...")
139
140		// Test validation
141		valid := hasher.ValidatePassword(password, hash)
142		fmt.Printf("Password validation: %v\n", valid)
143
144		// Test invalid password
145		invalid := hasher.ValidatePassword("wrongpassword", hash)
146		fmt.Printf("Invalid password validation: %v\n", invalid)
147	}
148
149	// Test weak password rejection
150	fmt.Println("\n--- Password Strength Validation ---")
151	weakPasswords := []string{"weak", "password", "12345678"}
152	for _, weak := range weakPasswords {
153		err := hasher.ValidatePasswordStrength(weak)
154		if err != nil {
155			fmt.Printf("'%s': %v\n", weak, err)
156		}
157	}
158
159	// Test strong password acceptance
160	strongPassword := "Str0ng!P@ssw0rd#2023"
161	err = hasher.ValidatePasswordStrength(strongPassword)
162	if err == nil {
163		fmt.Printf("'%s': Strong password accepted\n", strongPassword)
164	}
165
166	// Test password reset token generation
167	fmt.Println("\n--- Password Reset Tokens ---")
168	token, err := hasher.GeneratePasswordResetToken()
169	if err != nil {
170		fmt.Printf("Token generation failed: %v\n", err)
171	} else {
172		fmt.Printf("Generated token: %s\n", token[:20]+"...")
173		fmt.Printf("Token length: %d characters\n", len(token))
174	}
175}

Run the code:

1go run .

Security Features Demonstrated:

  1. Secure Password Hashing: Uses bcrypt with configurable cost
  2. Password Strength Validation: Enforces complexity requirements and blocks common passwords
  3. Secure Token Generation: Cryptographically secure random tokens for password resets
  4. Constant-Time Comparison: Uses bcrypt's built-in timing-safe comparison
  5. Memory-Hard Function: bcrypt is designed to be slow and memory-intensive

Production Considerations:

  • Use environment-specific bcrypt costs
  • Implement rate limiting for password reset requests
  • Add password breach checking
  • Implement secure password reset workflow
  • Add account lockout after failed attempts
  • Use HTTPS for all authentication endpoints

Exercise 5: SQL Injection Prevention System

Learning Objective: Build a comprehensive SQL injection prevention system that demonstrates proper database security practices.

Context: SQL injection remains one of the most critical web vulnerabilities, responsible for major breaches like the 2014 TalkTalk hack that exposed 157,000 customer records. The FBI estimates that SQL injection costs businesses over $1 billion annually in damages and remediation costs.

Difficulty: Advanced | Time: 30-35 minutes

Implement a multi-layered SQL injection prevention system that protects database interactions from various attack vectors:

  • Create parameterized query builder that prevents SQL injection
  • Implement input validation and sanitization layers
  • Build query logging and monitoring for suspicious patterns
  • Include dynamic query construction with safe validation
  • Add database connection pooling with security controls
  • Implement query result sanitization for output encoding

Real-world application: This system protects web applications from SQL injection attacks, which are responsible for data breaches, financial fraud, and complete system compromises in production environments.

Solution
  1// run
  2package main
  3
  4import (
  5	"database/sql"
  6	"fmt"
  7	"regexp"
  8	"strconv"
  9	"strings"
 10	"time"
 11	"unicode"
 12
 13	_ "github.com/mattn/go-sqlite3"
 14)
 15
 16// SQLInjectionPrevention prevents SQL injection through multiple layers
 17type SQLInjectionPrevention struct {
 18	db          *sql.DB
 19	queryLogger *QueryLogger
 20	validator   *InputValidator
 21}
 22
 23func NewSQLInjectionPrevention(dbPath string) (*SQLInjectionPrevention, error) {
 24	db, err := sql.Open("sqlite3", dbPath)
 25	if err != nil {
 26		return nil, fmt.Errorf("failed to open database: %w", err)
 27	}
 28
 29	return &SQLInjectionPrevention{
 30		db:          db,
 31		queryLogger: NewQueryLogger(),
 32		validator:   NewInputValidator(),
 33	}, nil
 34}
 35
 36// SafeQueryBuilder builds parameterized queries safely
 37type SafeQueryBuilder struct {
 38	query strings.Builder
 39	args  []interface{}
 40}
 41
 42func NewSafeQueryBuilder() *SafeQueryBuilder {
 43	return &SafeQueryBuilder{
 44		query: strings.Builder{},
 45		args:  make([]interface{}, 0),
 46	}
 47}
 48
 49func (sqb *SafeQueryBuilder) Select(columns ...string) *SafeQueryBuilder {
 50	sqb.query.WriteString("SELECT ")
 51	sqb.query.WriteString(strings.Join(columns, ", "))
 52	return sqb
 53}
 54
 55func (sqb *SafeQueryBuilder) From(table string) *SafeQueryBuilder {
 56	// Validate table name to prevent injection
 57	if !isValidIdentifier(table) {
 58		panic(fmt.Sprintf("invalid table name: %s", table))
 59	}
 60	sqb.query.WriteString(" FROM ")
 61	sqb.query.WriteString(table)
 62	return sqb
 63}
 64
 65func (sqb *SafeQueryBuilder) Where(condition string, args ...interface{}) *SafeQueryBuilder {
 66	sqb.query.WriteString(" WHERE ")
 67	sqb.query.WriteString(condition)
 68	sqb.args = append(sqb.args, args...)
 69	return sqb
 70}
 71
 72func (sqb *SafeQueryBuilder) Build() (string, []interface{}) {
 73	return sqb.query.String(), sqb.args
 74}
 75
 76// isValidIdentifier validates SQL identifiers
 77func isValidIdentifier(name string) bool {
 78	if len(name) == 0 || len(name) > 64 {
 79		return false
 80	}
 81
 82	// Must start with letter or underscore
 83	if !unicode.IsLetter(rune(name[0])) && name[0] != '_' {
 84		return false
 85	}
 86
 87	// Can contain letters, numbers, and underscores
 88	for _, char := range name {
 89		if !unicode.IsLetter(char) && !unicode.IsDigit(char) && char != '_' {
 90			return false
 91		}
 92	}
 93
 94	// Check for SQL keywords
 95	sqlKeywords := []string{
 96		"SELECT", "FROM", "WHERE", "INSERT", "UPDATE", "DELETE",
 97		"DROP", "CREATE", "ALTER", "EXEC", "UNION", "SCRIPT",
 98	}
 99
100	upperName := strings.ToUpper(name)
101	for _, keyword := range sqlKeywords {
102		if upperName == keyword {
103			return false
104		}
105	}
106
107	return true
108}
109
110// InputValidator validates and sanitizes user input
111type InputValidator struct {
112	patterns map[string]*regexp.Regexp
113}
114
115func NewInputValidator() *InputValidator {
116	return &InputValidator{
117		patterns: map[string]*regexp.Regexp{
118			"email":     regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`),
119			"username":  regexp.MustCompile(`^[a-zA-Z0-9_-]{3,20}$`),
120			"id":        regexp.MustCompile(`^[0-9]{1,10}$`),
121			"name":      regexp.MustCompile(`^[a-zA-Z\s'-]{1,50}$`),
122		},
123	}
124}
125
126func (iv *InputValidator) ValidateEmail(email string) error {
127	email = strings.TrimSpace(email)
128	if !iv.patterns["email"].MatchString(email) {
129		return fmt.Errorf("invalid email format: %s", email)
130	}
131	return nil
132}
133
134func (iv *InputValidator) SanitizeString(input string) string {
135	// Remove potential SQL injection characters
136	dangerous := []string{"'", "\"", ";", "--", "/*", "*/", "xp_", "sp_"}
137
138	result := input
139	for _, char := range dangerous {
140		result = strings.ReplaceAll(result, char, "")
141	}
142
143	return strings.TrimSpace(result)
144}
145
146// QueryLogger logs and monitors database queries for security
147type QueryLogger struct {
148	queries []QueryLog
149}
150
151type QueryLog struct {
152	Query     string
153	Args      []interface{}
154	Timestamp time.Time
155	Duration  time.Duration
156	Success   bool
157}
158
159func NewQueryLogger() *QueryLogger {
160	return &QueryLogger{
161		queries: make([]QueryLog, 0),
162	}
163}
164
165func (ql *QueryLogger) LogQuery(query string, args []interface{}, duration time.Duration, success bool) {
166	log := QueryLog{
167		Query:     query,
168		Args:      args,
169		Timestamp: time.Now(),
170		Duration:  duration,
171		Success:   success,
172	}
173
174	ql.queries = append(ql.queries, log)
175
176	// In production, send to logging system
177	if !success {
178		fmt.Printf("FAILED QUERY: %s with args %v\n", query, args)
179	}
180}
181
182// User represents a user in the database
183type User struct {
184	ID        int       `json:"id"`
185	Username  string    `json:"username"`
186	Email     string    `json:"email"`
187	Name      string    `json:"name"`
188	CreatedAt time.Time `json:"created_at"`
189}
190
191// UserRepository provides safe database operations for users
192type UserRepository struct {
193	sip *SQLInjectionPrevention
194}
195
196func NewUserRepository(sip *SQLInjectionPrevention) *UserRepository {
197	return &UserRepository{sip: sip}
198}
199
200// CreateUser safely creates a new user with validation
201func (ur *UserRepository) CreateUser(username, email, name string) (*User, error) {
202	// Validate all inputs
203	if err := ur.sip.validator.ValidateEmail(email); err != nil {
204		return nil, fmt.Errorf("invalid email: %w", err)
205	}
206
207	name = ur.sip.validator.SanitizeString(name)
208
209	// Build safe query
210	query := `
211		INSERT INTO users (username, email, name, created_at)
212		VALUES (?, ?, ?, ?)
213	`
214
215	start := time.Now()
216	result, err := ur.sip.db.Exec(query, username, email, name, time.Now())
217	duration := time.Since(start)
218
219	ur.sip.queryLogger.LogQuery(query, []interface{}{username, email, name}, duration, err == nil)
220
221	if err != nil {
222		return nil, fmt.Errorf("failed to create user: %w", err)
223	}
224
225	id, err := result.LastInsertId()
226	if err != nil {
227		return nil, fmt.Errorf("failed to get user ID: %w", err)
228	}
229
230	return &User{
231		ID:        int(id),
232		Username:  username,
233		Email:     email,
234		Name:      name,
235		CreatedAt: time.Now(),
236	}, nil
237}
238
239// InitializeDatabase creates the database schema
240func (sip *SQLInjectionPrevention) InitializeDatabase() error {
241	schema := `
242	CREATE TABLE IF NOT EXISTS users (
243		id INTEGER PRIMARY KEY AUTOINCREMENT,
244		username TEXT UNIQUE NOT NULL,
245		email TEXT UNIQUE NOT NULL,
246		name TEXT NOT NULL,
247		created_at DATETIME DEFAULT CURRENT_TIMESTAMP
248	);
249	`
250
251	_, err := sip.db.Exec(schema)
252	return err
253}
254
255func main() {
256	fmt.Println("=== SQL Injection Prevention System ===\n")
257
258	// Initialize system
259	sip, err := NewSQLInjectionPrevention(":memory:")
260	if err != nil {
261		panic(err)
262	}
263	defer sip.db.Close()
264
265	// Create database schema
266	if err := sip.InitializeDatabase(); err != nil {
267		panic(err)
268	}
269
270	userRepo := NewUserRepository(sip)
271
272	// Test safe user creation
273	fmt.Println("--- Safe User Creation ---")
274	user1, err := userRepo.CreateUser("alice123", "alice@example.com", "Alice Johnson")
275	if err != nil {
276		fmt.Printf("Failed to create user: %v\n", err)
277	} else {
278		fmt.Printf("Created user: %+v\n", user1)
279	}
280
281	// Test SQL injection attempt
282	fmt.Println("\n--- SQL Injection Prevention ---")
283
284	// Attempt SQL injection through username
285	maliciousUsername := "alice'; DROP TABLE users; --"
286	_, err = userRepo.CreateUser(maliciousUsername, "evil@example.com", "Hacker")
287	if err != nil {
288		fmt.Printf("SQL injection blocked: %v\n", err)
289	}
290
291	// Test input validation
292	fmt.Println("\n--- Input Validation ---")
293	testEmails := []string{
294		"valid@example.com",
295		"invalid-email",
296		"user'; DROP TABLE users; --@example.com",
297	}
298
299	for _, email := range testEmails {
300		err := sip.validator.ValidateEmail(email)
301		if err != nil {
302			fmt.Printf("Rejected '%s': %v\n", email, err)
303		} else {
304			fmt.Printf("Accepted '%s'\n", email)
305		}
306	}
307
308	fmt.Println("\nSystem successfully prevented SQL injection attacks!")
309}

Run the code:

1go run .

SQL Injection Prevention Techniques:

  1. Parameterized Queries: All database queries use parameter binding
  2. Input Validation: Strict validation for all user inputs
  3. Identifier Whitelisting: Table and column names are validated
  4. Query Builder: Safe query construction with validation
  5. Input Sanitization: Remove dangerous characters
  6. Query Logging: Monitor for suspicious query patterns

Attack Vectors Prevented:

  • Classic SQL injection: ' OR '1'='1
  • Union-based attacks: ' UNION SELECT * FROM users
  • Blind SQL injection: ' AND (SELECT COUNT(*) FROM users) > 0
  • Time-based attacks: '; WAITFOR DELAY '00:00:05'--
  • Stacked queries: '; DROP TABLE users--

Summary