The last breach didn’t start at the app server. It started at the dashboard.
With a fresh Metabase 0‑day in the headlines and recent mass thefts from data warehouses splashed across court filings, it’s time to admit something uncomfortable: your BI tooling is part of production. If it can reach customer data, pivot to your warehouse, or mint cloud tokens, it deserves the same engineering discipline you give to your core service. Most teams don’t do that. Attackers know.
Why attackers love your BI stack
Dashboards concentrate value. A single compromised BI service account can unlock an entire warehouse, cross all tenants, and export PII at line rate—often with fewer rate limits and alarms than your customer‑facing APIs. BI is also where good intentions turn into bad defaults: permissive roles so analysts can get work done, embedded secrets to keep cron jobs running, and public links or shared invites that outlive the intern who created them.
Two recent patterns should be on your risk board:
- App‑layer 0‑days on self‑hosted BI: Metabase, Superset, Redash, and friends are complex web apps you expose to internal users. When a remote exploit arrives, it’s a straight line from RCE to cloud keys, JDBC creds, or lateral movement into your warehouse.
- Warehouse exfil via stolen or over‑privileged credentials: We’ve just watched attackers siphon data from dozens of Snowflake tenants using compromised accounts and weak controls around external stages and large result sets. BI tools often store service creds that are perfect for this job.
If you still think of BI as “internal” and therefore safe, you’re playing 2015 security against 2026 adversaries.
A CTO decision framework: make BI a Tier‑0 system
Here’s a concrete plan. Ten decisions, no buzzwords. You can stage most of this in 90 days.
1) Promotion: BI to Tier‑0 with SLOs and patch SLAs
Treat BI like production:
- Patch SLA: Critical CVEs ≤ 72 hours, high ≤ 7 days. Publish the SLA and alert on drift.
- SLOs: Availability, auth latency, and query latency SLOs so you can measure risk to the business when you isolate or disable features during incidents.
- IaC everything: Immutable infra for the BI stack. No “pet” dashboards living on a snowflake VM under someone’s desk account.
2) Network isolation: private, not public
- Place BI servers on private subnets. No public IPs. Access via your ZTNA/VPN with device posture checks.
- Use VPC endpoints/PrivateLink for warehouse access (Snowflake, BigQuery, Redshift, Databricks) so queries never traverse the public internet.
- Block egress to the internet from BI except to approved endpoints (SSO, vendor endpoints). Force all other traffic through an egress proxy with logging.
3) Identity to data: no static passwords
- Use short‑lived, identity‑bound auth for databases and warehouses: AWS RDS IAM auth for Postgres/MySQL, Cloud SQL IAM for GCP, Snowflake key‑pair or OAuth with 1–8 hour TTLs bound to a named service principal.
- Kill shared users. Every query should resolve to a human or a named service with a narrow role. SCIM‑backed provisioning, automatic deprovisioning on offboarding.
- Disable local auth in BI. Enforce SSO+MFA only. No password resets, no password logins.
4) Authorize at the row and column
- Implement row‑level security (RLS) in the warehouse, not in the BI tool. Pass the BI user’s identity as a session parameter and let the warehouse enforce visibility.
- Use column masking for PII and secrets. Full value only for roles that need it; everyone else sees masked or tokenized fields.
- Prefer materialized, sanitized marts over connecting BI to raw prod tables.
5) Least privilege means “no stages, no UDFs, no copy”
- On Snowflake/Databricks, create BI_readonly roles that cannot create stages, external functions, or share data. They can select from approved views and nothing else.
- Block COPY INTO external locations from BI roles. That’s the fastest exfiltration path.
- Set WAREHOUSE size caps and QUERY_ACCELERATION off for BI roles to limit blast radius and cost abuse.
6) Egress controls that actually block exfil
- Restrict BI servers to upload only to an allow‑listed S3/GCS bucket under your account, with Object Ownership enforced and public access blocked.
- Force BI exports through a broker service that stamps lineage, applies watermarks, checks DLP rules, and stores audit metadata before handing users a download link.
- Limit result set size (for example, 100k rows or 100 MB per query) and daily export quotas per user and per team.
7) Secrets: short‑lived or bust
- Store all credentials in a managed secrets service (AWS Secrets Manager, GCP Secret Manager) and rotate at ≤ 30 days. For service‑to‑warehouse, prefer OAuth/IAM tokens with ≤ 8 hour TTLs.
- No secrets in env files or app configs baked into images. Inject at runtime via sidecar or native integration.
- Disable API keys for embedding that bypass SSO. Where embedding is required, use signed, short‑lived tokens scoped to a single view.
8) Query governance: spend and behavior limits
- Set time limits (e.g., 5 minutes) and cost thresholds per query and per user. Kill runaway scans.
- Implement dataset access reviews every 90 days with auto‑expire. If no one re‑ups access, it disappears.
- Require peer approval for creating public links or scheduled exports, with an expiration date by default (e.g., 14 days).
9) Logging, detections, and canaries
- Ship BI app logs and warehouse query logs to a central SIEM. Build alerts for unusual query shapes (SELECT * from wide tables, sudden full‑table scans, massive result sets) and anomalous export behavior.
- Plant honeytables and honeycolumns (non‑referenced but enticing PII fields) visible only to BI roles. Any access triggers a page.
- Record BI session replays for admins, same as you would for a production console.
10) Patching and plugin discipline
- Subscribe to vendor security advisories (Metabase, Superset, Redash). Automate version checks against a golden manifest.
- Forbid arbitrary plugins. Vet, sign, and pin plugin versions. Disable dynamic code execution inside BI where possible.
- Snapshot your BI image nightly and test restore weekly. In a 0‑day, you’ll want to redeploy to known‑good quickly.
Architectural patterns that reduce risk without killing velocity
Don’t connect BI directly to OLTP
It’s tempting to point BI at the production read replica. Don’t. The query patterns are hostile to OLTP, and you’re stretching your blast radius across your most sensitive data. Instead:
- Build analytics marts (dbt/ELT) with just the columns you need, pre‑aggregated where it makes sense, and masked appropriately.
- Refresh windows that meet business needs (hourly/daily), not “live.” Live dashboards are almost never worth the risk unless they’re operational metrics sourced from non‑PII telemetry.
Make embedding safe or don’t do it
- Use signed embed tokens with a 5–15 minute TTL and per‑view scopes. No static tokens; no global embed keys.
- Serve embedded dashboards through a backend proxy that injects identities and enforces RLS/CLS consistently, not from the browser.
Centralize exports through a broker
Most leakage happens during “download CSV.” Centralize that path:
- Exports from BI write to a controlled bucket. A broker service then checks DLP rules, stamps lineage, applies consistent formatting/watermarks, and produces a signed URL with a 10‑minute TTL.
- Expose a team‑level export ledger with reason codes. If people know their peers can see large exports, behavior improves.
Incident: your BI was just 0‑dayed. Now what?
When your BI vendor posts a critical advisory—or you catch a canary firing—treat it like a production incident. A practical runbook:
- Isolate: Remove BI from the network path (security group deny), pause external sharing, and snapshot the instance and disk for forensics.
- Revoke: Rotate warehouse creds, OAuth clients, and any cloud keys accessible from the BI node. In Snowflake, disable external stages and the affected roles until scoping is complete.
- Scope: Query warehouse audit logs for the BI service principal and any admin users. Look for unusual query shapes, time windows, result sizes, and COPY/UNLOAD commands.
- Contain: Temporarily enforce stricter quotas (smaller result caps, shorter TTLs) and pause scheduled exports organization‑wide.
- Restore: Redeploy BI from a pinned, patched image. Re‑enable roles progressively after an access review.
- Notify: If there’s material risk to customer data, prepare your disclosure with timelines, affected data categories, and the controls you’ve enacted.
Measure time to isolate, time to revoke, and time to restore as you would any P1. If these stretch into days, you’ve just built your board deck for resourcing next quarter.
SaaS BI vs self‑hosted: the real trade‑offs
Going SaaS (Looker, Mode, Hex, others) won’t save you from bad identity and data governance, but it does move patching and most app‑layer 0‑days off your plate. Trade‑offs:
- SaaS BI: Fewer patching headaches, mature admin consoles, and vendor SOC2/ISO audits. But data egress posture now includes a third party. You’ll want PrivateLink/VPC peering patterns, tenant isolation guarantees, and data residency commitments in the contract.
- Self‑hosted BI: You control everything—network, plugins, data plane—but you own every CVE, migration, and supply‑chain update. This can work well if you already run a private data platform and have infra discipline. Otherwise, you’re inventing a product security surface area you didn’t plan for.
My rule of thumb: if you can’t patch a critical CVE across your BI fleet in ≤ 72 hours and rotate warehouse creds in ≤ 24 hours, choose SaaS with private connectivity. Otherwise, self‑host with the controls in this playbook.
Data minimization beats clever controls
The single most effective control is not having the data in the first place:
- Classify and cull: Identify PII columns in your warehouse and remove them from marts feeding BI unless absolutely necessary. Replace with aggregates or tokens.
- TTL snapshots: Set expiry policies on staging tables and intermediate artifacts so “temporary” does not become “forever.”
- Differential privacy for metrics that don’t need row‑level fidelity.
Small team? A practical 30‑60‑90
Days 0–30: close the obvious holes
- Kill public IPs to BI; require SSO+MFA; disable local auth.
- Move BI to private subnets with egress through a proxy; allow‑list warehouse endpoints via PrivateLink.
- Rotate all warehouse and BI service credentials; remove shared users; enable audit logging.
- Set conservative result caps and per‑user daily export limits; pause public links and unreviewed scheduled exports.
Days 31–60: identity and authorization
- Implement IAM/OAuth‑backed auth to warehouses with 1–8 hour TTLs.
- Push RLS/CLS to the warehouse; migrate BI queries to sanctioned views and marts.
- Create least‑privilege roles for BI (no stages/UDFs/COPY). Add canary tables/columns and alerts.
Days 61–90: governance and resilience
- Stand up an export broker with DLP checks and watermarks; route all downloads through it.
- Document and drill the BI 0‑day incident runbook; measure isolate/revoke/restore times.
- Automate version pinning and patch rollouts; define and enforce patch SLAs.
Nearshore partners can help execute the boring, necessary parts: IaC, policy codification, SCIM/SSO wiring, and testable runbooks. None of this is glamorous. All of it prevents very public weekends.
What good looks like
When you get this right, your BI surface feels like your prod console:
- Engineers ship dashboards the same way they ship services: through code review, CI, and pinned images.
- Analysts authenticate with SSO+MFA; every query maps to a person; access expires if unused.
- PII never appears in marts by default; when it does, masking and RLS/CLS apply automatically via shared policies.
- Exports are visible, rate‑limited, watermarked, and auditable. Exfil is hard and obvious.
- When the next 0‑day drops, you isolate in minutes, revoke in hours, restore in a day—with a clean paper trail.
You don’t get there by buying a tool. You get there by treating BI like what it is: a production system with a big red button labeled “download.”
Key Takeaways
- Stop calling BI “internal.” If it touches customer data, it’s production. Promote it to Tier‑0 with real SLOs and patch SLAs.
- Kill public exposure. Put BI on private subnets with ZTNA, PrivateLink to warehouses, and locked‑down egress.
- No static creds. Use short‑lived, identity‑bound auth to data stores; ban shared users and local logins.
- Authorize in the warehouse. Enforce RLS/CLS and least privilege; block stages, UDFs, and COPY/UNLOAD for BI roles.
- Centralize and govern exports. Cap result sizes, add DLP via a broker, watermark everything, and audit aggressively.
- Drill the 0‑day runbook. Measure isolate/revoke/restore times like any P1. If you can’t patch in 72 hours, rethink your approach—or go SaaS with private connectivity.