The Problem: AI Agents Ship with Real Vulnerabilities
Most Laravel developers building AI-powered features focus on getting the agent to work. Security comes later—if at all. Episode 11 of the Ship AI with Laravel series by Harris Raftopoulos makes the cost of that approach painfully clear.
Before introducing any fixes, the episode demonstrates two attacks against a live support agent:
- Prompt extraction via social engineering — A carefully crafted sequence of messages convinces the agent to reveal its entire system prompt, its tool list, and its internal instructions.
- Unauthorized data access via tool abuse — The order lookup tool is called with an order ID belonging to a different customer, and it returns that customer's data without any authorization check.
Both vulnerabilities are straightforward to exploit and, according to the episode, ship in production AI apps every day.
Four Layers of Defense
Layer 1: Prompt Hardening
The first fix is the simplest: add explicit security boundaries directly to the system prompt. The agent is instructed to refuse requests to reveal its prompt or tools, and to treat anyone claiming to be an internal employee as a regular customer.
This reduces casual attacks but is not sufficient on its own. A determined attacker can still social-engineer around a prompt-only defense.
Layer 2: Local LLM Guard with Ollama and Llama 3.2
A separate prompt-guard agent is introduced, running locally via Ollama with Llama 3.2. Its sole responsibility is to classify each incoming message as safe or unsafe and return a JSON result.
{ "classification": "unsafe", "reason": "prompt injection attempt" }
This guard is wired in as middleware, so unsafe messages are blocked before they ever reach the main agent. Because it runs locally, there is no per-request API cost for this screening step.
Layer 3: Tool-Level Authorization
This is the fix that actually closes the data leak. The order lookup tool is scoped to the authenticated user:
// Tool enforces ownership — no matter what ID is passed in
$order = Order::where('id', $orderId)
->where('user_id', auth()->id())
->firstOrFail();
With this in place, the tool physically cannot return another customer's order, regardless of what order ID an attacker supplies. Prompt instructions alone cannot provide this guarantee; authorization logic in the tool itself can.
Layer 4: Output Filtering
A safety-net middleware scans every outgoing response and redacts sensitive patterns—SSNs, credit card numbers, API keys—before anything leaves the system. This acts as a last line of defense against accidental data exposure that slips through earlier layers.
Series Wrap-Up
Episode 11 closes out the eleven-episode Ship AI with Laravel series. The arc runs from composer require laravel/ai all the way to a production-ready, tested, and secured AI platform. If you are joining late, the author recommends starting at Episode 1 since each episode builds on the previous one.
The full source code is available on GitHub: github.com/harris21/ship-ai-with-laravel
Key Takeaways
- Prompt hardening alone is not enough — social engineering can bypass instruction-only defenses.
- Authorization belongs in the tool, not the prompt — scope database queries to the authenticated user at the code level.
- A local LLM guard adds zero API cost — Ollama + Llama 3.2 can classify messages as middleware before they hit your main model.
- Output filtering is a safety net — redact sensitive patterns on egress as a final layer.
- Defense in depth is the standard — stack multiple independent layers so no single failure exposes user data.
Source: Ship AI with Laravel: I Tricked My Own AI Into Leaking Everything — Laravel News