| name | security |
| description | Security best practices for web applications. Use when implementing authentication, authorization, input validation, or reviewing code for vulnerabilities. |
Security Skill
Security guidelines based on OWASP, Trail of Bits, and industry best practices.
When to Use This Skill
- Implementing authentication/authorization
- Validating and sanitizing user input
- Reviewing code for security vulnerabilities
- Configuring secure deployments
- Handling sensitive data
🔐 Authentication
Password Handling
import "golang.org/x/crypto/bcrypt"
func hashPassword(password string) (string, error) {
bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
return string(bytes), err
}
func checkPassword(password, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil
}
JWT Token Handling
import "github.com/golang-jwt/jwt/v5"
type Claims struct {
UserID string `json:"user_id"`
Role string `json:"role"`
jwt.RegisteredClaims
}
func generateToken(userID, role string, secret []byte) (string, error) {
claims := Claims{
UserID: userID,
Role: role,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(24 * time.Hour)),
IssuedAt: jwt.NewNumericDate(time.Now()),
Issuer: "llmproxy",
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(secret)
}
func validateToken(tokenString string, secret []byte) (*Claims, error) {
token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(t *jwt.Token) (interface{}, error) {
if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", t.Header["alg"])
}
return secret, nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
return claims, nil
}
return nil, fmt.Errorf("invalid token")
}
Secure Session Management
- Use secure, httpOnly cookies
- Implement session timeout
- Regenerate session ID after login
- Invalidate session on logout
http.SetCookie(w, &http.Cookie{
Name: "session",
Value: sessionToken,
HttpOnly: true,
Secure: true,
SameSite: http.SameSiteStrictMode,
MaxAge: 3600,
Path: "/",
})
🛡️ Authorization
Role-Based Access Control (RBAC)
type Permission string
const (
PermissionRead Permission = "read"
PermissionWrite Permission = "write"
PermissionDelete Permission = "delete"
PermissionAdmin Permission = "admin"
)
type Role struct {
Name string
Permissions []Permission
}
var roles = map[string]Role{
"viewer": {Name: "viewer", Permissions: []Permission{PermissionRead}},
"editor": {Name: "editor", Permissions: []Permission{PermissionRead, PermissionWrite}},
"admin": {Name: "admin", Permissions: []Permission{PermissionRead, PermissionWrite, PermissionDelete, PermissionAdmin}},
}
func hasPermission(userRole string, required Permission) bool {
role, ok := roles[userRole]
if !ok {
return false
}
for _, p := range role.Permissions {
if p == required {
return true
}
}
return false
}
Authorization Middleware
func RequirePermission(permission Permission) Middleware {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
claims := getUserClaims(r.Context())
if claims == nil {
http.Error(w, "unauthorized", http.StatusUnauthorized)
return
}
if !hasPermission(claims.Role, permission) {
http.Error(w, "forbidden", http.StatusForbidden)
return
}
next.ServeHTTP(w, r)
})
}
}
🔍 Input Validation
Validation Rules
import "github.com/go-playground/validator/v10"
type CreateUserRequest struct {
Name string `json:"name" validate:"required,min=2,max=100"`
Email string `json:"email" validate:"required,email"`
Password string `json:"password" validate:"required,min=8,max=72"`
Age int `json:"age" validate:"omitempty,min=0,max=150"`
}
var validate = validator.New()
func validateRequest(req interface{}) error {
if err := validate.Struct(req); err != nil {
var validationErrors validator.ValidationErrors
if errors.As(err, &validationErrors) {
return formatValidationErrors(validationErrors)
}
return err
}
return nil
}
SQL Injection Prevention
query := "SELECT * FROM users WHERE id = " + userInput
query := "SELECT * FROM users WHERE id = $1"
row := db.QueryRow(query, userInput)
query := "SELECT * FROM users WHERE name = :name AND status = :status"
rows, err := db.NamedQuery(query, map[string]interface{}{
"name": name,
"status": status,
})
XSS Prevention
import "html"
func sanitizeOutput(input string) string {
return html.EscapeString(input)
}
<div dangerouslySetInnerHTML={{ __html: userInput }} />
<div>{userInput}</div>
🔑 Secrets Management
Environment Variables
const apiKey = "sk-1234567890abcdef"
apiKey := os.Getenv("API_KEY")
if apiKey == "" {
log.Fatal("API_KEY environment variable required")
}
Configuration Structure
type Config struct {
Database struct {
Host string `env:"DB_HOST" envDefault:"localhost"`
Port int `env:"DB_PORT" envDefault:"5432"`
Password string `env:"DB_PASSWORD,required"`
}
JWT struct {
Secret string `env:"JWT_SECRET,required"`
Expiration time.Duration `env:"JWT_EXPIRATION" envDefault:"24h"`
}
}
.gitignore Secrets
# Environment files
.env
.env.local
.env.*.local
# Config files with secrets
config.yaml
secrets/
# Key files
*.pem
*.key
🌐 HTTP Security Headers
func SecurityHeadersMiddleware(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("Content-Security-Policy",
"default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'")
w.Header().Set("Strict-Transport-Security",
"max-age=31536000; includeSubDomains")
w.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin")
next.ServeHTTP(w, r)
})
}
⚡ Rate Limiting
import "golang.org/x/time/rate"
type RateLimiter struct {
limiters map[string]*rate.Limiter
mu sync.RWMutex
rate rate.Limit
burst int
}
func NewRateLimiter(r rate.Limit, b int) *RateLimiter {
return &RateLimiter{
limiters: make(map[string]*rate.Limiter),
rate: r,
burst: b,
}
}
func (rl *RateLimiter) getLimiter(key string) *rate.Limiter {
rl.mu.Lock()
defer rl.mu.Unlock()
limiter, exists := rl.limiters[key]
if !exists {
limiter = rate.NewLimiter(rl.rate, rl.burst)
rl.limiters[key] = limiter
}
return limiter
}
func (rl *RateLimiter) Middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ip := getClientIP(r)
limiter := rl.getLimiter(ip)
if !limiter.Allow() {
http.Error(w, "rate limit exceeded", http.StatusTooManyRequests)
return
}
next.ServeHTTP(w, r)
})
}
📋 Security Checklist
Authentication
Authorization
Input Validation
Data Protection
Logging & Monitoring
📚 References