Some checks failed
CI / Lint & Format (push) Failing after 16s
CI / Unit Tests (push) Has been skipped
CI / Memory Leak Check (push) Has been skipped
CI / SBOM Generation (push) Has been skipped
CI / Security Scan (push) Successful in 19s
CI / Security Tests (push) Has been skipped
CI / Advanced Security Tests (push) Has been skipped
352 lines
12 KiB
Markdown
352 lines
12 KiB
Markdown
# Security Testing Status
|
|
|
|
Tracking security testing progress and remaining tasks.
|
|
|
|
---
|
|
|
|
## Completed Testing
|
|
|
|
### Local Fuzzer (tests/fuzz/run_fuzz.py)
|
|
|
|
| Phase | Tests | Status |
|
|
|-------|-------|--------|
|
|
| Reconnaissance | 25 endpoints probed | PASS |
|
|
| Input Fuzzing | Binary, unicode, size limits | PASS |
|
|
| Injection Attacks | SQLi, XSS, SSTI, command injection | PASS |
|
|
| Auth/Authz | Header spoofing, privilege escalation | PASS |
|
|
| Business Logic | Burn-after-read, expiry, dedup | PASS |
|
|
| Cryptography | PoW token replay, timing | PASS |
|
|
|
|
### Production Fuzzer (mymx.me/paste)
|
|
|
|
| Phase | Tests | Status |
|
|
|-------|-------|--------|
|
|
| Content Fuzzing | Null bytes, unicode, 50KB, control chars | PASS |
|
|
| Injection Testing | SQLi, SSTI, XSS, command, path traversal | PASS |
|
|
| Header Injection | Host override, XFF chains, SQLi in headers | PASS |
|
|
| Path Fuzzing | Traversal, URL encoding, long paths | PASS |
|
|
| MIME Detection | GIF+JS, PNG+HTML, PDF+HTML polyglots | PASS |
|
|
|
|
### MIME Detection (Polyglot Attacks)
|
|
|
|
| Attack Vector | Payload | Result |
|
|
|---------------|---------|--------|
|
|
| PNG + HTML | Magic bytes + script tag | Served as image/png |
|
|
| GIF + JavaScript | GIF89a + JS comment trick | Served as image/gif |
|
|
| PDF + ZIP | PDF header + ZIP trailer | Served as application/pdf |
|
|
| SVG + Script | XML with embedded script | Served as text/plain |
|
|
| JPEG + PHP | JFIF + PHP code | Served as image/jpeg |
|
|
|
|
### Race Condition Testing
|
|
|
|
| Test | Method | Result |
|
|
|------|--------|--------|
|
|
| Burn-after-read bypass | HEAD then GET | SAFE - HEAD triggers deletion |
|
|
|
|
Verified via server logs: `Burn-after-read paste deleted via HEAD: <id>`
|
|
|
|
### Comprehensive Pentest Session (2025-12-26)
|
|
|
|
Full penetration test with profiled server (tests/security/pentest_session.py):
|
|
|
|
| Phase | Tests | Result |
|
|
|-------|-------|--------|
|
|
| Reconnaissance | /, /health, /challenge, /client, /metrics | 5/5 PASS |
|
|
| Paste Creation | PoW, burn-after-read, password, expiry | 4/4 PASS |
|
|
| Paste Retrieval | Metadata, raw, HEAD, burn, auth | 7/7 PASS |
|
|
| Error Handling | 404, invalid ID, no PoW, bad token | 3/4 PASS |
|
|
| Injection Attacks | SQLi payloads, SSTI templates | 4/7 PASS |
|
|
| Header Injection | X-Forwarded-For, Host override | 2/2 PASS |
|
|
| Rate Limiting | 100 rapid requests | 1/1 PASS |
|
|
| Size Limits | 4MB content rejection | 1/1 PASS |
|
|
| Concurrent Access | 10 threads, 5 workers | 1/1 PASS |
|
|
| MIME Detection | PNG, GIF, PDF, ZIP magic bytes | 4/4 PASS |
|
|
|
|
**Total: 32/36 PASS** (4 false negatives - server returns 400 for invalid IDs instead of 404)
|
|
|
|
Notes:
|
|
- Anti-flood triggered: PoW difficulty increased from 16 to 26 bits
|
|
- PoW token expiration working: rejects solutions after timeout
|
|
- Rate limiting enforced: 429 responses observed
|
|
- Size limit enforced: 413 for 4MB content
|
|
|
|
### Server Profiling Analysis (2025-12-26)
|
|
|
|
Profiled server during 18.5 minute pentest session:
|
|
|
|
| Metric | Value |
|
|
|--------|-------|
|
|
| Requests handled | 144 |
|
|
| Total CPU time | 0.142s (0.03%) |
|
|
| I/O wait time | 1114.4s (99.97%) |
|
|
| Avg request time | <1ms |
|
|
|
|
Verdict: Server is highly efficient. No CPU hotspots. PoW computation is client-side by design.
|
|
|
|
### Timing Attack Analysis
|
|
|
|
Tested authentication endpoints for timing oracle vulnerabilities (2025-12-25):
|
|
|
|
| Endpoint | Test | Variance | Result |
|
|
|----------|------|----------|--------|
|
|
| Password verification | Correct vs Wrong | 2.3% | SAFE |
|
|
| Password verification | Correct vs None | 2.1% | SAFE |
|
|
| Paste existence | Valid vs Invalid ID | Expected | OK (DB lookup) |
|
|
| Auth header | Valid vs Invalid format | Expected | OK (DB lookup) |
|
|
|
|
Password verification uses PBKDF2 with 600,000 iterations (~900ms constant-time).
|
|
No password oracle vulnerability - timing variance within acceptable bounds.
|
|
|
|
---
|
|
|
|
## Remaining Tasks
|
|
|
|
### MIME Detection - Additional Formats
|
|
|
|
Tested on production (2025-12-25):
|
|
|
|
```
|
|
[x] WebP (image/webp) PASS
|
|
[x] TIFF-LE (image/tiff) PASS
|
|
[x] TIFF-BE (image/tiff) PASS
|
|
[x] BMP (image/bmp) PASS
|
|
[x] ICO (image/x-icon) PASS
|
|
[x] WebM (video/webm) PASS
|
|
[x] MP4 (video/mp4) PASS
|
|
[x] MP3 (audio/mpeg) PASS
|
|
[x] MP3-ID3 (audio/mpeg) PASS
|
|
[x] FLAC (audio/flac) PASS
|
|
[x] OGG (audio/ogg) PASS
|
|
[x] 7z (application/x-7z-compressed) PASS
|
|
[x] RAR (application/vnd.rar) PASS
|
|
[x] XZ (application/x-xz) PASS
|
|
[x] BZ2 (application/x-bzip2) PASS
|
|
[x] WASM (application/wasm) PASS
|
|
[x] MachO-32 (application/x-mach-binary) PASS
|
|
[x] MachO-64 (application/x-mach-binary) PASS
|
|
|
|
Added (2025-12-26):
|
|
[x] HEIC (image/heic) PASS - ftyp box with heic brand
|
|
[x] HEIF (image/heif) PASS - ftyp box with mif1 brand
|
|
[x] AVIF (image/avif) PASS - ftyp box with avif brand
|
|
[x] MKV (video/webm) PASS - Same EBML header as WebM
|
|
|
|
Fallback to text/plain (safe default):
|
|
[~] MOV - ftyp offset varies
|
|
[~] CAB - Signature not implemented
|
|
[~] DEB - Signature not implemented
|
|
[~] AR - Signature not implemented
|
|
[~] TAR - ustar at offset 257 (beyond 16-byte check)
|
|
|
|
Fixed (2025-12-25):
|
|
[x] RPM - Added signature (0xEDABEEDB)
|
|
[x] AVI - Fixed RIFF subtype detection
|
|
[x] WAV - Fixed RIFF subtype detection
|
|
|
|
Known issues:
|
|
[!] JavaClass - Detected as Mach-O (0xCAFEBABE collision, unfixable)
|
|
|
|
Not detectable (structural limitations):
|
|
[~] DMG - UDIF signature in trailer, not header
|
|
[~] ISO - CD001 at offset 32769 (beyond 16-byte check)
|
|
[~] DOCX/XLSX/PPTX - ZIP-based, detected as application/zip (correct)
|
|
[~] ODF (ODT/ODS) - ZIP-based, detected as application/zip (correct)
|
|
```
|
|
|
|
### Fuzzing Improvements
|
|
|
|
```
|
|
[ ] Add --target option to run_fuzz.py for external testing
|
|
[ ] Implement adaptive rate limiting in production fuzzer
|
|
[x] Add hypothesis property-based tests for MIME detection
|
|
[x] Create polyglot generator for automated MIME confusion testing
|
|
[x] Add timing attack tests for authentication endpoints
|
|
```
|
|
|
|
**Polyglot Generator (2025-12-26):**
|
|
- `tests/security/polyglot_generator.py`: Creates files valid in multiple formats
|
|
- Supports: GIF+JS, PDF+JS, ZIP+HTML, PNG+HTML, generic primary:payload
|
|
- 41 polyglot tests verify MIME detection handles all cases correctly
|
|
|
|
**Hypothesis MIME Tests (2025-12-26):**
|
|
- `test_magic_prefix_detection`: All known signatures + random suffix detect correctly
|
|
- `test_random_binary_never_crashes`: Random binary never crashes detector
|
|
- `test_partial_magic_no_false_match`: Truncated magic bytes handled safely
|
|
- `test_magic_not_at_start_ignored`: Magic at non-zero offset ignored
|
|
|
|
### Penetration Testing (from PENTEST_PLAN.md)
|
|
|
|
```
|
|
[x] Race condition: Burn-after-read via HEAD then GET (SAFE)
|
|
[x] Race condition: Content hash deduplication counter (SAFE - locked)
|
|
[x] DoS: Memory exhaustion via unique IP rate limits (FIXED)
|
|
[x] DoS: Anti-flood list growth under load (SAFE - bounded)
|
|
[x] CLI: Clipboard command injection validation (SAFE)
|
|
[x] CLI: Certificate file permission exposure (SAFE - 0o600)
|
|
```
|
|
|
|
### CLI Security Audit (2025-12-26)
|
|
|
|
| Check | Status |
|
|
|-------|--------|
|
|
| Trusted clipboard path validation | PASS |
|
|
| PATH injection prevention | PASS |
|
|
| Subprocess safety (no shell=True) | PASS |
|
|
| Config permission warnings | PASS |
|
|
| Key file permissions (0o600) | PASS |
|
|
| Symlink attacks | LOW RISK |
|
|
|
|
### Memory Exhaustion Tests (2025-12-26)
|
|
|
|
| Component | Protection | Status |
|
|
|-----------|------------|--------|
|
|
| Anti-flood list | ANTIFLOOD_MAX_ENTRIES (10000) | PASS |
|
|
| Rate limit dict | RATE_LIMIT_MAX_ENTRIES (10000) | PASS |
|
|
| Lookup rate limit | LOOKUP_RATE_LIMIT_MAX_ENTRIES (10000) | FIXED |
|
|
| Content dedup | Database + PoW | PASS |
|
|
| Concurrent access | Thread-safe with locks | PASS |
|
|
|
|
### Documentation
|
|
|
|
```
|
|
[x] Add remaining MIME test results to security assessment
|
|
[x] Document rate limiting behavior under attack
|
|
[x] Create threat model diagram (documentation/threat-model.md)
|
|
[x] Add security headers audit to CI pipeline
|
|
```
|
|
|
|
---
|
|
|
|
## Rate Limiting Under Attack
|
|
|
|
### Defense Layers
|
|
|
|
```
|
|
Layer 1: Per-IP Rate Limiting
|
|
├── Window: 60 seconds
|
|
├── Max requests: 30 (configurable)
|
|
├── Response: 429 Too Many Requests
|
|
└── Memory cap: 10,000 IPs max
|
|
|
|
Layer 2: Anti-Flood (Dynamic PoW)
|
|
├── Base difficulty: 16 bits
|
|
├── Threshold: 5 pastes/window triggers increase
|
|
├── Step: +2 bits per threshold breach
|
|
├── Max difficulty: 28 bits
|
|
├── Decay: -2 bits every 30s when idle
|
|
└── Effect: Attackers must solve harder puzzles
|
|
|
|
Layer 3: Content Deduplication
|
|
├── Hash window: 300 seconds (5 min)
|
|
├── Max duplicates: 3 per hash per window
|
|
├── Response: 429 with "duplicate content" message
|
|
└── Bypass: Requires unique content each time
|
|
```
|
|
|
|
### Attack Scenarios
|
|
|
|
| Attack | Detection | Response | Recovery |
|
|
|--------|-----------|----------|----------|
|
|
| Single IP flood | Rate limit hit | 429 after 30 req/min | Auto after 60s |
|
|
| Distributed flood | Anti-flood threshold | PoW difficulty 16→28 | Decay after 30s idle |
|
|
| Content spam | Dedup detection | 429 after 3 dupes | Window expires 5min |
|
|
| Enumeration | Lookup rate limit | 429 after 60 req/min | Auto after 60s |
|
|
|
|
### Observed Behavior (Pentest 2025-12-26)
|
|
|
|
During 18.5 minute penetration test:
|
|
- Requests handled: 144
|
|
- Anti-flood triggered: Yes (difficulty 16→26 bits)
|
|
- Rate limit 429s observed: Yes
|
|
- PoW token expiration working: Rejected stale solutions
|
|
- Memory usage: Stable (capped dictionaries)
|
|
|
|
### Configuration
|
|
|
|
```python
|
|
# app/config.py defaults
|
|
RATE_LIMIT_MAX_ENTRIES = 10000 # Max tracked IPs
|
|
RATE_LIMIT_REQUESTS = 30 # Requests per window
|
|
RATE_LIMIT_WINDOW = 60 # Window in seconds
|
|
|
|
ANTIFLOOD_THRESHOLD = 5 # Pastes before PoW increase
|
|
ANTIFLOOD_STEP = 2 # Bits added per breach
|
|
ANTIFLOOD_MAX = 28 # Maximum difficulty
|
|
ANTIFLOOD_DECAY = 30 # Seconds before difficulty drops
|
|
|
|
DEDUP_WINDOW = 300 # Hash tracking window
|
|
DEDUP_MAX = 3 # Max duplicates allowed
|
|
```
|
|
|
|
### Monitoring
|
|
|
|
- `/metrics` endpoint exposes:
|
|
- `flaskpaste_rate_limit_total`: Rate limit hits
|
|
- `flaskpaste_pow_difficulty`: Current PoW difficulty
|
|
- `flaskpaste_paste_created_total`: Creation rate
|
|
- `flaskpaste_dedup_total`: Dedup rejections
|
|
|
|
---
|
|
|
|
## Test Commands
|
|
|
|
```bash
|
|
# Local fuzzer (starts isolated server)
|
|
./venv/bin/python tests/fuzz/run_fuzz.py --verbose
|
|
|
|
# Quick smoke test
|
|
./venv/bin/python tests/fuzz/run_fuzz.py --quick
|
|
|
|
# Specific phases only
|
|
./venv/bin/python tests/fuzz/run_fuzz.py --phases 1,2,3
|
|
|
|
# Hypothesis tests (via pytest)
|
|
./venv/bin/pytest tests/test_fuzz.py -v
|
|
|
|
# Comprehensive pentest (requires running server)
|
|
./venv/bin/python tests/security/pentest_session.py
|
|
|
|
# Profiled server for performance analysis
|
|
./venv/bin/python tests/security/profiled_server.py
|
|
|
|
# CLI security audit
|
|
./venv/bin/python tests/security/cli_security_audit.py
|
|
|
|
# DoS memory exhaustion tests
|
|
./venv/bin/python tests/security/dos_memory_test.py
|
|
|
|
# Race condition tests
|
|
./venv/bin/python tests/security/race_condition_test.py
|
|
```
|
|
|
|
---
|
|
|
|
## Security Controls Verified
|
|
|
|
| Control | Implementation | Verified |
|
|
|---------|----------------|----------|
|
|
| X-Content-Type-Options | nosniff | Yes |
|
|
| Content-Security-Policy | default-src 'none' | Yes |
|
|
| X-Frame-Options | DENY | Yes |
|
|
| MIME detection | UTF-8 validation (text/binary) | Yes |
|
|
| Input sanitization | Werkzeug header handling | Yes |
|
|
| SQL injection prevention | SQLAlchemy parameterized queries | Yes |
|
|
| SSTI prevention | No user content in templates | Yes |
|
|
| Path traversal prevention | ID validation regex | Yes |
|
|
| Constant-time password check | PBKDF2 600k iterations | Yes |
|
|
| Burn-after-read race condition | HEAD triggers deletion | Yes |
|
|
| Clipboard command injection | Trusted path validation | Yes |
|
|
| Memory exhaustion prevention | Max entries on all dicts | Yes |
|
|
| Race condition protection | Threading locks on counters | Yes |
|
|
| Anti-flood protection | Dynamic PoW difficulty (16-28 bits) | Yes |
|
|
| PoW token expiration | Rejects stale solutions | Yes |
|
|
|
|
---
|
|
|
|
## Notes
|
|
|
|
- Production testing requires rate limit awareness (1.5s+ delay)
|
|
- X-SSL-Client-SHA1 spoofing requires TRUSTED_PROXY_SECRET in production
|
|
- /metrics endpoint intentionally exposed for Prometheus
|
|
- Hypothesis tests use Flask test client (in-memory, not network)
|