Share
## https://sploitus.com/exploit?id=EEF9D770-BBF2-5148-B61A-F2052F71D61C
# 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
```

2. **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.

3. **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](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

MIT โ€” 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.