# 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: ` ### 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)