Read-only database access is not enough for AI agents
A read-only credential stops writes. It does not stop sensitive data exposure, expensive queries, mass export, or inconsistent answers. Here is the full security model.
Every guide for connecting AI to a database gives the same advice: use a read-only user.
It's good advice. We give it too. But somewhere along the way, “read-only” became shorthand for “safe,” and those are not the same thing.
A read-only credential stops exactly one category of problem: writes. The AI can't drop a table, can't update a row, can't inject data. That's necessary. Here's everything it doesn't stop.
What read-only doesn't prevent
Sensitive data exposure
Read-only on the whole database means the AI can read the whole database. Payroll, customer PII, internal pricing, the acquisition model in a forgotten finance_scratch schema.
Nobody plans to expose these. They get exposed because the read-only role was granted on ALL TABLES during a ten-minute setup, and nobody went back to narrow it. The AI will happily query whatever it can see, and unlike a human analyst, it doesn't know which tables it isn't supposed to look at.
We've seen this firsthand: across the 76,000+ AI-generated queries in our logs, agents hit information_schema over 2,000 times, often right after an access-denied error, probing for what else they could reach. Not malicious, just an agent trying to be helpful. That's exactly why the boundary needs to be enforced, not implied.
Expensive queries
A SELECT with a bad join against a billion-row table is read-only. It's also a four-figure BigQuery bill, a locked-up Postgres replica, or a warehouse queue that stalls everyone's dashboards.
AI agents write more queries than humans, faster, and with less intuition about cost. Read-only does nothing here. You need result limits, query timeouts, and ideally a layer that watches for pathological queries.
Mass data export
Reading is exactly what exfiltration looks like. SELECT * FROM customers is a perfectly valid read-only query. So is running it table by table until an entire database has walked out the door through a chat window.
This matters more with prompt injection in the picture. If an agent processes untrusted input (a document, a webpage, a pasted email) an injected instruction can ask it to read sensitive rows and include them in its output. The read-only credential is not the defense here. Scoped table access and result limits are.
Inconsistent answers
Not a security issue in the traditional sense, but a trust issue with real consequences. The AI guesses what your columns mean, and it guesses differently each session. In our query logs we found one session where Claude tried ORDER_TOTAL, then AMOUNT, then TOTAL_AMOUNT, then VALUE for the same revenue question, six wrong guesses before it found the real column.
Now imagine two people asking that question in separate conversations, each getting a different guess. One of those numbers goes into a board deck. The data team spends a day tracing it. Everyone trusts the tool a little less. Read-only had nothing to say about any of this.
No accountability
When something does go wrong, a wrong answer, an exposed row, a suspicious query pattern, a read-only credential leaves no trail. The database log shows one shared role running queries. Who asked? From which conversation? Was it the agent following instructions or a person fishing?
Without per-user identity and query logging, the answer is a shrug.
Stale credentials on laptops
The typical read-only setup puts the connection string in a config file on each person's machine. The credential is read-only, but it's still a credential: on laptops, in dotfiles, occasionally in a git commit. When someone leaves, it needs rotating everywhere at once. Most teams don't.
The pattern behind all of this
Read-only answers “what can this credential do to the database?”
It doesn't answer:
- Who is asking (identity)
- What they should see (scoping)
- How much they can pull (limits)
- What it means (context)
- What happened (audit)
Those five are the actual security model for team AI data access. Read-only is the sixth item, and the easiest one.
What sufficient looks like
If you're setting this up for a team, the checklist we'd hold any setup against, including our own:
- Read-only credentials against a replica, never the primary
- Table and column scoping per user or group, enforced, not implied
- Result limits and query timeouts
- Individual user identity, no shared credentials on laptops
- A query log: who asked, what SQL ran, what came back
- Shared metric definitions so answers are consistent
- One place to revoke access when someone leaves
You can build all seven around an open-source MCP server. Most teams build the first one, feel safe, and stop.
Where Contextflo fits
This checklist is essentially Contextflo's product spec. Your database is connected once, read-only, behind an access layer. Users authenticate individually and see only their scoped tables. Every question and query is logged. Definitions are shared. Offboarding is one click.
The point of this post isn't that read-only is wrong. It's step one. The mistake is stopping there and calling it a security model.
Related reading
- How to connect Claude or ChatGPT to your company data: the full picture, from direct connection to governed access
- DIY MCP server vs Contextflo for teams: the build-vs-buy breakdown, gap by gap
- Not every LLM SQL error is a hallucination: what we learned from 76K AI-generated queries