SecureBrowser (CEF Sandbox)

1. Overview

The SecureBrowser module implements BastionGuard’s hardened browsing mode based on Chromium Embedded Framework (CEF). It is intended for high-risk interactions such as banking logins and payment/checkout flows and enforces a strict navigation policy based on allowlisted domains (banks + optional payment providers) with a local warning interstitial for blocked destinations.

This module is used by the Secure Payments / Secure Browser entrypoints (e.g., the launcher that spawns BastionGuard-bankopener) and is designed to:

  • Run in a dedicated process and enforce “single instance” behavior via a lockfile
  • Initialize CEF with security-oriented command-line switches
  • Restrict navigation to trusted bank/payment domains while preserving functional flows (SSO, iframes, redirects)
  • Display a local HTTP warning page for blocked domains
  • Apply certificate-error handling policy (bypass only for trusted domains)
  • Provide robust backend fallback (Wayland → X11 retry, and final fallback to xdg-open)

2. Major Subsystems

  • CEF client handlers – request interception, lifecycle management, and UI watermark injection
  • Domain allowlists – system + user JSON lists for banking and payments
  • LocalWarningServer – local interstitial server used for blocked navigation warnings
  • SettingsStore – runtime toggle controlling payments allowlist activation
  • InstanceLock – lockfile-based singleton enforcement across processes

3. Local Warning Interstitial

SecureBrowser uses a local HTTP warning server to show a controlled interstitial page when navigation is blocked. The server is bound to a dedicated local IP and port:

  • IP: 127.0.0.2
  • HTTP port: 81

The server is started once per process via start_local_warning_server() and is kept alive using a global singleton pointer (g_warning_server). The current configuration runs HTTP only (no certificate material is provided).

Blocked navigations are redirected to:

http://127.0.0.2:81/<blocked_host>

4. Single-instance Enforcement

4.1 In-process Guard

A process-level guard prevents duplicate initialization in the same process:

static std::atomic<bool> g_cef_started{false};

If a duplicate request occurs, SecureBrowser falls back to the system browser (xdg-open).


4.2 Cross-process Lockfile

To enforce a single active instance across processes, SecureBrowser uses a lockfile:

/tmp/BastionGuard-bankopener.lock

The InstanceLock utility:

  • Creates/opens the lock file with mode 0600
  • Attempts a non-blocking exclusive flock (LOCK_EX | LOCK_NB)
  • If acquired, writes the process PID into the file and fsyncs it
  • Releases the lock on destruction, but intentionally does not remove the file (acts as a beacon and avoids races)

If the lock cannot be acquired, SecureBrowser assumes another instance is active and falls back to xdg-open.


5. Domain Allowlist Loading and Normalization

5.1 Host Parsing and Canonicalization

URLs are normalized to a host using:

  • Strip scheme (://) and path suffix
  • Remove credentials (user@host) if present
  • Lowercase
  • Remove leading dot
  • Strip www.
  • Strip port (e.g., :443)

The canonicalization pipeline is implemented by host_from_url() and clean_domain().


5.2 JSON Allowlist Format Flexibility

Allowlist files are loaded by load_list_file(path, out), which supports multiple JSON shapes:

  • Object with list array
  • Object with domains array
  • Object with whitelist array
  • Root-level array of strings

Binary reads are sanitized by removing embedded NUL bytes before JSON parsing to reduce parsing failures on corrupted files.


5.3 Banking Trusted Domains

Trusted banking domains are initialized lazily and cached in-memory:

  • System list: /usr/share/BastionGuard/data/bank/banks.json
  • User override: ~/.config/BastionGuard/whitelist.json

The cache uses an unordered_set for constant-time membership checks.

Matching behavior:

  • Direct match: host must equal an allowlisted domain
  • Parent-domain match: subdomains are allowed if any parent suffix matches the allowlist (e.g., login.sub.bank.com → allow if bank.com is allowlisted)

5.4 Payment Domains (Optional)

Payment-domain allowlisting is an optional subsystem controlled by:

SettingsStore::get_secure_payments_enabled()

If disabled, payment-domain checks always return false. If enabled, the module initializes a dedicated cache from:

  • System list: /usr/share/BastionGuard/data/payments/payments.json
  • User override: ~/.config/BastionGuard/payments.json

Matching supports both direct and parent-domain suffix matches (e.g., sub.gateway.paypal.compaypal.com).


6. CEF Client Handlers

6.1 SimpleHandler Responsibilities

The CEF client implementation (SimpleHandler) provides:

  • LifeSpan handling – tracks open browser windows and exits message loop when all are closed
  • Display handling – injects a UI watermark on title change
  • Request handling – enforces navigation policy and certificate error policy

6.2 Window Lifecycle

  • OnAfterCreated() adds the browser instance to an internal vector
  • OnBeforeClose() removes the instance and calls CefQuitMessageLoop() when no windows remain

This guarantees deterministic shutdown of the CEF message loop and supports clean process termination.


6.3 Watermark Injection

On title changes, SecureBrowser injects a fixed-position watermark into the main document using JavaScript. The watermark is appended only once (guarded by a DOM element ID) and is non-interactive (pointer-events: none).

The watermark serves as a user-visible assurance indicator that the user is inside the Secure Browser session.


7. Navigation Policy and Trusted Session Model

7.1 Policy Goals

The navigation policy is designed to:

  • Strictly prevent user-driven navigation from leaving trusted banking/payment domains
  • Allow necessary embedded resources during trusted sessions (iframes, SSO redirects, CDNs, CAPTCHA)
  • Provide a safe interstitial when blocked destinations are requested

7.2 Localhost Allow Rule

Requests to local infrastructure are always allowed to prevent self-blocking of the warning server:

  • 127.0.0.2, 127.0.0.1, localhost

7.3 Trusted Session State Machine

SecureBrowser maintains a session-level trust flag:

  • g_session_trusted – set to true once the main frame successfully loads a trusted bank/payment entry domain
  • g_entry_host – stores the entry host for diagnostics

Policy rules:

  1. Session entry: if the navigation is for the main frame and the host is bank/payment allowlisted, the session is marked trusted and navigation is allowed.
  2. Subframe permissive mode: while in a trusted session, all subframe loads are allowed to preserve real-world banking flows (embedded auth, iframes, integrations).
  3. Non user-gesture main-frame: while in a trusted session, main-frame navigations without user gesture are allowed (technical redirects and SSO flows).
  4. User-initiated main-frame exits: while in a trusted session, if the user clicks and attempts to navigate the main frame to a non-allowlisted host, the navigation is blocked and redirected to the warning interstitial.
  5. No trusted session: before session trust is established, only allowlisted bank/payment hosts are allowed. All other requests are blocked and redirected to the warning interstitial.

Blocked requests are redirected by loading the local warning URL in the main frame and returning true to cancel the original navigation.


8. Certificate Error Policy

Certificate errors are handled in OnCertificateError(). The policy is:

  • Bypass certificate errors only for bank/payment allowlisted hosts
  • Block certificate errors for all non-trusted hosts and redirect to the local warning interstitial

For trusted hosts, the handler calls callback->Continue(). For non-trusted hosts, it calls callback->Cancel().

This design prevents users from being trained to click through TLS errors on unknown domains while maintaining operational usability for allowlisted targets.


9. CEF Process Configuration (ClamApp)

9.1 Command-line Hardening

CEF command-line switches are applied in ClamApp::OnBeforeCommandLineProcessing(). Key security and isolation switches include:

  • incognito
  • disable-extensions
  • disable-sync
  • disable-component-update
  • disable-print-preview
  • disable-background-networking
  • disable-crash-reporter, disable-breakpad
  • no-default-browser-check, no-first-run

Feature toggles are applied via AppendSwitchWithValue to avoid collisions between multiple enable/disable-features settings:

  • disable-features=TranslateUI
  • enable-features=UseOzonePlatform,VaapiVideoDecoder,UseSkiaRenderer,DnsOverHttps

9.2 TLS Trust Store Configuration

SecureBrowser pins certificate bundle configuration to the system CA bundle:

  • ssl-certificates-file=/etc/ssl/certs/ca-certificates.crt

Additionally, environment variables are set before CEF initialization to maximize trust store compatibility across Chromium/NSS consumers:

  • SSL_CERT_DIR, SSL_CERT_FILE
  • NSS_CERT_DIR, NSS_CERT_FILE

9.3 DNS-over-HTTPS (DoH)

The sandbox enables DNS-over-HTTPS in automatic mode and defines a default server list:

  • dns-over-https-mode=automatic
  • dns-over-https-servers=https://cloudflare-dns.com/dns-query,https://dns.google/dns-query

This can reduce dependency on local DNS interception (e.g., dnsmasq) for this hardened browsing context.


9.4 Graphics and Platform Backend Selection (Wayland/X11)

The module supports both Wayland and X11 environments. Backend selection logic:

  • If BastionGuard_CEF_BACKEND is explicitly set, it is honored (wayland or x11)
  • Otherwise, presence of WAYLAND_DISPLAY selects Wayland

The ozone platform is set accordingly:

  • Wayland: ozone-platform=wayland + enable-webrtc-pipewire-capturer
  • X11: ozone-platform=x11

10. CEF Initialization and Fallback Strategy

10.1 Resource and Cache Paths

CEF runtime directories:

  • Resources: /usr/share/BastionGuard/cef
  • Locales: /usr/share/BastionGuard/cef/locales
  • Cache: ~/.cache/BastionGuard/cef_sandbox

The cache directory is created best-effort using std::filesystem::create_directories().


10.2 Initialization Attempts

CEF initialization is attempted via an internal helper try_init_cef(backend):

  • Initial attempt: “auto” (no forced backend)
  • If it fails and WAYLAND_DISPLAY is set, the module unsets Wayland and retries with x11
  • If initialization fails definitively, the module falls back to xdg-open

CEF is configured with:

  • no_sandbox = true (CEF/Chromium sandbox disabled at CEF level; isolation is enforced by policy and runtime constraints)
  • log_severity = warning
  • Explicit resource/locales/cache paths

11. Window Creation and Message Loop

On successful initialization:

  • A CefBrowserView is created with JavaScript and WebGL enabled
  • A top-level CEF window is created via a custom CefWindowDelegate
  • The message loop runs using CefRunMessageLoop()
  • On exit, the module calls CefShutdown() and resets g_cef_started

If CreateBrowserView() returns null, the module performs a controlled fallback: xdg-open + CefShutdown().


12. Security and Operational Considerations

  • Allowlist enforcement: primary defense relies on strict allowlists with parent-domain matching; list hygiene is critical.
  • Trusted session permissiveness: subframes are allowed during trusted sessions to support real flows; this is a deliberate usability/security trade-off.
  • User-initiated exit blocking: main-frame clicks to non-trusted domains are blocked once a trusted session is established.
  • Certificate bypass scope: TLS error bypass is limited to allowlisted bank/payment hosts; all others are blocked and warned.
  • Local interstitial security: the warning server is bound to loopback; blocking logic explicitly allows localhost to prevent self-denial.
  • Process singleton: lockfile-based singleton reduces multi-instance confusion and constrains attack surface.
  • Fallback behavior: when hardening cannot be guaranteed (init failure / existing instance), the module degrades to system browser rather than partially working insecurely.
  • CEF sandbox flag: no_sandbox=true indicates Chromium’s internal sandbox is disabled; if stronger isolation is required, consider enabling CEF sandbox and/or running the process under OS-level confinement (e.g., namespaces, seccomp, bubblewrap).

In summary, SecureBrowser delivers a hardened CEF-based browsing session centered on allowlist-driven navigation control, local warning interstitials, and conservative TLS error handling, with robust startup/compatibility fallbacks for real-world Linux desktop environments.