LLM07
OWASP LLM Top 10 MODEL

Insecure Plugin Design

Description

LLM plugins and extensions accept untrusted inputs or lack proper authorization. Insecure plugins enable unauthorized access, data exfiltration, or remote code execution.

Risk

Plugins extend LLM functionality but often lack security hardening. They may accept unvalidated LLM outputs as trusted inputs, fail to implement authorization checks, or expose dangerous capabilities without safeguards. Compromised plugins can bypass core LLM security controls and directly access sensitive systems or data.

Attack Scenarios

An email plugin for an LLM assistant accepts recipient addresses directly from model outputs without validation, allowing prompt injection to send phishing emails to arbitrary recipients.

A code execution plugin runs LLM-generated code without sandboxing or permission checks, enabling attackers to execute arbitrary commands on the host system via prompt manipulation.

Mitigations

Input Validation

Treat all inputs to plugins as untrusted, including those from the LLM. Validate, sanitize, and enforce strict schemas for all parameters.

Least Privilege

Grant plugins minimal required permissions. Use separate service accounts with restricted access to resources.

Sandboxing

Execute plugins in isolated environments with limited network, filesystem, and system access. Use containers or VMs for strong isolation.

User Confirmation

Require explicit user approval for high-risk plugin actions such as sending emails, executing code, or accessing sensitive data.

Code Examples

# Bad: Trusting LLM output directly
class EmailPlugin:
    def send_email(self, llm_output: str):
        to = llm_output  # No validation
        send_mail(to, subject, body)

# Good: Validating plugin inputs
from typing import Optional
import re

class SecureEmailPlugin:
    ALLOWED_DOMAINS = ['example.com', 'company.com']
    
    def send_email(self, recipient: str, subject: str, body: str, 
                   user_id: str) -> Optional[str]:
        # Validate email format
        if not re.match(r'^[^@]+@[^@]+\.[^@]+$', recipient):
            raise ValueError("Invalid email format")
        
        # Domain allowlist
        domain = recipient.split('@')[1]
        if domain not in self.ALLOWED_DOMAINS:
            raise ValueError(f"Domain {domain} not allowed")
        
        # Require user confirmation
        if not self.user_approved(user_id, recipient):
            return "Pending user approval"
        
        send_mail(recipient, subject, body)
        return "Email sent"

Evidence Requirements

  • Plugin security design reviews and threat models
  • Input validation and schema enforcement in plugin code
  • Sandboxing and permission configurations
  • User confirmation workflows for high-risk actions
  • Plugin authorization audit logs