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%)
262 lines
6.9 KiB
Markdown
262 lines
6.9 KiB
Markdown
# 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:
|
|
|
|
```go
|
|
// ✅ 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:
|
|
|
|
```go
|
|
// 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**
|
|
|
|
```go
|
|
// 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**:
|
|
|
|
```go
|
|
// ✅ 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**:
|
|
|
|
```go
|
|
// ✅ 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:
|
|
```bash
|
|
cscli bouncers add new-bouncer-name
|
|
```
|
|
|
|
2. Update Charon configuration:
|
|
```bash
|
|
# 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:
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
# 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:
|
|
|
|
```bash
|
|
# 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
|
|
|
|
- [OWASP Logging Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Logging_Cheat_Sheet.html)
|
|
- [CWE-312: Cleartext Storage](https://cwe.mitre.org/data/definitions/312.html)
|
|
- [CWE-315: Cookie Storage](https://cwe.mitre.org/data/definitions/315.html)
|
|
- [CWE-359: Privacy Exposure](https://cwe.mitre.org/data/definitions/359.html)
|
|
- [NIST SP 800-63B: Digital Identity Guidelines](https://pages.nist.gov/800-63-3/sp800-63b.html)
|
|
|
|
## 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)
|