# FlaskPaste A lightweight, secure pastebin REST API built with Flask. ## Features - **Simple REST API** - Create, retrieve, and delete pastes via HTTP - **Binary support** - Upload text, images, archives, and other binary content - **Automatic MIME detection** - Magic byte detection for common formats (PNG, JPEG, GIF, WebP, ZIP, PDF, GZIP) - **Client certificate authentication** - Optional auth via `X-SSL-Client-SHA1` header - **Automatic expiry** - Pastes expire after configurable period of inactivity - **Size limits** - Configurable limits for anonymous and authenticated users - **Abuse prevention** - Content-hash deduplication throttles repeated identical submissions - **Entropy enforcement** - Optional minimum entropy requirement to enforce client-side encryption - **Proof-of-work** - Configurable computational puzzle prevents automated spam - **E2E encryption** - Client-side AES-256-GCM encryption with key in URL fragment (zero-knowledge) - **Burn-after-read** - Single-access pastes that auto-delete after first retrieval - **Custom expiry** - Per-paste expiry override via X-Expiry header - **Password protection** - Optional paste passwords with PBKDF2 hashing - **Security headers** - HSTS, CSP, X-Frame-Options, Cache-Control, and more - **CLI client** - Standalone `fpaste` command-line tool included - **Request tracing** - X-Request-ID support for log correlation - **Proxy trust validation** - Optional shared secret for defense-in-depth - **Minimal dependencies** - Flask only, SQLite built-in ## Quick Start ```bash # Clone and setup git clone cd flaskpaste python3 -m venv venv source venv/bin/activate pip install -r requirements.txt # Run development server python run.py ``` ## API Endpoints | Method | Endpoint | Description | |--------|----------|-------------| | `GET /` | API information and usage | | `GET /health` | Health check (returns DB status) | | `GET /challenge` | Get proof-of-work challenge | | `GET /client` | Download fpaste CLI client | | `POST /` | Create a new paste | | `GET /` | Retrieve paste metadata | | `HEAD /` | Retrieve paste metadata (headers only) | | `GET //raw` | Retrieve raw paste content | | `HEAD //raw` | Retrieve paste headers (no body) | | `DELETE /` | Delete paste (requires auth) | ## Usage Examples ### Create a paste (raw text) ```bash curl --data-binary @file.txt http://localhost:5000/ ``` ### Create a paste (piped input) ```bash echo "Hello, World!" | curl --data-binary @- http://localhost:5000/ ``` ### Create a paste (JSON) ```bash curl -H "Content-Type: application/json" \ -d '{"content":"Hello from JSON!"}' \ http://localhost:5000/ ``` ### Retrieve paste metadata ```bash curl http://localhost:5000/abc12345 ``` ### Retrieve raw content ```bash curl http://localhost:5000/abc12345/raw ``` ### Delete a paste (requires authentication) ```bash curl -X DELETE \ -H "X-SSL-Client-SHA1: " \ http://localhost:5000/abc12345 ``` ## CLI Client A standalone command-line client `fpaste` is included. For E2E encryption, install the optional `cryptography` package. ### Installation ```bash # Download from running server curl -sS https://paste.example.com/client > fpaste && chmod +x fpaste # Optional: enable encryption support pip install cryptography ``` ### Basic Usage ```bash # Create paste from file ./fpaste create file.txt # Create paste from stdin echo "Hello" | ./fpaste # Create encrypted paste (E2E, zero-knowledge) ./fpaste create -e secret.txt # Returns: https://paste.example.com/abc123# # Create burn-after-read paste (single access, auto-deletes) ./fpaste create -b secret.txt # Create paste with custom expiry (1 hour) ./fpaste create -x 3600 temp.txt # Combine options: encrypted + burn-after-read ./fpaste create -e -b secret.txt # Get paste content ./fpaste get abc12345 # Get encrypted paste (auto-decrypts if URL has #key fragment) ./fpaste get "https://paste.example.com/abc123#" # Get paste metadata ./fpaste get -m abc12345 # Delete paste (requires auth) ./fpaste delete abc12345 # Show server info ./fpaste info ``` ### End-to-End Encryption The `-e` flag encrypts content client-side using AES-256-GCM before upload: - Key is generated locally and never sent to server - Key is appended to URL as fragment (`#...`) which browsers never transmit - Server stores only opaque ciphertext - Retrieval auto-detects `#key` fragment and decrypts locally This provides true zero-knowledge storage: the server cannot read your content. ### Configuration Set server URL and authentication via environment or config file: ```bash # Environment variables export FLASKPASTE_SERVER="https://paste.example.com" export FLASKPASTE_CERT_SHA1="your-cert-fingerprint" # Or config file (~/.config/fpaste/config) server = https://paste.example.com cert_sha1 = your-cert-fingerprint ``` ### mTLS Client Certificate Authentication The CLI supports mutual TLS (mTLS) for direct client certificate authentication: ```bash # Environment variables export FLASKPASTE_CLIENT_CERT="/path/to/client.crt" export FLASKPASTE_CLIENT_KEY="/path/to/client.key" export FLASKPASTE_CA_CERT="/path/to/ca.crt" # Optional CA verification # Or config file (~/.config/fpaste/config) client_cert = /path/to/client.crt client_key = /path/to/client.key ca_cert = /path/to/ca.crt ``` When client certificates are configured, fpaste performs mTLS authentication directly with the server (or TLS-terminating proxy). This is more secure than header-based authentication as the certificate is validated at the TLS layer. ### Generating Client Certificates The CLI includes a built-in certificate generator for mTLS authentication: ```bash # Generate EC certificate (recommended, fast key generation) ./fpaste cert # Outputs fingerprint to stdout, details to stderr # Generate and auto-configure fpaste ./fpaste cert --configure # Creates ~/.config/fpaste/client.{crt,key} and updates config # Custom options ./fpaste cert -a rsa -b 4096 -d 730 -n "my-client" --configure # RSA 4096-bit key, 2-year validity, custom common name # Supported curves for EC: secp256r1, secp384r1 (default), secp521r1 ./fpaste cert -c secp256r1 --configure ``` The generated certificate includes: - `CLIENT_AUTH` extended key usage for mTLS - SHA1 fingerprint compatible with `X-SSL-Client-SHA1` header - Self-signed with configurable validity period ## Configuration Configuration via environment variables: | Variable | Default | Description | |----------|---------|-------------| | `FLASK_ENV` | `development` | Environment (`development`, `production`, `testing`) | | `FLASKPASTE_DB` | `./data/pastes.db` | SQLite database path | | `FLASKPASTE_ID_LENGTH` | `12` | Paste ID length (hex characters) | | `FLASKPASTE_MAX_ANON` | `3145728` (3 MiB) | Max paste size for anonymous users | | `FLASKPASTE_MAX_AUTH` | `52428800` (50 MiB) | Max paste size for authenticated users | | `FLASKPASTE_EXPIRY` | `432000` (5 days) | Paste expiry in seconds | | `FLASKPASTE_DEDUP_WINDOW` | `3600` (1 hour) | Dedup throttle window in seconds | | `FLASKPASTE_DEDUP_MAX` | `3` | Max identical submissions per window | | `FLASKPASTE_PROXY_SECRET` | (empty) | Shared secret for proxy trust validation | | `FLASKPASTE_POW_DIFFICULTY` | `20` | PoW difficulty (leading zero bits, 0=disabled) | | `FLASKPASTE_POW_TTL` | `300` (5 min) | PoW challenge validity period | | `FLASKPASTE_POW_SECRET` | (auto) | Secret for signing PoW challenges | | `FLASKPASTE_URL_PREFIX` | (empty) | URL prefix for reverse proxy deployments | | `FLASKPASTE_MIN_ENTROPY` | `0` | Min entropy bits/byte (0=disabled, 6.0=require encryption) | | `FLASKPASTE_MIN_ENTROPY_SIZE` | `256` | Only check entropy for content >= this size | | `FLASKPASTE_MAX_EXPIRY` | `2592000` (30 days) | Maximum custom expiry allowed | ## Authentication FlaskPaste uses client certificate authentication. When deployed behind a reverse proxy (nginx, Apache), configure the proxy to: 1. Terminate TLS and validate client certificates 2. Extract the certificate SHA1 fingerprint 3. Pass it via the `X-SSL-Client-SHA1` header Example nginx configuration: ```nginx location / { proxy_pass http://127.0.0.1:5000; proxy_set_header X-SSL-Client-SHA1 $ssl_client_fingerprint; } ``` Authenticated users can: - Upload larger pastes (50 MiB vs 3 MiB) - Delete their own pastes ## Production Deployment ### Using Gunicorn ```bash pip install gunicorn gunicorn -w 4 -b 0.0.0.0:5000 wsgi:app ``` ### Using Podman/Docker ```bash podman build -t flaskpaste . podman run -d -p 5000:5000 -v flaskpaste-data:/app/data flaskpaste ``` See `Containerfile` for container build configuration. ## Development ### Running Tests ```bash pip install pytest pytest-cov pytest tests/ -v ``` ### Test Coverage ```bash pytest tests/ --cov=app --cov-report=term-missing ``` ### Project Structure ``` flaskpaste/ ├── app/ │ ├── __init__.py # Flask app factory │ ├── config.py # Configuration classes │ ├── database.py # SQLite management │ └── api/ │ ├── __init__.py # Blueprint setup │ └── routes.py # API endpoints ├── tests/ # Test suite ├── data/ # SQLite database ├── run.py # Development server ├── wsgi.py # Production WSGI entry ├── Containerfile # Podman/Docker build └── requirements.txt # Dependencies ``` ## Security Considerations - **Input validation** - Paste IDs are hex-only, auth headers validated - **MIME sanitization** - Content-Type headers are sanitized - **SQL injection protection** - Parameterized queries throughout - **Ownership enforcement** - Only owners can delete their pastes - **Size limits** - Prevents resource exhaustion attacks - **Abuse prevention** - Content-hash deduplication prevents spam flooding - **Entropy enforcement** - Optional minimum entropy rejects low-entropy (plaintext) uploads - **E2E encryption** - Client-side encryption keeps server zero-knowledge - **Burn-after-read** - Single-use pastes for sensitive data - **Security headers** - HSTS, CSP, X-Frame-Options, X-Content-Type-Options, Cache-Control - **Request tracing** - X-Request-ID for log correlation and debugging - **Proxy trust** - Optional `X-Proxy-Secret` validation to prevent header spoofing ## License MIT