Skip to content

shenolfernando2007/SecureProxy

Repository files navigation

SecureProxy

Description:

SecureProxy is a self-hosted Web Application Firewall (WAF) built with Flask. It sits in front of any web app as a reverse proxy, inspects every request for common attack patterns before it reaches the real backend, and logs everything to a live dashboard — regardless of what language or framework the backend itself is written in.

Why I Built This

Real WAF protection — signature-based attack detection, rate limiting, IP access control, a live view of what's being blocked — is usually something you pay Cloudflare or an enterprise vendor for. Small side projects, nonprofits, and personal servers rarely get that same protection, even though they get scanned and attacked just as often as anything else with a public IP. I built SecureProxy so any project can drop a real WAF in front of itself for free, self-hosted, without needing a huge budget or a security team.

Features

  • Reverse Proxy: Sits between the internet and your real app, forwarding clean traffic through untouched
  • Access Control: Static IP/CIDR allow and deny lists, checked before anything else
  • Rate Limiting: Sliding-window, per-IP, backed by memory (default) or Redis for multi-worker deployments
  • Request Size Limits: Oversized bodies are rejected with 413 before they're parsed
  • Signature-Based Detection: Checks URL, query string, form data, raw body, and headers against rule sets for SQL injection, XSS, path traversal, command injection, SSRF, XXE, SSTI, NoSQL injection, insecure deserialization, JNDI/Log4Shell, open redirects, request smuggling, and known scanner user agents
  • Decoding: Every surface is percent-decoded (repeatedly, to catch double-encoding) before rules run, so encoded payloads don't slip through
  • Anomaly Scoring: Several low/medium-severity signals on one request can add up and trigger a block, even with no single obvious match
  • Security Headers: Adds X-Content-Type-Options, X-Frame-Options, and Referrer-Policy to forwarded responses if the backend didn't already set them, and strips X-Powered-By
  • Live Dashboard: Shows what's being blocked, by rule and by source IP, in real time
  • Health Check: /healthz bypasses WAF logic entirely so load balancers can check the process is up without it counting as traffic

Tech Stack

  • Backend: Flask (Python)
  • Database: SQLite (request/block logging)
  • Rate Limiting: In-memory by default, optional Redis backend
  • Frontend: Dashboard built with plain HTML/JS, polling a JSON API
  • Reverse Proxy (production): Caddy, for automatic HTTPS
  • Deployment: Docker Compose

Installation

  1. Clone or download the repository.
git clone https://github.com/shenolfernando2007/SecureProxy.git
cd SecureProxy
  1. Install dependencies:
pip install -r requirements.txt

On Debian/Ubuntu/Kali (Python 3.11+): you may see error: externally-managed-environment. This happens because newer Debian-based systems block system-wide pip installs by default. Use a virtual environment instead:

python3 -m venv venv
source venv/bin/activate
pip install -r requirements.txt

If python3 -m venv fails, install it first with sudo apt install python3-venv, then retry. Remember to run source venv/bin/activate again each time you open a new terminal, before running the app.

  1. Run it:
python run.py

This starts everything at once, for local testing/demo purposes:

  • Demo backend (unprotected, for reference) — http://127.0.0.1:5000
  • WAF proxy (send your traffic here) — http://127.0.0.1:8080
  • Dashboard (watch what's being blocked) — http://127.0.0.1:8081
  • A background thread that purges log rows older than database.retention_days

Trying It Out

Send a normal request through the proxy:

curl "http://127.0.0.1:8080/search?q=hello"

Then try a few attacks — each should come back 403, and show up on the dashboard instantly:

curl "http://127.0.0.1:8080/search?q=%27%20OR%201%3D1%20--"                     # SQLi
curl "http://127.0.0.1:8080/fetch?url=http://169.254.169.254/latest/meta-data/" # SSRF
curl "http://127.0.0.1:8080/search?q=%7B%7B7*7%7D%7D"                           # SSTI
curl -A "sqlmap/1.7" "http://127.0.0.1:8080/"                                    # scanner UA

Or run the whole attack suite at once, in a second terminal while run.py is still running:

python tests/test_attacks.py

Project Structure

.
├── run.py                      # convenience launcher for local testing/demo
├── config.yaml                 # backend URL, ports, rate limits, rule toggles, ACLs
├── requirements.txt            # Python dependencies
├── waf/
│   ├── proxy.py                 # the reverse proxy: ACLs, rate limit, forwarding, security headers
│   ├── detector.py              # decodes and runs rule sets against a request, anomaly scoring
│   ├── rules.py                  # the attack signatures (regex-based), by category
│   ├── rate_limiter.py           # per-IP sliding window limiter (memory or Redis backend)
│   ├── logger.py                 # SQLite request logging and retention purging
│   └── config.py                 # config.yaml loader
├── dashboard/
│   ├── app.py                    # dashboard web app
│   └── templates/
│       └── dashboard.html        # live dashboard page
├── demo_backend/
│   └── app.py                    # a trivial "protected app" for testing
├── tests/
│   └── test_attacks.py           # sends sample attacks across every category, checks they're blocked
├── Dockerfile                  # container image for waf/dashboard
├── docker-compose.yml           # waf + dashboard + Caddy for production
├── Caddyfile                    # automatic HTTPS + routing for production
└── DEPLOY.md                    # step-by-step guide to a real deployment

How It Works

  client  --->  SecureProxy (WAF)  --->  your app
                      |
                      v
                 SQLite log  --->  live dashboard

Every request passes through the WAF first:

  1. Access control — static allow/deny lists are checked first
  2. Rate limiting — trip the per-IP limit and you're temporarily blocked
  3. Signature + anomaly inspection — the request is checked against every enabled rule set; a single high/critical match blocks it immediately, and several lower-severity matches can add up to a block too
  4. Forwarding — clean requests go to the real backend, untouched, with security headers added
  5. Logging — every request (or just blocked ones, depending on config) gets written to SQLite for the dashboard

Configuration

All tunables live in config.yaml (most can also be overridden by environment variable — see waf/config.py):

  • proxy.max_body_bytes — request body size cap; larger requests get 413 before parsing
  • proxy.trust_forwarded_for — only enable if SecureProxy sits behind another trusted reverse proxy (e.g. Caddy) that sets X-Forwarded-For itself
  • proxy.ip_allowlist / proxy.ip_denylist — static IP/CIDR access control
  • rate_limit — window size, request threshold, block duration, and backend: memory or backend: redis
  • rules — turn individual rule categories on/off, and tune anomaly_threshold
  • database.retention_days — how long log rows are kept before the background purge thread deletes them
  • logging.log_allowed_requests — set false to only log blocked traffic

Security Features

  • Every inspected surface (path, query string, form data, raw body, key headers) is percent-decoded repeatedly before matching, so encoded and double-encoded payloads are still caught
  • Rule severities feed both a hard-block decision and an accumulated anomaly score, so requests built from several individually-innocuous-looking pieces can still get blocked
  • Static IP allow/deny lists are checked before rate limiting or rule inspection
  • X-Forwarded-For is only trusted when explicitly configured, so a client can't spoof its own IP to dodge the rate limiter
  • Security response headers are added to forwarded responses, and X-Powered-By is stripped so the backend's stack isn't advertised
  • /healthz bypasses WAF logic entirely so health checks can't be blocked or logged as traffic

Deploying to Production

For a real, internet-facing deployment with automatic HTTPS, see DEPLOY.md — it walks through getting this running on an actual server with a real domain using Docker Compose + Caddy.

Production Checklist

Before deploying to production:

  1. Set a real dashboard password: generate a real DASHBOARD_PASS_HASH (see .env.example), never use the default
  2. Use a production WSGI server: the Docker setup already runs Gunicorn instead of Flask's dev server
  3. Enable HTTPS: Caddy handles this automatically in the Docker Compose setup
  4. Set proxy.ip_allowlist: if the deployment should only be reachable from specific IPs
  5. Regular backups: back up the data volume/directory if block history matters to you
  6. Tune rules.anomaly_threshold: defaults are hand-picked, not tuned against real traffic — adjust once you see your own false-positive rate
  7. Move to the Redis rate-limit backend: once running more than one worker

Development Notes

  • The demo backend, WAF, and dashboard all run in one process via run.py for local testing — in production they run as separate services (see docker-compose.yml)
  • Rate limiting defaults to in-memory and per-process; rate_limit.backend: redis shares state across multiple workers/hosts
  • SQLite logging is fine for single-server scale; a busier multi-server deployment would want Postgres instead
  • Detection is regex/signature-based plus an additive anomaly score, not ML-based and not a full port of OWASP CRS — it will still miss novel or heavily obfuscated payloads

Known Issues & Fixes

  • No allowlisting/false-positive workflow beyond the static proxy.ip_allowlist: a legitimate request that happens to match a signature still gets blocked with no automatic appeal path — exclude specific rules per-route in waf/detector.py if this comes up
  • Single-worker by default: the in-memory rate limiter and SQLite logging don't play well with multiple workers fighting each other — see the Redis-backed path in DEPLOY.md once this needs to scale

License

Released under the MIT License — free to use, modify, and self-host.

Contributions and forks are welcome. If you run into a bug or have an idea for a feature, open an issue or a pull request.

About

SecureProxy — a self-hosted Web Application Firewall for small teams and side projects, with signature-based attack detection and a live dashboard of blocked traffic. Built with Flask and SQLite.

Resources

License

Stars

1 star

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors