The moment an LLM is allowed to generate SQL that touches a real database, the classic SQL injection risk gets a new twist: instead of a malicious user crafting an injection string, a manipulated prompt could trick the model into generating a query that does something the application never intended (deleting rows, reading tables outside its scope). The defense is the same time-tested one relational databases have always relied on — parameterized queries — combined with restricting exactly what the connected role is allowed to do at the database level.
Step-by-Step Implementation
1. Establish Parameterized Forms: Construct database query strings using standard parameters (? or %s).
2. Set User Privileges: Restrict the database connector role to prevent table drops or modifications.
3. Apply Constraint Parsers: Run validation rules to check values before executing commands.
# Parameterized SQLite query engine preventing SQL injection
import sqlite3
def secure_database_query(user_supplied_name: str):
# Parameterized connection to SQL database
conn = sqlite3.connect("enterprise_records.db")
cursor = conn.cursor()
# Use safe placeholders instead of string concatenation
query = "SELECT employee_id, role, salary FROM workforce WHERE name = ?"
cursor.execute(query, (user_supplied_name,))
records = cursor.fetchall()
conn.close()
return records
| Query Execution | Security Level | Flexibility Profile |
|---|---|---|
| Raw Query Interpolation | Low (Prone to SQL Injection) | High (Allows dynamic string assembly) |
| Parameterized Queries | 100% Secure against standard SQL Injection | Moderate (Values are bound to static slots) |
An LLM-in-the-loop doesn't change the underlying defense — it changes what "user input" means. The untrusted input is no longer just a form field; it's the model's generated SQL itself, which should never be executed by directly concatenating it into a raw query string. Route the model's intent (table, filters, values) into a parameterized query your own code constructs, and additionally restrict the database role the connection uses to read-only or narrowly-scoped access — two independent layers, so a failure in one doesn't expose the whole database.
Practical Challenge
Write an SQLite script that takes a user-provided email and updates a profile field securely using parameterized syntax.
Concept Check
Sources & Further Reading
- OWASP: SQL Injection — the standard reference on SQL injection mechanics and parameterized-query defenses this article builds on.
- Python sqlite3 Documentation: SQL placeholders — official reference for the parameterized query syntax used in the code sample.
AI