Skip to main content

Security Guide

forge includes built-in security features to protect your application from common vulnerabilities.

Overview

forge provides protection against:

  • SQL Injection - Parameterized queries and proper escaping
  • XSS (Cross-Site Scripting) - Output sanitization
  • CSRF (Cross-Site Request Forgery) - CSRF tokens
  • Session Hijacking - Secure session management
  • Password Attacks - Secure password hashing

SQL Injection Protection

forge uses parameterized queries by default, preventing SQL injection:

// Safe: Uses parameterized queries
users, err := User.Objects.
Filter(User.Fields.Username.Equals(username)).
All(ctx)

// The SQL builder automatically escapes and parameterizes
// SQL: SELECT * FROM users WHERE username = $1
// Args: [username]

Never Use String Concatenation

// ❌ BAD: Vulnerable to SQL injection
query := fmt.Sprintf("SELECT * FROM users WHERE username = '%s'", username)

// ✅ GOOD: Use QuerySet API
users, err := User.Objects.
Filter(User.Fields.Username.Equals(username)).
All(ctx)

XSS Protection

Automatic Escaping

When rendering templates, forge automatically escapes output:

// In templates, output is automatically escaped
{{ .User.Username }} // Safe: HTML entities escaped

Manual Escaping

For custom output, use the HTML escape function:

import "html"

escaped := html.EscapeString(userInput)

Safe HTML

If you need to output HTML, mark it as safe:

// Only if you trust the content
safeHTML := template.HTML(trustedContent)

CSRF Protection

Enable CSRF Protection

CSRF protection is enabled by default. Configure it in config.yaml:

security:
csrf:
enabled: true
secret_key: "your-secret-key-change-in-production"
cookie_name: "csrf_token"

Using CSRF Tokens

In forms, include the CSRF token:

<form method="POST" action="/posts/create">
<input type="hidden" name="csrf_token" value="{{ .CSRFToken }}">
<!-- form fields -->
</form>

API Requests

For API requests, include CSRF token in header:

fetch('/api/posts/', {
method: 'POST',
headers: {
'X-CSRF-Token': getCSRFToken(),
'Content-Type': 'application/json'
},
body: JSON.stringify(data)
});

Exempt Routes

Exempt certain routes from CSRF protection:

router.Use(csrf.Protect(
csrf.Secure(true),
csrf.Exempt("/api/public/"),
))

Session Security

Secure Session Configuration

Configure secure sessions:

security:
session:
secret: "your-secret-key-change-in-production"
secure: true # HTTPS only
http_only: true # Not accessible via JavaScript
same_site: "strict"
max_age: 86400 # 24 hours

Session Management

// Create session
session, err := sessionManager.Create(r.Context(), userID)

// Get session
userID, err := sessionManager.Get(r.Context())

// Destroy session
err := sessionManager.Destroy(r.Context())

Password Security

Password Hashing

Always hash passwords using bcrypt:

import "github.com/forgego/forge/identity"

// Hash password
hashed, err := identity.HashPassword(password)

// Verify password
ok := identity.CheckPassword(password, hashed)

Password Validation

Enforce strong passwords:

func (User) Hooks() *schema.ModelHooks {
return &schema.ModelHooks{
BeforeCreate: func(ctx context.Context, instance interface{}) error {
user := instance.(*User)

// Validate password strength
if len(user.Password) < 8 {
return errors.New("password must be at least 8 characters")
}

// Hash password
hashed, err := identity.HashPassword(user.Password)
if err != nil {
return err
}
user.Password = hashed

return nil
},
}
}

Authentication

Secure Authentication

Use secure authentication practices:

// Rate limit login attempts
func LoginHandler(w http.ResponseWriter, r *http.Request) {
// Check rate limit
if rateLimitExceeded(r) {
http.Error(w, "Too many attempts", http.StatusTooManyRequests)
return
}

// Authenticate
user, err := authenticate(r)
if err != nil {
// Don't reveal if user exists
http.Error(w, "Invalid credentials", http.StatusUnauthorized)
return
}

// Create secure session
session, err := createSession(user)
// ...
}

Input Validation

Validate All Input

Always validate user input:

// Use schema validators
schema.String("email").
Required().
Validators(schema.EmailValidator()).
Build()

// Custom validation in hooks
func (User) Hooks() *schema.ModelHooks {
return &schema.ModelHooks{
Clean: func(ctx context.Context, instance interface{}) error {
user := instance.(*User)

// Validate email format
if !isValidEmail(user.Email) {
return errors.New("invalid email format")
}

return nil
},
}
}

HTTPS

Force HTTPS in Production

server:
tls:
enabled: true
cert_file: "/path/to/cert.pem"
key_file: "/path/to/key.pem"

Redirect HTTP to HTTPS

router.Use(func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.TLS == nil && r.Header.Get("X-Forwarded-Proto") != "https" {
http.Redirect(w, r, "https://"+r.Host+r.RequestURI, http.StatusMovedPermanently)
return
}
next.ServeHTTP(w, r)
})
})

Security Headers

Add Security Headers

router.Use(func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("X-Content-Type-Options", "nosniff")
w.Header().Set("X-Frame-Options", "DENY")
w.Header().Set("X-XSS-Protection", "1; mode=block")
w.Header().Set("Strict-Transport-Security", "max-age=31536000")
next.ServeHTTP(w, r)
})
})

File Upload Security

Validate File Uploads

func handleFileUpload(w http.ResponseWriter, r *http.Request) {
// Limit file size
r.ParseMultipartForm(10 << 20) // 10 MB

file, header, err := r.FormFile("file")
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
defer file.Close()

// Validate file type
if !isAllowedFileType(header.Filename) {
http.Error(w, "Invalid file type", http.StatusBadRequest)
return
}

// Validate file size
if header.Size > maxFileSize {
http.Error(w, "File too large", http.StatusBadRequest)
return
}

// Save file securely
// ...
}

Best Practices

  1. Always Use Parameterized Queries - Never concatenate SQL
  2. Validate All Input - Don't trust user input
  3. Hash Passwords - Never store plain text passwords
  4. Use HTTPS - Encrypt data in transit
  5. Keep Dependencies Updated - Update packages regularly
  6. Limit Rate - Prevent brute force attacks
  7. Log Security Events - Monitor for suspicious activity
  8. Regular Security Audits - Review code for vulnerabilities

Security Checklist

  • SQL injection protection (parameterized queries)
  • XSS protection (output escaping)
  • CSRF protection (tokens in forms)
  • Secure sessions (httpOnly, secure, sameSite)
  • Password hashing (bcrypt)
  • Input validation (all user input)
  • HTTPS enabled (production)
  • Security headers (X-Frame-Options, etc.)
  • Rate limiting (login attempts)
  • File upload validation (type, size)
  • Error messages (don't reveal sensitive info)
  • Dependency updates (regular updates)

Next Steps