# How do you connect an AI model to a database safely?

Date: 2026-07-20 · Author: Robert Scales · https://guidetoartificialintelligence.com/en/learn/connect-ai-model-database-safely/

**Answer:** Give the model its own database account that can only read the few tables or views it needs, and let your code, not the model, run every query, with values passed as parameters. That way, even a confused or manipulated model can only see what you chose to show it and cannot change anything.

Connecting a language model to a database is one of the most useful things you can build: staff can ask "How many orders did we ship to Quebec last month?" in plain words and get an answer from live data. It is also one of the easiest ways to cause damage, because the model's output turns into commands that a real system carries out.

The safe approach does not depend on writing a clever prompt. It depends on limits the database itself enforces. Assume the model will sometimes misunderstand a request, and that someday a document or message it reads will try to trick it. This trick is called **prompt injection**: text hidden in the input that tells the model to do something its owner never intended. Then design the connection so that neither mistake can hurt you.

## The risk has a name: excessive agency

The OWASP Gen AI Security Project, part of the OWASP open security community, lists **excessive agency** among the top risks for AI applications. It means an AI system can take harmful actions because of a bad, unclear or manipulated output from the model. OWASP traces it to three causes:

| Cause | Database example | Fix |
|---|---|---|
| Too much functionality | The tool can run any SQL, not just look things up | Offer only the lookups the task needs |
| Too many permissions | The account can also insert, update and delete | A read-only account limited to specific tables |
| Too much autonomy | Results or changes go out with nobody checking | A person approves anything with real impact |

OWASP's own example fits exactly: a feature meant only to read data that connects with an account allowed to write and delete as well. Its recommendation is to enforce the limit with database permissions on the account the AI uses, not with instructions to the model.

## A worked example: a read-only role in PostgreSQL

Here is a setup for PostgreSQL, a popular open-source database. The goal is an assistant that answers sales questions from an `orders` table, without seeing customer names or emails.

```sql
-- 1. A dedicated account for the AI tool, used by nothing else
CREATE ROLE ai_reader LOGIN;  -- set its password from your secrets manager

-- 2. A view that exposes only the columns the assistant needs
CREATE SCHEMA reporting;
CREATE VIEW reporting.order_summary AS
  SELECT order_id, order_date, region, total
  FROM public.orders;

-- 3. The smallest set of privileges that makes it work
GRANT CONNECT ON DATABASE shop TO ai_reader;
GRANT USAGE ON SCHEMA reporting TO ai_reader;
GRANT SELECT ON reporting.order_summary TO ai_reader;

-- 4. Safety nets for every session of this account
ALTER ROLE ai_reader SET default_transaction_read_only = on;
ALTER ROLE ai_reader SET statement_timeout = '5s';
```

Why each part matters. In PostgreSQL, a new table belongs to whoever created it, and other accounts get nothing on it until someone grants a **privilege**, a named permission such as SELECT (read) or UPDATE (change). So `ai_reader` starts with no access to `orders` at all. The **view**, a saved query that looks like a table, shows four columns and hides the rest; the OWASP SQL injection guidance recommends exactly this, granting access to a view instead of the underlying table. `USAGE` on the schema lets the account find the view, and `SELECT` lets it read. The last two lines make every session read-only by default and stop any query that runs longer than five seconds, so a runaway request cannot slow the database for everyone.

> **Try it:** On a test copy of your database, create a role like `ai_reader`, connect as it, and try `UPDATE reporting.order_summary SET total = 0;`. You should get a permission error. Then try reading `public.orders` directly, which should fail too.

## Let your code run the queries

The second layer is in your application. There are two common designs.

**Fixed queries with parameters.** You write the SQL, and the model only picks the query and fills in values, such as a region and a month. Your code passes those values separately from the SQL text, which is called a **parameterized query**. OWASP's SQL injection guidance names this its first defense: the database always treats a parameter as data, so input like `tom' or '1'='1` is searched for as text rather than run as code.

```python
import psycopg

def orders_by_region(region: str, month_start: str):
    with psycopg.connect(AI_READER_DSN) as conn:
        conn.read_only = True
        with conn.cursor() as cur:
            cur.execute(
                "SELECT count(*), sum(total) FROM reporting.order_summary "
                "WHERE region = %s AND order_date >= %s::date "
                "AND order_date < (%s::date + interval '1 month')",
                (region, month_start, month_start))
            return cur.fetchone()
```

**Model-written queries with review.** For open-ended questions, the model drafts a SELECT statement. Before running it, your code checks that it is a single read statement, shows it in the logs, and executes it only through the `ai_reader` connection. The check is a convenience; the role is the real protection.

## How do you stop it from changing data it shouldn't?

Make change impossible at the database level for the account the AI uses. If the role holds only SELECT on a view, an UPDATE or DELETE fails no matter what the model writes or what an injected message tells it to do. The read-only session setting adds a second barrier, but treat it as a safety net rather than the lock, since a session can switch that setting back off; missing privileges cannot be switched on from inside the session.

When the assistant does need to write, for example to create a support ticket, give that job a separate account and a separate tool that can insert into one table only. Prefer a narrow function with fixed inputs over free-form SQL. For anything with real impact, such as refunds or deleting records, follow OWASP's advice to require a person's approval before the action runs, and enforce that approval in your own system rather than asking the model to respect it.

Finally, keep a record. OWASP notes that logging and rate limits do not prevent misuse, but they show you quickly when something unexpected is happening. Log every query the AI runs, with the user who asked and the time, and review the log after the first week.

## Sources

- LLM06:2025 Excessive Agency: https://genai.owasp.org/llmrisk/llm062025-excessive-agency/
- PostgreSQL 18 Documentation: 5.8. Privileges: https://www.postgresql.org/docs/18/ddl-priv.html
- SQL Injection Prevention Cheat Sheet: https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html
