| Version | Supported |
|---|---|
| Latest release | Yes |
| Previous minor | Security fixes only |
| Older | No |
If you discover a security vulnerability in OpenObscure, please report it responsibly using GitHub's private vulnerability reporting.
- Go to the OpenObscure GitHub repository → Security tab → Report a vulnerability
- Include:
- Description of the vulnerability
- Steps to reproduce
- Affected component(s) (L0 Core proxy, L1 plugin, L2 crypto)
- Potential impact assessment
- Suggested fix (if any)
Do not open a public issue for security vulnerabilities — use GitHub's private reporting to ensure the issue can be assessed and fixed before public disclosure.
OpenObscure is a community-maintained open-source project. Response times depend on contributor availability and the nature of the vulnerability. We aim to:
- Acknowledge reports promptly
- Assess severity and impact
- Develop and release a fix, coordinating disclosure with the reporter
- Disclose publicly after the fix is available
Critical vulnerabilities (key extraction, proxy bypass, plaintext PII leaks) will be prioritized. Community contributions to fixes are welcome and encouraged.
- Bypass of PII detection (input that should be caught but isn't)
- FPE key extraction without OS keychain access
- Transcript decryption without the passphrase
- L0 Core proxy bypass (traffic reaching LLM without passing through proxy)
- File Access Guard bypass (reading denied paths)
- Memory leaks of plaintext PII
- Denial of service against the proxy
- Dependency vulnerabilities with exploitable paths in OpenObscure
- ONNX model substitution attacks (replacing SCRFD/BlazeFace/PaddleOCR models with malicious ones)
- Response integrity model bypass (crafting text that evades both R1 dictionary and R2 classifier detection)
- Image processing bypass (crafting images where faces/text aren't detected)
- Resource exhaustion via image processing (OOM, CPU spin on adversarial inputs)
- Base64 bomb attacks (crafted base64 strings that decode to extremely large images)
- Attacks requiring root/admin access to the host OS (see Threat Model — this is explicitly out of scope)
- Attacks requiring a compromised host agent process
- PII types not yet covered (tracked in roadmap)
- Regex evasion using obfuscated/unusual PII formats (report these as feature requests, not vulnerabilities)
- Vulnerabilities in LLM providers, the host agent itself, or upstream dependencies without a demonstrated exploit path through OpenObscure
OpenObscure's security depends on the secrecy of keys, never on the secrecy of code. All algorithms used (FF1, AES-256-GCM, Argon2id) are public NIST/OWASP standards. Publishing source code does not weaken the system.
Three independent layers (L0-L2) ensure that a failure in one layer does not expose all PII. L0 (proxy) and L1 (plugin) operate at different interception points — compromising one does not bypass the other.
- No telemetry — zero outbound connections beyond forwarded LLM requests
- No default credentials — FPE key must be explicitly generated with
--init-key - No cloud dependencies — everything runs locally on the user's device
- Passthrough-first auth — OpenObscure never provisions or stores LLM API keys by default
OpenObscure supports multiple secret storage backends to accommodate both desktop and headless (Docker, CI, VPS) environments. The priority chain for each secret is:
FPE master key (32 bytes, AES-256) — see FPE Configuration for full setup and rotation:
OPENOBSCURE_MASTER_KEYenvironment variable (64 hex chars) — for headless/Docker/CI- OS keychain (macOS Keychain, Windows Credential Store, Linux keyutils/Secret Service) — for desktop
- Encrypted file (
~/.openobscure/master.key.enc) — fallback for environments without keychain or env vars - Fail (503 — proxy refuses to start without a key)
L0/L1 auth token (32 bytes, hex):
OPENOBSCURE_AUTH_TOKENenvironment variable — for headless environments~/.openobscure/.auth-tokenfile (permissions 0600 on Unix) — auto-generated on first run- Auto-generate with
OsRngand write to file
Security trade-offs: Environment variables are intentionally supported because containerized and headless environments lack OS keychain access. Env vars are visible to process inspectors (e.g., /proc/*/environ on Linux). This is the standard pattern used by Docker secrets, Kubernetes secrets, and HashiCorp Vault. For maximum security on desktop environments, prefer OS keychain over env vars.
Secrets are never written to:
- Source code or config files
- Log output (all logging passes through PII scrubbing)
The FPE key and transcript passphrase are independent — compromising one does not expose the other.
- All dependencies are audited for license and security before inclusion (see
LICENSE_AUDIT.mdin each component) - Rust dependencies use
cargo auditfor known vulnerability scanning - L1 plugin has one optional runtime dependency (
@openobscure/scanner-napifor native PII scanning). All other Node.js dependencies are dev-only (build/test). - ONNX models are sourced from HuggingFace with checksum verification
- L0 and L2 are written in Rust — memory-safe by default (no buffer overflows, use-after-free, or data races)
- L1 is TypeScript with strict mode — type-checked at compile time
- PII values are never logged — only match counts and types are recorded
- Integration tests verify FPE roundtrip correctness and PII redaction completeness
- Release binaries built with
opt-level = "s", LTO, symbol stripping,panic = "abort" - No debug symbols in release builds
- Binary verifiability is a goal — see open_source_strategy.md for the build verification approach
The image pipeline introduces additional attack surfaces that are actively mitigated:
| Attack | Mitigation |
|---|---|
| Malicious images (crafted PNG/JPEG headers to exploit decoders) | image crate's decoders are Rust (memory-safe). Images resized to 960px max before processing. |
| Base64 bombs (small base64 that decodes to massive images) | Size check after base64 decode, before image::load_from_memory(). Max dimension enforced. |
| Adversarial inputs to face/OCR models | Fail-open — undetected faces/text pass through. Models are supplementary to text-based PII scanning. |
ONNX model substitution (replacing .onnx files with malicious models) |
Models verified by SHA256 checksum against trusted manifest. Model paths are admin-configured, not user-supplied. |
| Resource exhaustion (large images causing OOM or CPU spin) | 960px resize cap, sequential model loading (never both face + OCR in RAM), 224MB hard ceiling. |
| EXIF-based attacks (crafted EXIF metadata) | EXIF read via kamadak-exif for screenshot detection only; EXIF is stripped by re-encoding (pixels only). |
SSRF via URL image fetch (url_allow_localhost_http = true) |
Attacker-injected image_url values pointing to http://127.0.0.1:* cause the proxy to make GET requests to any loopback-bound service. Default is true for testing only. Set url_allow_localhost_http = false in production. Residual risk: hostnames bypass numeric-IP SSRF check and are resolved at connection time — use url_fetch_enabled = false or network-level egress controls in high-security environments. See Config Reference — image.url_allow_localhost_http. |
The R2 cognitive firewall introduces a TinyBERT ONNX classifier for persuasion detection:
| Attack | Mitigation |
|---|---|
| R2 model substitution (replacing ONNX file with malicious model) | Model path is admin-configured, not user-supplied. SHA256 checksum verification against trusted manifest. |
| R2 adversarial inputs (crafting manipulative text that evades R2) | R2 is supplementary to R1 dictionary. Adversarial text evading R2 may still be caught by R1 phrase matching. Fail-open: R2 model failure falls back to R1-only. |
| R2 model extraction (extracting model weights from ONNX file) | Model weights are readable from the ONNX file. Not a security concern — the model detects manipulation patterns, it does not contain user data. |
| R2 denial of service (triggering expensive inference repeatedly) | First-window early exit (128 tokens) limits inference cost for clean text. Model evicted after idle timeout (default 300s). Sensitivity tier controls R2 invocation rate. |
The /_openobscure/health and /_openobscure/ner endpoints require the X-OpenObscure-Token header (32-byte hex, stored at ~/.openobscure/.auth-token with 0600 permissions). If you run L0 behind a reverse proxy (Nginx, Caddy, Traefik), ensure /_openobscure/* routes are not exposed externally — they should only be reachable from localhost or the L1 plugin process.
The crash buffer (crash_buffer = true in [logging]) is disabled by default. Without it, forensic log data is permanently lost after an OOM kill or SIGKILL. For production deployments where forensic completeness matters (HIPAA, PCI-DSS incident response), enable the crash buffer:
[logging]
crash_buffer = true
crash_buffer_size = 4194304 # 4MBSee Crash Recovery for forensic workflow details.
The following files under ~/.openobscure/ contain security-relevant data. Remove them when uninstalling OpenObscure:
| File | Contents |
|---|---|
.auth-token |
32-byte L0/L1 auth secret |
request_journal.buf |
Request metadata (UUIDs, timestamps, PII span counts — no plaintext PII) |
crash.buf |
Buffered log lines (PII-scrubbed, but contains request IDs) |
.crashed |
Panic marker (contains panic message and stack location) |
The FPE master key is stored in the OS keychain — remove it separately using keyring delete openobscure fpe-master-key or your platform's keychain manager.
OpenObscure has not undergone a formal third-party security audit. The cryptographic implementation uses well-tested library implementations (RustCrypto ecosystem) rather than custom primitives. Community review is welcomed and encouraged.
If you or your organization are interested in sponsoring a formal audit, please reach out.