Security
July 15, 2026 ยท View on GitHub
oproxy is a local developer proxy. It captures, stores, rewrites, and replays HTTP traffic, so treat its state as sensitive.
Threat Model
Trust boundary:
- local users who can access the UI/API listener
- clients configured to use oproxy as a proxy
- anyone with access to
storage_path,mitm.root_ca_path, Docker volumes, browser local storage, logs, or exported capture files
Primary risks:
- captured secrets in headers and bodies
- CA private key exposure
- remote administration of proxy controls
- admin-triggered requests to internal networks
- Lua scripts modifying traffic
- webhooks sending captured metadata to another service
CA Handling
CA files live under mitm.root_ca_path, default ./certs.
The private key is root.key. On Unix, oproxy writes it with mode 0600.
Anyone with this key can create certificates trusted by clients that installed the oproxy CA. Protect:
certs/- Docker
oproxy-certsvolume - backups
- copied CA material
If CA material changes, remove the old CA from trust stores and install the new one only where needed.
Remote Administration Risks
Default built-in bind host is 127.0.0.1, and allow_remote_admin defaults to false.
Binding to 0.0.0.0 exposes the proxy listener to the network. With allow_remote_admin=false, ordinary LAN Host headers are treated as proxy traffic rather than management traffic.
If you intentionally enable remote admin:
bind_host: "0.0.0.0"
allow_remote_admin: true
admin_token: "change-me"
allow_private_admin_egress: false
Set a token. The server warns if remote admin is enabled without one.
When remote admin is enabled, /admin/forward, playback, and webhooks cannot target private, loopback, link-local, multicast, or unspecified IP ranges unless allow_private_admin_egress is true.
allow_remote_admin and containerized deployments
On bare metal, Host: 127.0.0.1/localhost/::1/0.0.0.0 requests are only
treated as management traffic when the TCP peer is verifiably loopback - a
remote client cannot get in just by sending a spoofed Host header. Under
Docker's bridge networking with a published port, the container never
observes a genuinely loopback peer for that traffic, even when the request
really is coming from a browser on the same machine (Docker's port-publishing
NAT rewrites the source address). To make the Web UI reachable there anyway,
allow_remote_admin=true + a configured admin_token extends the same
trust-the-token-not-the-peer model already used for LAN-hostname admin access
to this loopback-family case too: once both are set, a loopback-family Host
header is accepted as management traffic regardless of peer address, and the
token is still required for anything beyond the small public-path allowlist
(/, /login, /health, /admin/ca, /setup, /setup/mobile,
/admin/setup/network-info, static assets). This only changes behavior for
non-loopback peers; a real loopback peer is still trusted without a token, as
before. Don't enable allow_remote_admin without a strong admin_token, and
don't publish the port beyond host loopback (-p 127.0.0.1:8080:8080, not
-p 8080:8080) unless you intend the admin UI to be reachable from the LAN.
Storage Considerations
Server-side storage_path persists rule and control-plane state. Live sessions are in memory unless explicitly saved or exported.
Sensitive locations:
storage_pathmitm.root_ca_path- Docker volumes
- browser local storage for Compose collections and variables
- HAR exports
- raw cURL/Fetch/Python exports
- saved session JSON files
- webhook destination logs
Default exports redact common sensitive headers and body fields. Raw exports are intentionally unredacted.
GET /api/sessions/{id}/export?format=<curl|fetch|python>&raw=true requests the unredacted variant; the intuitive redact=false is not accepted. raw defaults to false (redacted) when omitted.
Lua Scripts
Lua scripts are stored in lua_scripts.json and run for each request/response while enabled.
The Lua environment removes selected globals such as io, os, package, require, load, loadfile, dofile, debug, and coroutine. Scripts still can modify traffic and abort requests, so only enable scripts you trust.
Webhooks
Webhooks can be configured for request_captured and response_captured. Payloads include event type, session id, timestamp, request method, URI, and response status if present.
If a webhook secret is set, oproxy sends an x-oproxy-signature HMAC-SHA256 header over the JSON payload.