Read-only database user vs BI tool permissions: what's the difference

4 min read February 20, 2024

When you connect a BI tool or query assistant to your database, there are two distinct layers of access control — and most teams only think about one of them.

Understanding both layers, and how they interact, helps you make better decisions about what to configure and what to trust.

The two layers

Layer 1: Database user permissions

This is the access control enforced by the database itself. When you create a database user with GRANT SELECT ON ..., you’re configuring what that user is allowed to do at the database level. The database engine enforces this unconditionally — no application can override it.

If a user only has SELECT permission, any INSERT, UPDATE, DELETE, or DROP statement issued under that user’s credentials will be rejected by the database engine with a permission error. This happens regardless of what application is making the connection.

This layer is your foundational control.

Layer 2: Application-level enforcement

This is what the tool itself does before it sends a query to the database. A tool that has “safe mode” or “read-only mode” is running its own checks on the generated SQL before executing it.

For example: the tool receives your natural language prompt, generates a SQL query, validates that it’s a SELECT statement (rejecting anything else), and only then executes it.

This layer can catch things that database-level permissions don’t, like:

  • Blocking SELECT queries that are structurally dangerous (e.g., missing a WHERE clause on a large table)
  • Blocking schema inspection queries you don’t want the user to run
  • Rate limiting queries per user or per connection

But application-level enforcement has a fundamental limitation: it depends on the application working correctly.

Why both layers matter

The two layers are complementary, not redundant.

Database permissionsApplication enforcement
Enforced byDatabase engineApplication code
Can be bypassed byHaving credentials + direct DB accessBugs, misconfigurations, or intentional override
ScopeWhat the user is allowed to doWhat the application allows the user to do
Survives app bugsYesN/A (it is the app)
Survives direct DB connectionYesNo

The key insight: application-level enforcement only applies when the query goes through the application. If credentials are leaked and an attacker connects to the database directly, application-level read-only mode is irrelevant. The database user’s permissions are all that matters.

Conversely, database permissions don’t protect against all types of unsafe queries — they only reject queries that the user isn’t permitted to run. A read-only user can still run a SELECT that reads every row in your largest table and causes performance issues.

Defense in depth

Using both layers together means:

  1. Even if the application has a bug and generates a write query, the database rejects it
  2. Even if credentials are leaked and someone connects directly, they can only read (not write) data
  3. Even if someone bypasses application enforcement, they’re bounded by database permissions

Neither layer alone provides this complete coverage.

Comparing configurations

ConfigurationDB breach + credentialsApp bug generates writeDirect DB connection
Root user, no app enforcementFull accessExecutesFull access
Root user + app read-onlyFull accessBlocked by appFull access
Read-only user, no app enforcementRead-onlyRejected by DBRead-only
Read-only user + app read-onlyRead-onlyBlocked by app and rejected by DBRead-only

The bottom row is the configuration to aim for. It provides protection at both layers, in both the application path and the direct connection path.

What to look for in a tool

When evaluating a query tool:

Application-level read-only enforcement:

  • Does the tool explicitly validate that generated queries are read-only before executing them?
  • Is this enforced at the application layer, not just at the UI layer?
  • Does it block DDL statements (DROP, ALTER, CREATE) in addition to DML (INSERT, UPDATE, DELETE)?

Database connection:

  • Does the tool support or recommend connecting with a least-privilege database user?
  • Does the setup documentation show how to create a read-only user?

The right answer to both is yes. Application enforcement and database-level scoping should be treated as complementary, not alternative.

The practical checklist

Before connecting any database tool:

  1. Create a read-only database user — see guides for PostgreSQL and MySQL
  2. Scope the user to the tables you need — see limiting access to specific tables
  3. Confirm the tool enforces read-only at the application layer — check documentation or ask the vendor
  4. Verify the setup — connect, run a SELECT, then attempt an INSERT or UPDATE and confirm both behave as expected