Build with AI

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

· By Robert Scales · 4 min read

Short 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:

CauseDatabase exampleFix
Too much functionalityThe tool can run any SQL, not just look things upOffer only the lookups the task needs
Too many permissionsThe account can also insert, update and deleteA read-only account limited to specific tables
Too much autonomyResults or changes go out with nobody checkingA 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.

-- 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.

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

  1. LLM06:2025 Excessive Agency — OWASP Gen AI Security Project, May 5, 2025
  2. PostgreSQL 18 Documentation: 5.8. Privileges — The PostgreSQL Global Development Group, June 8, 2026
  3. SQL Injection Prevention Cheat Sheet — OWASP Cheat Sheet Series, July 7, 2026

Get the free Guide to Artificial Intelligence

Sign up for the weekly newsletter and receive the free guide: what changed in AI this week, one lesson, one prompt to try. Unsubscribe in one click.

How we handle your address

Back to top

Display

Your choices are saved in this browser only. No data is sent.

Text size
100%
Colours
Reading
More settings
Accessibility statement