Files
Charon/docs/security/api-key-handling.md
GitHub Actions 93894c517b fix(security): resolve API key logging vulnerability and enhance import validation
Critical security fix addressing CWE-312/315/359 (Cleartext Storage/Cookie
Storage/Privacy Exposure) where CrowdSec bouncer API keys were logged in cleartext.
Implemented maskAPIKey() utility to show only first 4 and last 4 characters,
protecting sensitive credentials in production logs.

Enhanced CrowdSec configuration import validation with:
- Zip bomb protection via 100x compression ratio limit
- Format validation rejecting zip archives (only tar.gz allowed)
- CrowdSec-specific YAML structure validation
- Rollback mechanism on validation failures

UX improvement: moved CrowdSec API key display from Security Dashboard to
CrowdSec Config page for better logical organization.

Comprehensive E2E test coverage:
- Created 10 test scenarios including valid import, missing files, invalid YAML,
  zip bombs, wrong formats, and corrupted archives
- 87/108 E2E tests passing (81% pass rate, 0 regressions)

Security validation:
- CodeQL: 0 CWE-312/315/359 findings (vulnerability fully resolved)
- Docker Image: 7 HIGH base image CVEs documented (non-blocking, Debian upstream)
- Pre-commit hooks: 13/13 passing (fixed 23 total linting issues)

Backend coverage: 82.2% (+1.1%)
Frontend coverage: 84.19% (+0.3%)
2026-02-04 00:12:13 +00:00

6.9 KiB

API Key Security Guidelines

Overview

This document outlines security best practices for handling API keys and other sensitive credentials in Charon. These guidelines help prevent common vulnerabilities like CWE-312 (Cleartext Storage of Sensitive Information), CWE-315 (Cleartext Storage in Cookie), and CWE-359 (Exposure of Private Personal Information).

Logging Best Practices

NEVER Log Sensitive Credentials

Critical Rule: Never log sensitive credentials (API keys, tokens, passwords) in plaintext.

Masking Implementation

Charon implements secure API key masking that shows only the first 4 and last 4 characters:

// ✅ GOOD: Masked key
logger.Infof("API Key: %s", maskAPIKey(apiKey))
// Output: "API Key: abcd...xyz9"

// ❌ BAD: Full key exposure
logger.Infof("API Key: %s", apiKey)
// Output: "API Key: abcd1234567890xyz9" (SECURITY RISK!)

Masking Rules

The maskAPIKey() function implements these rules:

  1. Empty keys: Returns [empty]
  2. Short keys (< 16 chars): Returns [REDACTED]
  3. Normal keys (≥ 16 chars): Shows first 4 + last 4 characters (e.g., abcd...xyz9)

These rules ensure that:

  • Keys cannot be reconstructed from logs
  • Users can still identify which key was used (by prefix/suffix)
  • Debugging remains possible without exposing secrets

Key Storage

File Storage Requirements

API keys must be stored with secure file permissions:

// Save with restricted permissions (owner read/write only)
err := os.WriteFile(keyFile, []byte(apiKey), 0600)

Required permissions: 0600 (rw-------)

  • Owner: read + write
  • Group: no access
  • Others: no access

Storage Best Practices

  1. Use secure file permissions (0600) for key files
  2. Store keys in environment variables for production deployments
  3. Never commit keys to version control (.gitignore all key files)
  4. Encrypt keys at rest when possible
  5. Use separate keys per environment (dev/staging/prod)

Key Validation

Format Validation

The validateAPIKeyFormat() function enforces these rules:

  • Length: 16-128 characters
  • Charset: Alphanumeric + underscore (_) + hyphen (-)
  • No spaces or special characters
// Valid keys
"api_key_1234567890123456"           // ✅
"api-key-ABCDEF1234567890"           // ✅
"1234567890123456"                   // ✅

// Invalid keys
"short"                               // ❌ Too short (< 16 chars)
strings.Repeat("a", 129)              // ❌ Too long (> 128 chars)
"api key with spaces"                 // ❌ Invalid characters
"api@key#special"                     // ❌ Invalid characters

Validation Benefits

  • Prevents weak/malformed keys
  • Detects potential key corruption
  • Provides early failure feedback
  • Improves security posture

Security Warnings

Log Aggregation Risks

If logs are shipped to external services (CloudWatch, Splunk, Datadog, etc.):

  • Masked keys are safe to log
  • Full keys would be exposed across multiple systems
  • Log retention policies apply to all destinations

Error Message Safety

Never include sensitive data in error messages:

// ✅ GOOD: Generic error
return fmt.Errorf("authentication failed")

// ❌ BAD: Leaks key info
return fmt.Errorf("invalid API key: %s", apiKey)

HTTP Response Safety

Never return API keys in HTTP responses:

// ✅ GOOD: Omit sensitive fields
c.JSON(200, gin.H{
    "status": "registered",
    "keyFile": "/path/to/key",  // Path only, not content
})

// ❌ BAD: Exposes key
c.JSON(200, gin.H{
    "apiKey": apiKey,  // SECURITY RISK!
})

Key Rotation

Rotation Best Practices

  1. Rotate keys regularly (every 90 days recommended)
  2. Rotate immediately after:
    • Suspected compromise
    • Employee offboarding
    • Log exposure incidents
    • Security audit findings
  3. Use graceful rotation:
    • Generate new key
    • Update configuration
    • Test with new key
    • Revoke old key

Rotation Procedure

  1. Generate new bouncer in CrowdSec:

    cscli bouncers add new-bouncer-name
    
  2. Update Charon configuration:

    # Update environment variable
    CHARON_SECURITY_CROWDSEC_API_KEY=new-key-here
    
    # Or update key file
    echo "new-key-here" > /path/to/bouncer.key
    chmod 0600 /path/to/bouncer.key
    
  3. Restart Charon to apply new key

  4. Revoke old bouncer:

    cscli bouncers delete old-bouncer-name
    

Incident Response

If Keys Are Exposed

If API keys are accidentally logged or exposed:

  1. Rotate the key immediately (see rotation procedure above)
  2. Purge logs containing the exposed key:
    • Local log files
    • Log aggregation services (CloudWatch, Splunk, etc.)
    • Backup archives
  3. Audit access logs for unauthorized usage
  4. Update incident response procedures to prevent recurrence
  5. Notify security team following your organization's procedures

Log Audit Procedure

To check if keys were exposed in logs:

# Search local logs (should find NO matches)
grep -r "full-api-key-pattern" /var/log/charon/

# Expected: No results (keys should be masked)

# If matches found, keys were exposed - follow incident response

Compliance

Standards Addressed

This implementation addresses:

  • CWE-312: Cleartext Storage of Sensitive Information
  • CWE-315: Cleartext Storage in Cookie
  • CWE-359: Exposure of Private Personal Information
  • OWASP A02:2021: Cryptographic Failures

Compliance Frameworks

Key handling practices align with:

  • GDPR: Personal data protection (Article 32)
  • PCI-DSS: Requirement 3.4 (Render PAN unreadable)
  • SOC 2: Security criteria (CC6.1 - Logical access controls)
  • ISO 27001: A.9.4.3 (Password management)

Testing

Security Test Coverage

All API key handling functions have comprehensive unit tests:

# Run security 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

Test Scenarios

Tests cover:

  • Empty keys → [empty]
  • Short keys (< 16) → [REDACTED]
  • Normal keys → abcd...xyz9
  • Length validation (16-128 chars)
  • Character set validation
  • File permissions (0600)
  • No full key exposure in logs

References

Updates

Date Change Author
2026-02-03 Initial documentation for Sprint 0 security fix GitHub Copilot

Last Updated: 2026-02-03 Next Review: 2026-05-03 (Quarterly)