Skip to content

Security: brokengg4/morphix

Security

SECURITY.md

Security & hardening

How Morphix is protected in production, and how to reproduce each layer. This is a reference for others building a similar "obfuscated frontend + protected backend behind Cloudflare" setup.

Honest disclaimer first: none of the frontend measures are "real" security. Anything the browser downloads can be read by a determined person. These layers raise the effort/cost of casual copying, scraping, and abuse — the actual security boundary is the backend, which holds all logic and validates all input.

The layers

1. JavaScript obfuscation

The entire frontend (frontend/build.mjs) is run through javascript-obfuscator: control-flow flattening, RC4 string-array encoding, renamed globals, dead-code injection, self-defending, and debug protection. Function names, endpoint strings, and logic become unreadable _0x… output. The readable source lives on GitHub; only the obfuscated build (dist/) is ever served.

2. Devtools guard

disable-devtool (vendored in frontend/weblib/, no CDN dependency) is injected into the production index.html. It disables the right-click menu and, if devtools is opened, blanks the page and redirects away. It cannot block view-source: (that protocol runs no JS) — which is why layer 3 exists.

3. Empty-shell / view-source protection

The served HTML is a bare shell: <div id="app-root"></div> plus the script tag. All page markup lives inside a template string in the obfuscated app.js and is injected at runtime. So view-source: on the page reveals nothing, and the markup is also RC4-encoded inside the obfuscated bundle. view-source: can never be fully blocked on the web — the goal is to make it show nothing useful.

4. Cloudflare Pages reverse proxy (origin hiding)

The public URL is a Cloudflare Pages project (pages/_worker.js) that reverse-proxies every request to the backend. Visitors only ever see Cloudflare's edge — the backend's host/IP is never exposed to the browser, and Cloudflare provides TLS and DDoS mitigation in front. The proxy forwards CF-Connecting-IP so the backend can still rate-limit by real client IP.

5. Origin lockdown (block direct scraping of the backend)

Cloudflare hides the origin from browsers, but scanners (Censys, Shodan, etc.) can still find the backend's own URL and hit it directly, bypassing the proxy. To stop that, the Pages proxy attaches a secret header X-Origin-Auth: <PROXY_SECRET>, and the backend (backend/turnstile.gooriginAllowed) rejects any request that lacks it with 403 — except /api/health, which stays open for uptime checks. Result: the backend only answers requests that came through your Cloudflare proxy. Set the same PROXY_SECRET value on both sides (see below).

6. Invisible Cloudflare Turnstile (bot protection)

When configured, an invisible Turnstile widget runs on the page and issues a token; the backend verifies it with Cloudflare's siteverify before every conversion or URL fetch. This blocks automated abuse of the API without showing a captcha to real users. It is env-gated — off unless a sitekey/secret is set.

7. Per-IP rate limiting + concurrency cap

The backend (backend/ratelimit.go) enforces a token-bucket rate limit per client IP on /api/convert and /api/fetch (real IP via CF-Connecting-IP), returning 429 with Retry-After when exceeded. A global semaphore caps how many conversions run at once so a burst of large media can't exhaust the CPU. Tunable via flags: -rate, -burst, -maxjobs.

8. SSRF protection on URL upload

/api/fetch downloads remote files server-side. It refuses non-http(s) schemes and blocks connections to loopback, private, and link-local addresses at dial time (checked on the resolved IP, so DNS rebinding can't bypass it), with a size cap and timeout.

Enabling the protected layers

Layers 1–4 and 7–8 are always on in the production build. Layers 5 and 6 are env-gated — turn them on like this:

Origin lockdown

  1. Pick a long random string as PROXY_SECRET.
  2. Set it as a Cloudflare Pages environment variable (project → Settings → Variables and Secrets), or wrangler pages secret put PROXY_SECRET.
  3. Set the same value as PROXY_SECRET on the backend host (Render → your service → Environment).
  4. Redeploy both. Direct hits to the backend now return 403; the proxied site works normally.

Turnstile

  1. Cloudflare dashboard → Turnstile → add a widget in Invisible mode for your Pages hostname. Copy the sitekey and secret.
  2. On the backend host, set TURNSTILE_SITEKEY (the sitekey) and TURNSTILE_SECRET (the secret).
  3. Redeploy the backend. The widget auto-loads and every conversion/fetch is verified. With the keys unset, the feature stays off and nothing changes.

What is NOT possible on a pages.dev domain

Custom Cloudflare WAF / firewall / rate-limiting rules require your own domain added to Cloudflare as a zone. A *.pages.dev URL is Cloudflare-managed and has no zone you can attach rules to. Cloudflare still applies its baseline bot/DDoS protection to Pages, and the origin lockdown (layer 5) + app rate limiting (layer 7) cover direct-access and abuse. To get custom WAF rules, put the site on your own domain (add it to Cloudflare, point it at the Pages project, and create rules on that zone).

There aren't any published security advisories