Security posture
IDebug is a dev-only debugger. When enabled, /__idebug can expose full request bodies, headers, cookies, and function arguments that already passed redaction defaults. Treat the inspector like a local secrets viewport — not a public dashboard.
Threat model
| Risk | Why it matters |
|---|---|
| Accidental production enable | Anyone who can hit your app origin can read recent traffic |
Server bound to 0.0.0.0 | LAN peers (or the whole internet if the port is exposed) can open /__idebug |
| DNS rebinding | A malicious site can try to make the browser call http://evil.com/__idebug/... after rebinding evil.com to 127.0.0.1 |
| Shared machines / screenshares | Session list may contain auth headers (redacted by default) and PII you sent in bodies |
IDebug is not multi-tenant, has no user accounts, and is not a production APM.
Defaults (maximize safety)
| Control | Default | Role |
|---|---|---|
enabled | false | Entire middleware + UI no-op until you opt in |
hostCheck | true | Host must be loopback / localhost (DNS rebinding bar) |
localRemoteOnly | true | TCP peer must be loopback |
accessToken | null | Optional shared secret (strongly recommended on non-loopback binds) |
| Redaction | on | authorization, password, tokens, … → *** before store |
IDebug.configure({
enabled: process.env.NODE_ENV !== 'production',
appName: 'my-api',
// Security (these are the defaults — shown for clarity)
hostCheck: true,
localRemoteOnly: true,
// accessToken: process.env.IDEBUG_TOKEN, // recommend setting one
});Prefer binding the HTTP server itself to loopback when you only need local debugging:
app.listen(3000, '127.0.0.1');Host check (DNS rebinding protection)
Browsers send a Host header for every request. A DNS rebinding attack loads JS from an attacker-controlled name, then rebinds that name to your machine and calls your local app with Host: evil.com.
IDebug’s mitigation (default on): reject /__idebug unless Host is:
localhost127.0.0.0/8(e.g.127.0.0.1)::1/[::1]- or a name you explicitly list in
allowedHosts(e.g./etc/hostsentries likemyapp.local)
This matches the industry bar used by local debug sidecars (for example Spotlight documents DNS rebinding protections on its local server). Rebinding to loopback is useless if the Host is still evil.com and we refuse it.
IDebug.configure({
enabled: true,
allowedHosts: ['myapp.local'], // custom local DNS / hosts file
});Denied requests return HTTP 403 with reason: "host_not_allowed".
Local remote guard (bind guard)
Even with a correct Host, a process listening on 0.0.0.0:3000 can be reached from the network. When localRemoteOnly: true (default), IDebug also checks the socket peer address and rejects non-loopback clients (HTTP 403, reason: "remote_not_local").
Disable only when you intentionally debug from another device on LAN/VPN:
IDebug.configure({
enabled: true,
localRemoteOnly: false,
accessToken: process.env.IDEBUG_TOKEN, // require a token if not loopback-only
allowedHosts: ['192.168.1.20'], // Host header as the device uses it
});Access token (optional, recommended)
Set accessToken to require a shared secret on every UI/API request:
| Mechanism | Header / query |
|---|---|
| Bearer | Authorization: Bearer <token> |
| Custom header | X-IDebug-Token: <token> |
| Query (SSE / SPA) | ?idebug_token=<token> |
EventSource cannot set Authorization headers, so the SPA uses the query param (and stores it in sessionStorage when opened as /__idebug?idebug_token=...).
IDebug.configure({
enabled: true,
accessToken: process.env.IDEBUG_TOKEN ?? 'change-me',
});Open: http://127.0.0.1:3000/__idebug?idebug_token=change-me
Missing/invalid token → HTTP 401.
Redaction is not access control
Default redaction scrubs common secret keys before events hit the ring buffer. It does not:
- remove arbitrary PII in free-text bodies
- replace your need for Host / remote / token gates
- make production exposure safe
Add domain keys via redactKeys: ['ssn', 'taxId']. See Redaction.
Response hardening
All /__idebug responses set:
X-Frame-Options: DENYX-Content-Type-Options: nosniffReferrer-Policy: no-referrerCache-Control: no-store
There is still no multi-user auth and no CSRF cookie model — same-origin browser access from a page that can already talk to your origin is in scope of “you enabled a local debugger.”
Production checklist
-
enabled: false(or unset) in production builds - Prefer
app.listen(port, '127.0.0.1')for local-only debug sessions - Leave
hostCheck: trueandlocalRemoteOnly: trueunless you know you need otherwise - Set
accessTokenif the process is reachable beyond loopback - Do not put
/__idebugbehind a public reverse proxy without your own auth - Assume captured traffic may include credentials that escaped redaction keys
React overlay
@idebug/react runs in the page (no server UI). It does not open a network listener, but it still stores network payloads in memory in the tab. Keep enabled: import.meta.env.DEV (or equivalent) and never ship the overlay to production users.