This document outlines the security features and best practices when using GoIgniter framework.
- Security Headers Middleware
- CSRF Protection
- Rate Limiting
- Session Security
- File Upload Security
- CORS Configuration
- Error Handling in Production
- Request Body Limits
The SecurityHeaders middleware adds essential security headers to all responses.
package main
import (
"github.com/semutdev/goigniter/system/core"
"github.com/semutdev/goigniter/system/middleware"
)
func main() {
app := core.New()
// Basic security headers
app.Use(middleware.SecurityHeaders())
// Or with custom configuration
app.Use(middleware.SecurityHeadersWithConfig(middleware.SecurityConfig{
XSSProtection: true,
ContentTypeNosniff: true,
XFrameOptions: "DENY",
HSTS: true, // Only for HTTPS
HSTSMaxAge: 31536000,
HSTSIncludeSubdomains: true,
ContentSecurityPolicy: "default-src 'self'",
ReferrerPolicy: "strict-origin-when-cross-origin",
}))
app.Run(":8080")
}| Header | Value | Purpose |
|---|---|---|
| X-XSS-Protection | 1; mode=block |
Enables browser XSS filter |
| X-Content-Type-Options | nosniff |
Prevents MIME type sniffing |
| X-Frame-Options | DENY |
Prevents clickjacking |
| Strict-Transport-Security | max-age=31536000 |
Forces HTTPS (when enabled) |
| Referrer-Policy | strict-origin-when-cross-origin |
Controls referrer information |
CSRF (Cross-Site Request Forgery) protection is essential for web applications with forms.
package main
import (
"github.com/semutdev/goigniter/system/core"
"github.com/semutdev/goigniter/system/middleware"
)
func main() {
app := core.New()
// Enable CSRF protection
app.Use(middleware.CSRF())
// For forms, include CSRF token
app.GET("/form", func(c *core.Context) error {
token := middleware.CSRFToken(c)
html := `<form method="POST">
<input type="hidden" name="csrf_token" value="` + token + `">
<input type="text" name="data">
<button type="submit">Submit</button>
</form>`
return c.HTML(200, html)
})
app.POST("/form", func(c *core.Context) error {
// CSRF is automatically validated
return c.String(200, "Success!")
})
app.Run(":8080")
}// Skip CSRF for API routes (using token authentication instead)
app.Use(middleware.CSRFWithConfig(middleware.CSRFConfig{
Skipper: middleware.CSRFAPISkipper("/api"),
}))
// Or skip specific paths
app.Use(middleware.CSRFWithConfig(middleware.CSRFConfig{
Skipper: middleware.CSRFSkipper("/api", "/webhook"),
}))For AJAX requests, include the token in a header:
// Get token from meta tag or cookie
const token = document.querySelector('meta[name="csrf-token"]').content;
fetch('/api/data', {
method: 'POST',
headers: {
'X-CSRF-Token': token,
'Content-Type': 'application/json'
},
body: JSON.stringify({ data: 'example' })
});Rate limiting protects against DoS attacks and abuse.
// Limit to 100 requests per minute
app.Use(middleware.RateLimit(100, time.Minute))
// Limit to 10 requests per second
app.Use(middleware.RateLimit(10, time.Second))app.Use(middleware.RateLimitWithConfig(middleware.RateLimitConfig{
Max: 100,
Window: time.Minute,
Message: "Too many requests, please try again later",
KeyFunc: func(c *core.Context) string {
// Use IP + User ID for more granular limiting
return c.IP() + "_" + c.GetString("user_id")
},
}))// Global rate limit
app.Use(middleware.RateLimit(1000, time.Minute))
// API rate limit (stricter)
apiGroup := app.Group("/api")
apiGroup.Use(middleware.RateLimit(100, time.Minute))Sessions support both HMAC signing and AES-256 encryption.
package main
import (
"github.com/semutdev/goigniter/system/core"
"github.com/semutdev/goigniter/system/libraries/session"
)
func main() {
// Initialize session with secret key
session.Init(session.Config{
Secret: "your-secret-key-at-least-32-characters-long",
CookieName: "goigniter_session",
MaxAge: 86400, // 24 hours
Secure: true, // Set true in production (HTTPS)
SameSite: http.SameSiteStrictMode,
})
app := core.New()
app.GET("/", func(c *core.Context) error {
sess := session.Get(c)
sess.Set("user_id", 123)
sess.Save(c)
return c.String(200, "Session saved")
})
app.Run(":8080")
}import (
"github.com/semutdev/goigniter/system/libraries/session"
)
func main() {
// Generate a secure encryption key (32 bytes for AES-256)
encryptKey := session.MustGenerateKey(32)
session.Init(session.Config{
Secret: "your-secret-key-at-least-32-characters-long",
Encrypt: true,
EncryptKey: encryptKey,
Secure: true, // HTTPS only
SameSite: http.SameSiteStrictMode,
})
// ... rest of application
}- Always use HTTPS (
Secure: true) - Use Strict SameSite (
SameSiteStrictMode) - Encrypt sensitive data (
Encrypt: true) - Use strong secret keys (minimum 32 bytes)
- Regenerate session after login
The upload library has built-in security protections.
import (
"github.com/semutdev/goigniter/system/libraries/upload"
)
func handleUpload(c *core.Context) error {
uploader := upload.New(upload.Config{
UploadPath: "./uploads",
AllowedTypes: "jpg|jpeg|png|gif|pdf",
MaxSize: 5000, // 5MB in KB
FileName: "random",
CreateDirs: true,
})
result, err := uploader.Do("file", c.Request)
if err != nil {
// Handle specific errors
switch err {
case upload.ErrFileTooBig:
return c.String(400, "File too large")
case upload.ErrInvalidType:
return c.String(400, "File type not allowed")
case upload.ErrSuspiciousFile:
return c.String(400, "Suspicious file detected")
case upload.ErrMimeMismatch:
return c.String(400, "File content doesn't match extension")
}
return c.String(500, "Upload failed")
}
return c.JSON(200, result)
}- Dangerous extension blocking - PHP, EXE, BAT, SH, etc. are blocked
- Double extension detection -
file.php.jpgis blocked - Null byte injection prevention -
file.php%00.jpgis blocked - Path traversal prevention -
../in filename is blocked - MIME type validation - Extension must match content type
- Magic byte validation - Actual file content is checked
- Store uploads outside web root
- Use random filenames to prevent enumeration
- Set proper file permissions (not executable)
- Scan for viruses using ClamAV or similar
- Limit file size appropriately
CORS (Cross-Origin Resource Sharing) should be configured carefully.
// Recommended: Explicitly list allowed origins
app.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOrigins: []string{"https://yourdomain.com", "https://app.yourdomain.com"},
AllowMethods: []string{"GET", "POST", "PUT", "DELETE"},
AllowHeaders: []string{"Origin", "Content-Type", "Authorization"},
AllowCredentials: true,
MaxAge: 86400,
}))
// For development only (not recommended for production)
app.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOrigins: []string{"http://localhost:3000"},
}))- Never use
AllowOrigins: []string{"*"}withAllowCredentials: true - Default configuration now has empty
AllowOriginsfor security
Never expose internal error details to users.
app.Use(middleware.RecoveryWithConfig(middleware.RecoveryConfig{
HideErrorDetails: true,
ProductionMessage: "Something went wrong. Please try again later.",
DisablePrintStack: false, // Log stack internally, don't show to user
LogFunc: func(c *core.Context, err any, stack []byte) {
// Log to your monitoring system
log.Printf("[ERROR] %v\nRequest: %s %s\nStack: %s",
err, c.Method(), c.Path(), stack)
},
}))Prevent memory exhaustion attacks by limiting request body size.
app.POST("/api/data", func(c *core.Context) error {
var data map[string]any
// Bind with 1MB limit
err := c.BindWithLimit(&data, 1<<20) // 1MB
if err == core.ErrBodyTooLarge {
return c.String(413, "Request body too large")
}
if err != nil {
return c.String(400, "Invalid request")
}
return c.JSON(200, data)
})package main
import (
"log"
"net/http"
"os"
"time"
"github.com/semutdev/goigniter/system/core"
"github.com/semutdev/goigniter/system/libraries/session"
"github.com/semutdev/goigniter/system/middleware"
)
func main() {
// Session setup with encryption
session.Init(session.Config{
Secret: os.Getenv("SESSION_SECRET"),
Encrypt: true,
EncryptKey: session.MustGenerateKey(32),
Secure: true,
SameSite: http.SameSiteStrictMode,
})
app := core.New()
// Security headers
app.Use(middleware.SecurityHeaders())
// Rate limiting
app.Use(middleware.RateLimit(100, time.Minute))
// Recovery (hide details in production)
app.Use(middleware.RecoveryWithConfig(middleware.RecoveryConfig{
HideErrorDetails: os.Getenv("APP_ENV") == "production",
ProductionMessage: "Internal Server Error",
}))
// CSRF (skip for API routes)
app.Use(middleware.CSRFWithConfig(middleware.CSRFConfig{
Skipper: middleware.CSRFAPISkipper("/api"),
}))
// CORS for specific origins only
app.Use(middleware.CORSWithConfig(middleware.CORSConfig{
AllowOrigins: []string{"https://yourdomain.com"},
}))
// Your routes here...
log.Fatal(app.Run(":8080"))
}- Enable Security Headers middleware
- Enable CSRF protection for web routes
- Configure Rate Limiting
- Use encrypted sessions with HTTPS
- Validate file uploads
- Configure CORS with explicit origins
- Hide error details in production
- Limit request body size
- Use HTTPS in production
- Keep dependencies updated