Hardening and threat model

On this page

Hardening and threat model

This page is the short version of Security, written for the person who has to say yes to running BxLens on a server. It says what the console can do, what it never does, what leaves the machine, and what you should still do yourself.

What BxLens is, security wise

BxLens shows the inside of a running application. That is its job, and it is also the risk. Someone who gets into the console sees SQL text, URLs, error messages, thread stacks, settings, and (on a full collect level) headers and the callers of queries. So the console is built like an admin tool: off by default, closed to everyone but allowed addresses, behind a password, and quiet about itself.

What the console can do

WhoCan
Anyone not allowed by console.access, access.allowedHosts or access.requireHeaderNothing. A plain 404, so the console does not say it exists.
Anyone allowed, without a passwordSee the login page. Nothing else. With no password set at all: the setup page, and 503 for the rest.
A viewer (BoxLang+)Read requests, queries, errors, reports, HTTP, executors, tasks, datasources, applications, watchers, cache statistics, modules, Mail without message text. Chat with Lensy with the tools a viewer may use. Change nothing.
An adminEverything above, plus read logs, environment, system and threads. Change live settings (BoxLang+), passwords and secrets. Run, pause and reload tasks, clear caches, restart an application, start and stop a watcher, requeue mail, run GC. With BoxLang+: download logs, thread dumps and the diagnostic bundle, read a cache value. With console.allowHeapDump: take a heap dump. Approve what Lensy proposes.

console.readOnly removes every change. console.actions: false removes the actions on the server. Both are in boxlang.json only.

There are no user names. The password decides the role, so two admins share one password, and the audit log names the role and the address, not the person.

What the console can never do

  • Reach another site. The console and the bar load nothing from outside: scripts, styles, fonts and icons come from your own server. See what leaves the server for the few calls the server makes.
  • Change who can reach it. The Settings page and Lensy cannot change console.*, access.*, bar.access, store.* or secrets.dir. The server refuses those keys.
  • Show a secret. No route, answer, audit line, log line, bundle, prompt or Lensy tool returns a stored password, hash, key, API key, SMTP password or channel address. Pages show set, not set or the host.
  • Run SQL. Lensy's database tools read JDBC metadata only. No tool takes SQL text.
  • Stop or restart the server. There is no such tool or button. (An application can be restarted by name, with BoxLang+ and a typed confirmation, and not the one that serves the console.)
  • Read arbitrary files. The Logs page reads the BoxLang logs directory only, and refuses names outside it and links that point outside.
  • Act on another server. The pages of another server are read only, and the server refuses a change aimed at one.
  • Keep bound query values unless you ask. Statement text is kept with literals masked. Bound values need collectors.queries.includeParams, and are never kept at collect.level light.

What leaves the server

BxLens makes no call for telemetry or for licensing. The license is checked inside the runtime through bx-plus. These are the only outgoing connections, and each one needs somebody to switch it on:

ConnectionWhenGoes to
Registration formOnce, when someone submits the Register page on Free or Trial. From the server, https only, no redirects. Sends the form and the versions of BxLens, BoxLang and Java, nothing about your application. Checking the key is offlineregistration.endpoint (https://bxlens-register.boxlang.io)
SMTPAlerts, reports, test mails (BoxLang+)The SMTP server an admin typed
ChannelsAlerts and reports to Slack, Teams, Telegram, or a webhook (BoxLang+)The https address an admin saved. Public addresses only, checked again before every send. See Channels
AI providerOnly with ai.enabled, BoxLang+, and bx-ai. Redacted prompts and tool resultsThe provider an admin chose. A local model keeps it inside your network
MCP serversOnly if an admin adds one and Lensy needs it. Public https addresses, no credentialsThe address an admin saved. See MCP servers

The buttons Ask ChatGPT and Ask Claude send nothing from the server: your browser opens the site and you paste.

How data and secrets are protected

  • Redaction before storing. Values under keys such as password, token, secret, apikey, authorization, cookie and credential are masked on the server before they reach a page or a store. Add your own names to redact.keys. Query strings, URL credentials and -Dname=value pairs are masked too. See Redaction.
  • Not redacted: the Logs page shows lines as written, exception messages are kept as the application produced them, and the Environment page hides by name only (a secret under a harmless name shows). Treat the console like the log files.
  • Passwords set in the console are salted PBKDF2-HMAC-SHA256 hashes (310000 rounds), compared in constant time. Failed logins are counted per address (and per proxy connection), with a lockout. Sessions are in memory, with an idle and a 12 hour limit.
  • The Secret Store seals the secrets BxLens keeps (the SMTP password, the AI key, channel addresses and tokens, password hashes) with AES-256-GCM. By default the key sits beside the data. That protects against a copied secrets.json and a backup that holds only the data. It does not protect against someone who has the whole folder or runs as the same user. Set LENS_SECRETS_KEY from your platform's secret store to keep the key off the volume.
  • The store holds redacted data. See The store and Backup, retention and disk usage.
  • The audit log records logins, denials and every change, with the role and address, never a password or key.

The threat model in one table

ThreatWhat stops itWhat is left for you
A stranger finds the consoleClosed by default, plain 404 for callers who are not allowed, no listingUse a tight console.access, access.allowedHosts and, if you can, access.requireHeader set by your proxy
Password guessingLockout per address and per proxy connection, a 10 to 256 character password with a slow hash, a login headerUse a long random password. Set access.proxyPeers right so the address is real
Sniffing the passwordconsole.requireHttps, Secure cookieTerminate TLS properly, and require it
A web page attacks the console from a browser (CSRF, DNS rebinding)X-Lens-CSRF on every change, X-Lens-Login on sign in, SameSite=Strict cookie, Host check for a local ruleKeep access.allowedHosts as short as you can
Script injection in the pageEverything from your app is shown as text, no x-html, a policy that allows only your own server as a sourceThe policy is not strict: it allows unsafe-eval for scripts and unsafe-inline for styles. Do not rely on it alone. Keep the console behind your VPN or proxy
A viewer or a leaked viewer passwordA viewer cannot change anything and does not see logs, environment, system, threads or the admin tabsRequest data is still sensitive. Keep the viewer password private too
A stolen admin session12 hour limit, idle timeout (console.sessionMinutes), a password change ends the other sessions of the roleShorten sessionMinutes for a production console
Secrets in a backup or a screenshotSealed store, secrets never shownKeep the key out of the backup that holds the data
The AI sends your data awayOff by default, redacted prompts, approval for every action, a local model possibleUse a local model, or accept the provider's terms
A channel or MCP address points inside your network (SSRF)https only, public addresses only, checked again before each connection, no redirectsDo not add hosts you do not control to notifications.channelHosts
Damage by a mistake of an adminconsole.readOnly, console.actions, a typed name for an application restart, an audit line for each actionTurn them on in production
A heap dump leaks everythingOff by default, admin only, a confirmation, deleted after the downloadTurn console.allowHeapDump on only for the time you need it

A hardening list

In order of value:

  1. Put the console behind your VPN or an authenticating proxy. Then BxLens is the second lock, not the only one.
  2. console.access to the VPN range. access.allowedHosts to your name. access.requireHeader if the proxy can add a secret header.
  3. console.requireHttps: true, and access.proxyPeers to your proxy addresses only.
  4. A long random admin password, kept out of files you commit (${env.NAME}, bxsecret: or set in Settings, Security). A viewer password for everyone who only looks. Change them when someone leaves.
  5. console.sessionMinutes at 15 or less. console.readOnly: true and console.actions: false where nobody needs to act.
  6. collect.level: "light". bar.enabled: false. Keep collectors.queries.includeParams off.
  7. console.allowHeapDump: false. ai.enabled: false or a local provider.
  8. LENS_SECRETS_KEY from your secret manager, and a note in your restore procedure about where the key is.
  9. Permissions on store.dir, secrets.dir and the logs directory: the user that runs BoxLang, and your backup, and nobody else.
  10. Read bxlens-audit.log or ship it. With BoxLang+ switch on the Security alert.
  11. Upgrade. See Upgrading.

Known limits

  • The Content-Security-Policy needs unsafe-eval (the Alpine.js build evaluates expressions) and unsafe-inline for styles. A move to the Alpine CSP build is planned.
  • Shared passwords, no single sign on, no per person accounts, no second factor. Use your proxy for that.
  • Sessions are in memory: a restart signs everyone out.
  • Anything an application puts into a log line or an error message is shown as it was written.
  • The check between a name lookup and the connection for channels and MCP servers is very short but not zero, see Channels.

To report a problem, see Troubleshooting.