Files
flaskpaste/documentation/security-testing-status.md
Username 6da80aec76
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
docs: update for simplified MIME detection (v1.5.1)
2025-12-26 19:52:40 +01:00

12 KiB

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

# 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

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