504 lines
13 KiB
Markdown
504 lines
13 KiB
Markdown
|
|
# Security-Aware Documentation
|
|
|
|
## Overview
|
|
|
|
Document systems without compromising security. Core principle: **Documentation should inform, not expose**.
|
|
|
|
**Key insight**: Examples with real credentials/PII leak secrets. Obviously fake examples are safe and clear.
|
|
|
|
## When to Use
|
|
|
|
Load this skill when:
|
|
- Documenting authentication/authorization
|
|
- Creating API examples with credentials
|
|
- Writing about systems handling PII or classified data
|
|
- Documenting security features
|
|
|
|
**Symptoms you need this**:
|
|
- "How do I show API key example without exposing real keys?"
|
|
- Writing documentation for healthcare/finance systems
|
|
- Creating examples with user data
|
|
- Documenting security configurations
|
|
|
|
**Don't use for**:
|
|
- General documentation without security concerns
|
|
- Internal-only docs (though still good practice)
|
|
|
|
## Sanitizing Examples
|
|
|
|
### Rule 1: Never Use Real Credentials
|
|
|
|
❌ **WRONG**:
|
|
```bash
|
|
# Don't mask real secrets
|
|
curl -H "Authorization: Bearer sk_live_51Hx***REDACTED***" \
|
|
https://api.example.com/users
|
|
```
|
|
|
|
**Problem**: Pattern `sk_live_51Hx...` suggests real Stripe key structure. Reader might think they should unmask it.
|
|
|
|
✅ **RIGHT**:
|
|
```bash
|
|
# Generate obviously fake credentials
|
|
curl -H "Authorization: Bearer fake_key_abc123_for_docs_only" \
|
|
https://api.example.com/users
|
|
```
|
|
|
|
**Better**: Clearly fake, no confusion possible.
|
|
|
|
|
|
### Rule 2: Use Obviously Fake Values
|
|
|
|
**Fake Credentials Pattern**:
|
|
```
|
|
fake_[type]_[random]_for_docs_only
|
|
```
|
|
|
|
**Examples**:
|
|
- API key: `fake_api_key_abc123_for_docs_only`
|
|
- JWT token: `eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.FAKE_TOKEN_FOR_DOCUMENTATION.fake_signature_do_not_use`
|
|
- Database password: `fake_password_p@ssw0rd_example_only`
|
|
- SSH key: `fake_ssh_key_AAAAB3NzaC1yc2EAAAADAQABAAABAQ... (truncated for docs)`
|
|
|
|
**Fake PII Pattern**:
|
|
```
|
|
[common_name]@example.com
|
|
000-00-0000 (SSN)
|
|
+1-555-0123 (phone - 555 is reserved for fiction)
|
|
```
|
|
|
|
**Examples**:
|
|
- Email: `jane.doe@example.com`, `user@example.org`
|
|
- SSN: `000-00-0000`, `123-45-6789` (invalid format)
|
|
- Phone: `+1-555-0100`, `+1-555-0199`
|
|
- Address: `123 Main Street, Anytown, ST 12345`
|
|
|
|
|
|
### Rule 3: Use Reserved Domains
|
|
|
|
**Reserved for documentation** (RFC 2606):
|
|
- `example.com`
|
|
- `example.net`
|
|
- `example.org`
|
|
- `test.com` (not official but commonly accepted)
|
|
|
|
❌ **WRONG**:
|
|
```javascript
|
|
const API_URL = 'https://api.acmecorp.com'; // Real company?
|
|
```
|
|
|
|
✅ **RIGHT**:
|
|
```javascript
|
|
const API_URL = 'https://api.example.com'; // Obviously fake
|
|
```
|
|
|
|
|
|
### Rule 4: Complete Fake Examples (Not Partial)
|
|
|
|
❌ **WRONG**:
|
|
```python
|
|
# Partial example - reader must guess
|
|
api_key = "YOUR_API_KEY_HERE"
|
|
client = APIClient(api_key)
|
|
```
|
|
|
|
**Problem**: Reader doesn't know what `YOUR_API_KEY_HERE` should look like.
|
|
|
|
✅ **RIGHT**:
|
|
```python
|
|
# Complete fake example - copy-paste-run (with fake backend)
|
|
api_key = "fake_api_key_abc123_for_docs_only"
|
|
client = APIClient(api_key)
|
|
|
|
# For real usage:
|
|
# 1. Get API key from https://dashboard.example.com/settings
|
|
# 2. Replace fake_api_key_abc123_for_docs_only with your real key
|
|
# 3. Never commit real keys to git
|
|
```
|
|
|
|
**Better**: Complete example + clear instructions for real usage.
|
|
|
|
|
|
## Threat Disclosure Decisions
|
|
|
|
### Document: Security Features Users Must Configure
|
|
|
|
✅ **DO document**:
|
|
```markdown
|
|
## Security Configuration
|
|
|
|
### Enable MFA (Required for Production)
|
|
|
|
Multi-factor authentication prevents unauthorized access even if passwords are compromised.
|
|
|
|
Enable MFA for all admin accounts:
|
|
\```bash
|
|
user-admin mfa enable --user admin@example.com --method totp
|
|
\```
|
|
|
|
**Security impact**: Without MFA, stolen passwords grant full access.
|
|
```
|
|
|
|
|
|
### Document: Security Best Practices
|
|
|
|
✅ **DO document**:
|
|
```markdown
|
|
## Hardening Guide
|
|
|
|
### Disable Unused Services
|
|
|
|
**Threat**: Unused services increase attack surface.
|
|
|
|
Disable SSH if not needed:
|
|
\```bash
|
|
systemctl disable sshd
|
|
systemctl stop sshd
|
|
\```
|
|
|
|
**Verification**: `systemctl status sshd` should show "inactive (dead)"
|
|
```
|
|
|
|
|
|
### Don't Document: Specific Vulnerabilities
|
|
|
|
❌ **DON'T document**:
|
|
```markdown
|
|
## Known Issues
|
|
|
|
### CVE-2024-12345: SQL Injection in /api/users Endpoint
|
|
|
|
Vulnerable code in `user_controller.py:45`:
|
|
\```python
|
|
query = f"SELECT * FROM users WHERE id = {user_id}" # Vulnerable!
|
|
\```
|
|
|
|
Attacker can inject SQL via: `/api/users?id=1 OR 1=1`
|
|
```
|
|
|
|
**Problem**: Provides exploit guide to attackers.
|
|
|
|
✅ **DO instead**: Coordinate with security team for disclosure. Document fix after patch released:
|
|
```markdown
|
|
## Security Updates
|
|
|
|
### Version 2.1.5 (2024-03-15)
|
|
|
|
**Security fix**: Resolved input validation issue in user API (CVE-2024-12345).
|
|
Upgrade immediately.
|
|
|
|
For technical details, see our security advisory: [link]
|
|
```
|
|
|
|
|
|
### Don't Document: Internal Security Architecture (Unless Necessary)
|
|
|
|
❌ **DON'T document** (in public docs):
|
|
```markdown
|
|
## Internal Security Architecture
|
|
|
|
Our secrets vault runs on ec2-10-0-1-50.internal with:
|
|
- Port 8200 (HTTP API)
|
|
- Port 8201 (cluster communication)
|
|
- Root token stored in S3 bucket: company-secrets-prod
|
|
- Unsealing keys split across: admin1@company.com, admin2@company.com, admin3@company.com
|
|
```
|
|
|
|
**Problem**: Reveals infrastructure details aiding attackers (IP addresses, ports, bucket names, key custodians).
|
|
|
|
✅ **DO instead**: Document what users need, abstract internals:
|
|
```markdown
|
|
## Secrets Management
|
|
|
|
Secrets are stored in an encrypted vault. To access secrets:
|
|
|
|
1. Request access via [access-request-form]
|
|
2. Use provided vault token
|
|
3. Tokens expire after 8 hours
|
|
|
|
See [secrets-access-guide] for details.
|
|
```
|
|
|
|
|
|
## Compliance Sensitivity
|
|
|
|
### Rule: Document Controls Without Revealing Weaknesses
|
|
|
|
❌ **WRONG**:
|
|
```markdown
|
|
## SOC2 Compliance
|
|
|
|
### Access Control (CC6.1)
|
|
|
|
**Control**: Role-based access control (RBAC)
|
|
|
|
**Current gaps**:
|
|
- Admin users can bypass RBAC via debug mode (known issue #245)
|
|
- No access reviews conducted (planned for Q3)
|
|
- 3 dormant admin accounts still active (cleanup delayed)
|
|
```
|
|
|
|
**Problem**: Audit report publicly reveals control weaknesses.
|
|
|
|
✅ **RIGHT**:
|
|
```markdown
|
|
## SOC2 Compliance
|
|
|
|
### Access Control (CC6.1)
|
|
|
|
**Control**: Role-based access control (RBAC)
|
|
|
|
**Implementation**:
|
|
- Authentication: MFA required for admin accounts
|
|
- Authorization: Permissions enforced at API layer and database layer
|
|
- Access reviews: Quarterly review of all privileged accounts
|
|
- Account lifecycle: Automated disablement after 30 days inactivity
|
|
|
|
**Audit evidence**: Available to authorized auditors via [compliance-portal]
|
|
```
|
|
|
|
**Better**: Focus on what exists, keep gaps internal.
|
|
|
|
|
|
## Redaction Patterns
|
|
|
|
### Logs: Redact Sensitive Data
|
|
|
|
❌ **WRONG**:
|
|
```
|
|
[2024-03-15 10:23:45] User login: user=john.smith@acme.com, password=MyP@ssw0rd123, token=eyJhbGci...
|
|
[2024-03-15 10:24:12] API call: GET /users/12345, auth_token=sk_live_51HxAbC123...
|
|
```
|
|
|
|
✅ **RIGHT**:
|
|
```
|
|
[2024-03-15 10:23:45] User login: user=john.smith@acme.com, password=[REDACTED], token=[REDACTED]
|
|
[2024-03-15 10:24:12] API call: GET /users/12345, auth_token=[REDACTED]
|
|
```
|
|
|
|
**Even better** (for docs): Use fake data:
|
|
```
|
|
[2024-03-15 10:23:45] User login: user=jane.doe@example.com, password=[REDACTED], token=[REDACTED]
|
|
[2024-03-15 10:24:12] API call: GET /users/67890, auth_token=[REDACTED]
|
|
```
|
|
|
|
|
|
### Screenshots: Blur Sensitive Data
|
|
|
|
**Before sharing screenshot**:
|
|
1. Use test account (user@example.com, fake data)
|
|
2. Blur any real data (names, emails, IDs)
|
|
3. Use browser extensions: "Redact for Screenshot"
|
|
|
|
❌ **WRONG**: Screenshot with production data visible
|
|
|
|
✅ **RIGHT**: Screenshot with:
|
|
- Fake user names (Jane Doe, John Smith)
|
|
- Fake emails (jane@example.com)
|
|
- Blurred real data if any leaked
|
|
|
|
|
|
### Diagrams: Anonymize Infrastructure
|
|
|
|
❌ **WRONG**:
|
|
```
|
|
[Load Balancer: lb-prod-01.company.com (52.12.34.56)]
|
|
↓
|
|
[API Servers: api-01.internal (10.0.1.10), api-02.internal (10.0.1.11)]
|
|
↓
|
|
[Database: postgres-master.internal (10.0.2.50)]
|
|
```
|
|
|
|
✅ **RIGHT**:
|
|
```
|
|
[Load Balancer: lb.example.com (203.0.113.10)] # RFC 5737 documentation IP
|
|
↓
|
|
[API Servers: api-01, api-02 (192.168.1.10-11)] # RFC 1918 private IP
|
|
↓
|
|
[Database: postgres-master (192.168.2.50)]
|
|
```
|
|
|
|
|
|
### Database Schemas: Use Synthetic Data
|
|
|
|
❌ **WRONG**:
|
|
```sql
|
|
-- Example users table
|
|
SELECT * FROM users LIMIT 3;
|
|
|
|
| id | email | ssn | credit_card |
|
|
|----|--------------------------|-------------|------------------|
|
|
| 1 | alice@acmecorp.com | 123-45-6789 | 4532-1234-5678-9012 |
|
|
| 2 | bob@acmecorp.com | 987-65-4321 | 5105-1051-0510-5100 |
|
|
| 3 | charlie@acmecorp.com | 456-78-9123 | 3782-822463-10005 |
|
|
```
|
|
|
|
✅ **RIGHT**:
|
|
```sql
|
|
-- Example users table (synthetic data)
|
|
SELECT * FROM users LIMIT 3;
|
|
|
|
| id | email | ssn | credit_card |
|
|
|----|------------------------|---------------|------------------|
|
|
| 1 | alice@example.com | 000-00-0001 | 0000-0000-0000-0001 |
|
|
| 2 | bob@example.com | 000-00-0002 | 0000-0000-0000-0002 |
|
|
| 3 | charlie@example.com | 000-00-0003 | 0000-0000-0000-0003 |
|
|
```
|
|
|
|
|
|
## Security Feature Documentation
|
|
|
|
### Pattern: Threat + Configuration + Impact
|
|
|
|
```markdown
|
|
## Security Feature: [Name]
|
|
|
|
### Threat Prevented
|
|
[What attack does this prevent?]
|
|
|
|
### Configuration
|
|
[How to enable/configure?]
|
|
\```bash
|
|
[Example commands]
|
|
\```
|
|
|
|
### Security Impact
|
|
**If enabled**: [What protection do you get?]
|
|
**If disabled**: [What risk remains?]
|
|
|
|
### Verification
|
|
[How to verify it's working?]
|
|
\```bash
|
|
[Test commands]
|
|
\```
|
|
```
|
|
|
|
### Example: Rate Limiting
|
|
|
|
```markdown
|
|
## Security Feature: API Rate Limiting
|
|
|
|
### Threat Prevented
|
|
**Brute force attacks**: Attacker attempts thousands of login requests to guess passwords.
|
|
**DoS attacks**: Attacker overwhelms API with excessive requests.
|
|
|
|
### Configuration
|
|
|
|
Enable rate limiting in `config.yaml`:
|
|
\```yaml
|
|
rate_limiting:
|
|
enabled: true
|
|
max_requests: 100 # per minute per IP
|
|
window: 60 # seconds
|
|
block_duration: 300 # 5 minutes
|
|
\```
|
|
|
|
Restart API server:
|
|
\```bash
|
|
systemctl restart api-server
|
|
\```
|
|
|
|
### Security Impact
|
|
**If enabled**:
|
|
- Brute force attack limited to 100 attempts/minute (vs unlimited)
|
|
- Single IP cannot DoS entire service
|
|
- Legitimate users unaffected (typical usage: 10-20 req/min)
|
|
|
|
**If disabled**:
|
|
- Attacker can attempt 1000s of passwords per minute
|
|
- Single attacker can exhaust server resources
|
|
- No protection against credential stuffing attacks
|
|
|
|
### Verification
|
|
|
|
Test rate limit:
|
|
\```bash
|
|
# Attempt 101 requests in 1 minute
|
|
for i in {1..101}; do
|
|
curl https://api.example.com/login -d "user=test&pass=fake"
|
|
done
|
|
|
|
# Expected: First 100 succeed, 101st returns:
|
|
# HTTP 429 Too Many Requests
|
|
# Retry-After: 60
|
|
\```
|
|
|
|
Check logs:
|
|
\```bash
|
|
grep "rate_limit_exceeded" /var/log/api-server.log
|
|
# Should show: [2024-03-15 10:25:45] Rate limit exceeded: IP 203.0.113.10, endpoint /login
|
|
\```
|
|
```
|
|
|
|
|
|
## Quick Reference: Sanitization Checklist
|
|
|
|
| Data Type | ❌ Wrong | ✅ Right |
|
|
|-----------|---------|---------|
|
|
| **API Key** | `sk_live_***REDACTED***` | `fake_api_key_abc123_for_docs_only` |
|
|
| **JWT Token** | `eyJhbGci...` (real masked) | `eyJhbGci...FAKE_TOKEN_FOR_DOCUMENTATION...` |
|
|
| **Email** | `john.smith@acme.com` | `jane.doe@example.com` |
|
|
| **SSN** | `***-**-1234` | `000-00-0000` |
|
|
| **Phone** | `(555) ***-1234` | `+1-555-0100` |
|
|
| **IP Address** | `52.12.34.56` (real AWS) | `203.0.113.10` (RFC 5737 docs) |
|
|
| **Domain** | `api.acme.com` | `api.example.com` |
|
|
| **Database Password** | `p@ssw***` | `fake_password_example_only` |
|
|
|
|
|
|
## Common Mistakes
|
|
|
|
### ❌ Masking Real Secrets
|
|
|
|
**Wrong**: `sk_live_***REDACTED***` (pattern suggests real key)
|
|
|
|
**Right**: `fake_api_key_abc123_for_docs_only` (obviously fake)
|
|
|
|
**Why**: Masked secrets still leak structure. Readers might try to unmask or think it's production data.
|
|
|
|
|
|
### ❌ Using Real Company Names
|
|
|
|
**Wrong**: `curl https://api.acmecorp.com` (might be real company)
|
|
|
|
**Right**: `curl https://api.example.com` (reserved for docs)
|
|
|
|
**Why**: Avoid accidental real company references. Use RFC-designated example domains.
|
|
|
|
|
|
### ❌ Documenting Exploits Before Patch
|
|
|
|
**Wrong**: Publish CVE details with exploit code before customers patch
|
|
|
|
**Right**: Coordinate disclosure with security team, publish after patch available
|
|
|
|
**Why**: Responsible disclosure prevents weaponizing vulnerabilities before users can protect themselves.
|
|
|
|
|
|
### ❌ Incomplete Redaction
|
|
|
|
**Wrong**: Redact password but leave username + server IP
|
|
|
|
**Right**: Redact all PII/credentials and use example IPs
|
|
|
|
**Why**: Partial redaction still enables attacks. Usernames + server IPs = reconnaissance.
|
|
|
|
|
|
## Cross-References
|
|
|
|
**Use WITH this skill**:
|
|
- `muna/technical-writer/clarity-and-style` - Write clear security documentation
|
|
- `ordis/security-architect/threat-modeling` - Understand threats documentation might expose
|
|
|
|
**Use AFTER this skill**:
|
|
- `muna/technical-writer/documentation-testing` - Verify examples work (with fake credentials)
|
|
|
|
## Real-World Impact
|
|
|
|
**Projects using security-aware documentation**:
|
|
- **API Documentation (Healthcare)**: Sanitized all examples with `jane.doe@example.com`, `fake_api_key_...`. Prevented accidental PII exposure in publicly-accessible docs.
|
|
- **OAuth Flow Tutorial**: Used complete fake examples (`client_id=fake_client_abc123`) vs placeholders (`YOUR_CLIENT_ID_HERE`). Support tickets reduced 60% ("I don't know what client ID looks like").
|
|
- **Database Migration Guide**: Used synthetic data (SSN: 000-00-0000) vs redacted real data (SSN: ***-**-1234). Compliance audit passed with "exemplary PII handling in documentation".
|
|
|
|
**Key lesson**: **Obviously fake examples are clearer and safer than masked real data. Complete fake examples enable copy-paste-run testing without security risk.**
|