- Add race condition testing results (HEAD triggers burn-after-read) - Add timing attack analysis (PBKDF2 constant-time verified) - Mark RPM, AVI, WAV MIME signatures as fixed - Update security controls table with new verifications
FlaskPaste
A lightweight, secure pastebin REST API built with Flask.
Features
- Simple REST API - Create, retrieve, list, and delete pastes via HTTP
- Binary support - Upload text, images, archives, and other binary content
- Automatic MIME detection - Magic byte detection (images, video, audio, documents, executables, archives)
- Client certificate authentication - mTLS or header-based via reverse proxy
- Tiered expiry - 1 day (anon), 7 days (untrusted), 30 days (trusted PKI)
- Size limits - 3 MiB anonymous, 50 MiB authenticated
- Abuse prevention - Content-hash deduplication throttles spam
- Proof-of-work - Computational puzzles prevent automated abuse
- Anti-flood - Dynamic PoW difficulty increases under attack
- Rate limiting - Per-IP throttling with X-RateLimit-* headers
- E2E encryption - Client-side AES-256-GCM with key in URL fragment
- Burn-after-read - Single-access pastes that auto-delete
- Password protection - PBKDF2-HMAC-SHA256 with 600k iterations
- Built-in PKI - Certificate authority for client certificate issuance
- Admin support - First registered user can manage all pastes
- Security headers - HSTS, CSP, X-Frame-Options, X-Content-Type-Options
- CLI client - Standalone
fpastetool with encryption support - Request tracing - X-Request-ID for log correlation
- Audit logging - PKI certificate lifecycle events (issue, revoke, auth failure)
- Observability - Request duration metrics via Prometheus histogram
- Minimal dependencies - Flask + SQLite, optional cryptography for CLI
Quick Start
# Clone and setup
git clone <repository>
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 /<id> |
Retrieve paste metadata | |
HEAD /<id> |
Retrieve paste metadata (headers only) | |
GET /<id>/raw |
Retrieve raw paste content | |
HEAD /<id>/raw |
Retrieve paste headers (no body) | |
DELETE /<id> |
Delete paste (requires auth) | |
GET /pastes |
List user's pastes (requires auth) | |
GET /register/challenge |
Get PoW challenge for registration | |
POST /register |
Register and get client certificate | |
GET /pki |
PKI status and CA info | |
GET /pki/ca.crt |
Download CA certificate |
Usage Examples
Create a paste (raw text)
curl --data-binary @file.txt http://localhost:5000/
Create a paste (piped input)
echo "Hello, World!" | curl --data-binary @- http://localhost:5000/
Create a paste (JSON)
curl -H "Content-Type: application/json" \
-d '{"content":"Hello from JSON!"}' \
http://localhost:5000/
Retrieve paste metadata
curl http://localhost:5000/abc12345
Retrieve raw content
curl http://localhost:5000/abc12345/raw
Delete a paste (requires authentication)
curl -X DELETE \
-H "X-SSL-Client-SHA1: <your-cert-fingerprint>" \
http://localhost:5000/abc12345
List user's pastes (requires authentication)
curl -H "X-SSL-Client-SHA1: <your-cert-fingerprint>" \
http://localhost:5000/pastes
CLI Client
A standalone command-line client fpaste is included. For E2E encryption, install the optional cryptography package.
Installation
# Download from running server
curl -sS https://paste.example.com/client > fpaste && chmod +x fpaste
# Optional: enable encryption support
pip install cryptography
Basic Usage
# Create paste from file (encrypts by default)
./fpaste file.txt
# Returns: https://paste.example.com/abc123#<key>
# Shortcut: file path auto-selects "create" command
./fpaste secret.txt # Same as: ./fpaste create secret.txt
# Create paste from stdin
echo "Hello" | ./fpaste
# Disable encryption (upload plaintext)
./fpaste -E file.txt
./fpaste create --no-encrypt file.txt
# Create burn-after-read paste (single access, auto-deletes)
./fpaste -b secret.txt
# Create paste with custom expiry (1 hour)
./fpaste -x 3600 temp.txt
# Combine options: encrypted + burn-after-read
./fpaste -b secret.txt
# Get paste content (auto-decrypts if URL has #key fragment)
./fpaste get "https://paste.example.com/abc123#<key>"
# Get paste metadata
./fpaste get -m abc12345
# List your pastes (requires auth)
./fpaste list
# List all pastes (admin only)
./fpaste list --all
# Delete paste (requires auth)
./fpaste delete abc12345
# Delete multiple pastes
./fpaste delete abc12345 def67890
# Delete all pastes (admin only, requires confirmation)
./fpaste delete --all --confirm 42 # where 42 is expected count
# Show server info
./fpaste info
# Register and get client certificate (if server supports it)
./fpaste register
# Saves certificate to ~/.config/fpaste/
# Register with auto-configuration
./fpaste register --configure
# Creates cert files and updates config with fingerprint
# Register with custom name
./fpaste register -n my-laptop --configure
End-to-End Encryption
Content is encrypted by default 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
#keyfragment and decrypts locally - Use
-Eor--no-encryptto disable encryption
This provides true zero-knowledge storage: the server cannot read your content.
Configuration
Set server URL and authentication via environment or config file:
# 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:
# 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:
# 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_AUTHextended key usage for mTLS- SHA1 fingerprint compatible with
X-SSL-Client-SHA1header - Self-signed with configurable validity period
Configuration
Configuration via environment variables:
| Variable | Default | Description |
|---|---|---|
FLASK_DEBUG |
0 |
Enable debug mode (1 = enabled) |
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_ANON |
86400 (1 day) |
Default expiry for anonymous users |
FLASKPASTE_EXPIRY_UNTRUSTED |
604800 (7 days) |
Default expiry for untrusted cert users |
FLASKPASTE_EXPIRY_TRUSTED |
2592000 (30 days) |
Default expiry for trusted (PKI) cert users |
FLASKPASTE_MAX_EXPIRY |
7776000 (90 days) |
Maximum custom expiry allowed |
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_REGISTER_POW |
24 |
Registration PoW difficulty (higher than paste creation) |
FLASKPASTE_POW_SECRET |
(auto) | Secret for signing PoW challenges |
FLASKPASTE_ANTIFLOOD |
1 |
Enable anti-flood (dynamic PoW difficulty) |
FLASKPASTE_ANTIFLOOD_WINDOW |
60 |
Anti-flood measurement window (seconds) |
FLASKPASTE_ANTIFLOOD_THRESHOLD |
5 |
Requests per window before difficulty increase |
FLASKPASTE_ANTIFLOOD_STEP |
2 |
Difficulty bits added per threshold breach |
FLASKPASTE_ANTIFLOOD_MAX |
28 |
Maximum PoW difficulty |
FLASKPASTE_ANTIFLOOD_DECAY |
60 |
Seconds before difficulty decreases |
FLASKPASTE_RATE_LIMIT |
1 |
Enable IP-based rate limiting |
FLASKPASTE_RATE_WINDOW |
60 |
Rate limit window (seconds) |
FLASKPASTE_RATE_MAX |
10 |
Max requests per window (anon) |
FLASKPASTE_RATE_AUTH_MULT |
5 |
Multiplier for authenticated users |
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_PKI_ENABLED |
0 |
Enable PKI certificate authority |
FLASKPASTE_PKI_CA_PASSWORD |
(empty) | CA password (required when PKI enabled) |
FLASKPASTE_PKI_CERT_DAYS |
365 |
Client certificate validity (days) |
Authentication
FlaskPaste uses client certificate authentication. When deployed behind a reverse proxy (nginx, Apache), configure the proxy to:
- Terminate TLS and validate client certificates
- Extract the certificate SHA1 fingerprint
- Pass it via the
X-SSL-Client-SHA1header
Example nginx configuration:
location / {
proxy_pass http://127.0.0.1:5000;
proxy_set_header X-SSL-Client-SHA1 $ssl_client_fingerprint;
}
Trust Levels
FlaskPaste distinguishes three trust levels:
| Level | Description | Default Expiry |
|---|---|---|
| Anonymous | No certificate | 1 day |
| Untrusted | Valid certificate, not registered via PKI | 7 days |
| Trusted | Certificate registered via /register endpoint |
30 days |
Authenticated users can:
- Upload larger pastes (50 MiB vs 3 MiB)
- Delete their own pastes
- List their own pastes
Admin users (first user to register via PKI) can additionally:
- List all pastes (
GET /pastes?all=1) - Delete any paste
Production Deployment
Using Gunicorn
pip install gunicorn
gunicorn -w 4 -b 0.0.0.0:5000 wsgi:app
Using Podman/Docker
podman build -t flaskpaste .
podman run -d -p 5000:5000 -v flaskpaste-data:/app/data flaskpaste
See Containerfile for container build configuration.
Using systemd
# Create service user
sudo useradd -r -s /sbin/nologin flaskpaste
# Copy application
sudo mkdir -p /opt/flaskpaste/data
sudo cp -r . /opt/flaskpaste/
sudo chown -R flaskpaste:flaskpaste /opt/flaskpaste
# Copy service unit and environment file
sudo cp examples/flaskpaste.service /etc/systemd/system/
sudo mkdir -p /etc/flaskpaste
sudo cp examples/flaskpaste.env /etc/flaskpaste/env
sudo chmod 600 /etc/flaskpaste/env
# Enable and start service
sudo systemctl daemon-reload
sudo systemctl enable --now flaskpaste
See examples/ for service unit and configuration templates.
Development
Running Tests
pip install pytest pytest-cov
pytest tests/ -v
Test Coverage
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
│ ├── audit.py # Audit logging for PKI events
│ ├── metrics.py # Prometheus metrics and histograms
│ └── api/
│ ├── __init__.py # Blueprint setup
│ └── routes.py # API endpoints
├── tests/ # Test suite (356 tests)
├── 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 (or admins) can delete pastes
- Size limits - Prevents resource exhaustion attacks
- Abuse prevention - Content-hash deduplication prevents spam flooding
- Entropy enforcement - Optional minimum entropy rejects plaintext uploads
- E2E encryption - Client-side AES-256-GCM, server is zero-knowledge
- Burn-after-read - Single-use pastes for sensitive data
- Password protection - PBKDF2-HMAC-SHA256 with 600k iterations
- Security headers - HSTS, CSP, X-Frame-Options, X-Content-Type-Options
- Proof-of-work - Computational puzzles prevent automated spam
- Rate limiting - Per-IP throttling with X-RateLimit-* headers
- Request tracing - X-Request-ID for log correlation
- PKI support - Built-in CA for client certificate issuance
- Audit logging - PKI certificate events for compliance and forensics
- Observability - Prometheus metrics for monitoring and alerting
License
MIT