# Security Best Practices

This document outlines security best practices for developing and maintaining Charon. These guidelines help prevent common vulnerabilities and ensure compliance with industry standards.

## Table of Contents

- [Secret Management](#secret-management)
- [Logging Security](#logging-security)
- [Input Validation](#input-validation)
- [File System Security](#file-system-security)
- [Database Security](#database-security)
- [API Security](#api-security)
- [Compliance](#compliance)
- [Security Testing](#security-testing)

---

## Secret Management

### Principles

1. **Never commit secrets to version control**
2. **Use environment variables for production**
3. **Rotate secrets regularly**
4. **Mask secrets in logs**
5. **Encrypt secrets at rest**

### API Keys and Tokens

#### Storage

- **Development**: Store in `.env` file (gitignored)
- **Production**: Use environment variables or secret management service
- **File storage**: Use 0600 permissions (owner read/write only)

```bash
# Example: Secure key file creation
echo "api-key-here" > /data/crowdsec/bouncer.key
chmod 0600 /data/crowdsec/bouncer.key
chown charon:charon /data/crowdsec/bouncer.key
```

#### Masking

Always mask secrets before logging:

```go
// ✅ GOOD: Masked secret
logger.Infof("API Key: %s", maskAPIKey(apiKey))

// ❌ BAD: Full secret exposed
logger.Infof("API Key: %s", apiKey)
```

Charon's masking rules:

- Empty: `[empty]`
- Short (< 16 chars): `[REDACTED]`
- Normal (≥ 16 chars): `abcd...xyz9` (first 4 + last 4)

#### Validation

Validate secret format before use:

```go
if !validateAPIKeyFormat(apiKey) {
    return fmt.Errorf("invalid API key format")
}
```

Requirements:

- Length: 16-128 characters
- Charset: Alphanumeric + underscore + hyphen
- No spaces or special characters

#### Rotation

Rotate secrets regularly:

1. **Schedule**: Every 90 days (recommended)
2. **Triggers**: After suspected compromise, employee offboarding, security incidents
3. **Process**:
   - Generate new secret
   - Update configuration
   - Test with new secret
   - Revoke old secret
   - Update documentation

### Passwords and Credentials

- **Storage**: Hash with bcrypt (cost factor ≥ 12) or Argon2
- **Transmission**: HTTPS only
- **Never log**: Full passwords or password hashes
- **Requirements**: Enforce minimum complexity and length

---

## Logging Security

### What to Log

✅ **Safe to log**:

- Timestamps
- User IDs (not usernames if PII)
- IP addresses (consider GDPR implications)
- Request paths (sanitize query parameters)
- Response status codes
- Error types (generic messages)
- Performance metrics

❌ **Never log**:

- Passwords or password hashes
- API keys or tokens (use masking)
- Session IDs (full values)
- Credit card numbers
- Social security numbers
- Personal health information (PHI)
- Any Personally Identifiable Information (PII)

### Log Sanitization

Before logging user input, sanitize:

```go
// ✅ GOOD: Sanitized logging
logger.Infof("Login attempt from IP: %s", sanitizeIP(ip))

// ❌ BAD: Direct user input
logger.Infof("Login attempt: username=%s password=%s", username, password)
```

### Log Retention

- **Development**: 7 days
- **Production**: 30-90 days (depends on compliance requirements)
- **Audit logs**: 1-7 years (depends on regulations)

**Important**: Shorter retention reduces exposure risk if logs are compromised.

### Log Aggregation

If using external log services (CloudWatch, Splunk, Datadog):

- Ensure logs are encrypted in transit (TLS)
- Ensure logs are encrypted at rest
- Redact sensitive data before shipping
- Apply same retention policies
- Audit access controls regularly

---

## Input Validation

### Principles

1. **Validate all inputs** (user-provided, file uploads, API requests)
2. **Whitelist approach**: Define what's allowed, reject everything else
3. **Fail securely**: Reject invalid input with generic error messages
4. **Sanitize before use**: Escape/encode for target context

### File Uploads

```go
// ✅ GOOD: Comprehensive validation
func validateUpload(file multipart.File, header *multipart.FileHeader) error {
    // 1. Check file size
    if header.Size > maxFileSize {
        return fmt.Errorf("file too large")
    }

    // 2. Validate file type (magic bytes, not extension)
    buf := make([]byte, 512)
    file.Read(buf)
    mimeType := http.DetectContentType(buf)
    if !isAllowedMimeType(mimeType) {
        return fmt.Errorf("invalid file type")
    }

    // 3. Sanitize filename
    safeName := sanitizeFilename(header.Filename)

    // 4. Check for path traversal
    if containsPathTraversal(safeName) {
        return fmt.Errorf("invalid filename")
    }

    return nil
}
```

### Path Traversal Prevention

```go
// ✅ GOOD: Secure path handling
func securePath(baseDir, userPath string) (string, error) {
    // Clean and resolve path
    fullPath := filepath.Join(baseDir, filepath.Clean(userPath))

    // Ensure result is within baseDir
    if !strings.HasPrefix(fullPath, baseDir) {
        return "", fmt.Errorf("path traversal detected")
    }

    return fullPath, nil
}

// ❌ BAD: Direct path join (vulnerable)
fullPath := baseDir + "/" + userPath
```

### SQL Injection Prevention

```go
// ✅ GOOD: Parameterized query
db.Where("email = ?", email).First(&user)

// ❌ BAD: String concatenation (vulnerable)
db.Raw("SELECT * FROM users WHERE email = '" + email + "'").Scan(&user)
```

### Command Injection Prevention

```go
// ✅ GOOD: Use exec.Command with separate arguments
cmd := exec.Command("cscli", "bouncers", "list")

// ❌ BAD: Shell with user input (vulnerable)
cmd := exec.Command("sh", "-c", "cscli bouncers list " + userInput)
```

---

## File System Security

### File Permissions

| File Type | Permissions | Owner | Rationale |
|-----------|-------------|-------|-----------|
| Secret files (keys, tokens) | 0600 | charon:charon | Owner read/write only |
| Configuration files | 0640 | charon:charon | Owner read/write, group read |
| Log files | 0640 | charon:charon | Owner read/write, group read |
| Executables | 0750 | root:charon | Owner read/write/execute, group read/execute |
| Data directories | 0750 | charon:charon | Owner full access, group read/execute |

### Directory Structure

```
/data/charon/
├── config/          (0750 charon:charon)
│   ├── config.yaml  (0640 charon:charon)
│   └── secrets/     (0700 charon:charon) - Secret storage
│       └── api.key  (0600 charon:charon)
├── logs/            (0750 charon:charon)
│   └── app.log      (0640 charon:charon)
└── data/            (0750 charon:charon)
```

### Temporary Files

```go
// ✅ GOOD: Secure temp file creation
f, err := os.CreateTemp("", "charon-*.tmp")
if err != nil {
    return err
}
defer os.Remove(f.Name())  // Clean up

// Set secure permissions
if err := os.Chmod(f.Name(), 0600); err != nil {
    return err
}
```

---

## Database Security

### Query Security

1. **Always use parameterized queries** (GORM `Where` with `?` placeholders)
2. **Validate all inputs** before database operations
3. **Use transactions** for multi-step operations
4. **Limit query results** (avoid SELECT *)
5. **Index sensitive columns** sparingly (balance security vs performance)

### Sensitive Data

| Data Type | Storage Method | Example |
|-----------|----------------|---------|
| Passwords | bcrypt hash | `bcrypt.GenerateFromPassword([]byte(password), 12)` |
| API Keys | Environment variable or encrypted field | `os.Getenv("API_KEY")` |
| Tokens | Hashed with random salt | `sha256(token + salt)` |
| PII | Encrypted at rest | AES-256-GCM |

### Migrations

```go
// ✅ GOOD: Add sensitive field with proper constraints
migrator.AutoMigrate(&User{})

// ❌ BAD: Store sensitive data in plaintext
// (Don't add columns like `password_plaintext`)
```

---

## API Security

### Authentication

- **Use JWT tokens** or session cookies with secure flags
- **Implement rate limiting** (prevent brute force)
- **Enforce HTTPS** in production
- **Validate all tokens** before processing requests

### Authorization

```go
// ✅ GOOD: Check user permissions
if !user.HasPermission("crowdsec:manage") {
    return c.JSON(403, gin.H{"error": "forbidden"})
}

// ❌ BAD: Assume user has access
// (No permission check)
```

### Rate Limiting

Protect endpoints from abuse:

```go
// Example: 100 requests per hour per IP
limiter := rate.NewLimiter(rate.Every(36*time.Second), 100)
```

**Critical endpoints** (require stricter limits):

- Login: 5 attempts per 15 minutes
- Password reset: 3 attempts per hour
- API key generation: 5 per day

### Input Validation

```go
// ✅ GOOD: Validate request body
type CreateBouncerRequest struct {
    Name string `json:"name" binding:"required,min=3,max=64,alphanum"`
}

if err := c.ShouldBindJSON(&req); err != nil {
    return c.JSON(400, gin.H{"error": "invalid request"})
}
```

### Error Handling

```go
// ✅ GOOD: Generic error message
return c.JSON(401, gin.H{"error": "authentication failed"})

// ❌ BAD: Reveals authentication details
return c.JSON(401, gin.H{"error": "invalid API key: abc123"})
```

---

## Compliance

### GDPR (General Data Protection Regulation)

**Applicable if**: Processing data of EU residents

**Requirements**:

1. **Data minimization**: Collect only necessary data
2. **Purpose limitation**: Use data only for stated purposes
3. **Storage limitation**: Delete data when no longer needed
4. **Security**: Implement appropriate technical measures (encryption, masking)
5. **Breach notification**: Report breaches within 72 hours

**Implementation**:

- ✅ Charon masks API keys in logs (prevents exposure of personal data)
- ✅ Secure file permissions (0600) protect sensitive data
- ✅ Log retention policies prevent indefinite storage
- ⚠️ Ensure API keys don't contain personal identifiers

**Reference**: [GDPR Article 32 - Security of processing](https://gdpr-info.eu/art-32-gdpr/)

---

### PCI-DSS (Payment Card Industry Data Security Standard)

**Applicable if**: Processing, storing, or transmitting credit card data

**Requirements**:

1. **Requirement 3.4**: Render PAN unreadable (encryption, masking)
2. **Requirement 8.2**: Strong authentication
3. **Requirement 10.2**: Audit trails
4. **Requirement 10.7**: Retain audit logs for 1 year

**Implementation**:

- ✅ Charon uses masking for sensitive credentials (same principle for PAN)
- ✅ Secure file permissions align with access control requirements
- ⚠️ Charon doesn't handle payment cards directly (delegated to payment processors)

**Reference**: [PCI-DSS Quick Reference Guide](https://www.pcisecuritystandards.org/)

---

### SOC 2 (System and Organization Controls)

**Applicable if**: SaaS providers, cloud services

**Trust Service Criteria**:

1. **CC6.1**: Logical access controls (authentication, authorization)
2. **CC6.6**: Encryption of data in transit
3. **CC6.7**: Encryption of data at rest
4. **CC7.2**: Monitoring and detection (logging, alerting)

**Implementation**:

- ✅ API key validation ensures strong credentials (CC6.1)
- ✅ File permissions (0600) protect data at rest (CC6.7)
- ✅ Masked logging enables monitoring without exposing secrets (CC7.2)
- ⚠️ Ensure HTTPS enforcement for data in transit (CC6.6)

**Reference**: [SOC 2 Trust Services Criteria](https://www.aicpa.org/interestareas/frc/assuranceadvisoryservices/trustdataintegritytaskforce)

---

### ISO 27001 (Information Security Management)

**Applicable to**: Any organization implementing ISMS

**Key Controls**:

1. **A.9.4.3**: Password management systems
2. **A.10.1.1**: Cryptographic controls
3. **A.12.4.1**: Event logging
4. **A.18.1.5**: Protection of personal data

**Implementation**:

- ✅ API key format validation (minimum 16 chars, charset restrictions)
- ✅ Key rotation procedures documented
- ✅ Secure storage with file permissions (0600)
- ✅ Masked logging protects sensitive data

**Reference**: [ISO 27001:2013 Controls](https://www.iso.org/standard/54534.html)

---

### Compliance Summary Table

| Framework | Key Requirement | Charon Implementation | Status |
|-----------|----------------|----------------------|--------|
| **GDPR** | Data protection (Art. 32) | API key masking, secure storage | ✅ Compliant |
| **PCI-DSS** | Render PAN unreadable (Req. 3.4) | Masking utility (same principle) | ✅ Aligned |
| **SOC 2** | Logical access controls (CC6.1) | Key validation, file permissions | ✅ Compliant |
| **ISO 27001** | Password management (A.9.4.3) | Key rotation, validation | ✅ Compliant |

---

## Security Testing

### Static Analysis

```bash
# Run CodeQL security scan
.github/skills/scripts/skill-runner.sh security-codeql-scan

# Expected: 0 CWE-312/315/359 findings
```

### Unit Tests

```bash
# Run security-focused unit tests
go test ./backend/internal/api/handlers -run TestMaskAPIKey -v
go test ./backend/internal/api/handlers -run TestValidateAPIKeyFormat -v
go test ./backend/internal/api/handlers -run TestSaveKeyToFile_SecurePermissions -v
```

### Integration Tests

```bash
# Run Playwright E2E tests
.github/skills/scripts/skill-runner.sh test-e2e-playwright

# Check for exposed secrets in test logs
grep -i "api[_-]key\|token\|password" playwright-report/index.html
# Expected: Only masked values (abcd...xyz9) or no matches
```

### Penetration Testing

**Recommended schedule**: Annual or after major releases

**Focus areas**:

1. Authentication bypass
2. Authorization vulnerabilities
3. SQL injection
4. Path traversal
5. Information disclosure (logs, errors)
6. Rate limiting effectiveness

---

## Security Checklist

### Before Every Release

- [ ] Run CodeQL scan (0 critical findings)
- [ ] Run unit tests (100% pass)
- [ ] Run integration tests (100% pass)
- [ ] Check for hardcoded secrets (TruffleHog, Semgrep)
- [ ] Review log output for sensitive data exposure
- [ ] Verify file permissions (secrets: 0600, configs: 0640)
- [ ] Update dependencies (no known CVEs)
- [ ] Review security documentation updates
- [ ] Test secret rotation procedure
- [ ] Verify HTTPS enforcement in production

### During Code Review

- [ ] No secrets in environment variables (use .env)
- [ ] All secrets are masked in logs
- [ ] Input validation on all user-provided data
- [ ] Parameterized queries (no string concatenation)
- [ ] Secure file permissions (0600 for secrets)
- [ ] Error messages don't reveal sensitive info
- [ ] No commented-out secrets or debugging code
- [ ] Security tests added for new features

### After Security Incident

- [ ] Rotate all affected secrets immediately
- [ ] Audit access logs for unauthorized use
- [ ] Purge logs containing exposed secrets
- [ ] Notify affected users (if PII exposed)
- [ ] Update incident response procedures
- [ ] Document lessons learned
- [ ] Implement additional controls to prevent recurrence

---

## Resources

### Internal Documentation

- [API Key Handling Guide](./security/api-key-handling.md)
- [ARCHITECTURE.md](../ARCHITECTURE.md)
- [CONTRIBUTING.md](../CONTRIBUTING.md)

### External References

- [OWASP Top 10](https://owasp.org/Top10/)
- [OWASP Cheat Sheet Series](https://cheatsheetseries.owasp.org/)
- [CWE Top 25](https://cwe.mitre.org/top25/)
- [NIST Cybersecurity Framework](https://www.nist.gov/cyberframework)
- [SANS Top 25 Software Errors](https://www.sans.org/top25-software-errors/)

### Security Standards

- [GDPR Official Text](https://gdpr-info.eu/)
- [PCI-DSS Standards](https://www.pcisecuritystandards.org/)
- [SOC 2 Trust Services](https://www.aicpa.org/)
- [ISO 27001](https://www.iso.org/standard/54534.html)

---

## Updates

| Date | Change | Author |
|------|--------|--------|
| 2026-02-03 | Initial security practices documentation | GitHub Copilot |

---

**Last Updated**: 2026-02-03
**Next Review**: 2026-05-03 (Quarterly)
**Owner**: Security Team / Lead Developer
