orta 60 puan

Output Handling ve Plugin Tasarımı

OWASP LLM02 + LLM07 derinlemesine: çıktıyı asla eval'e koyma, plugin scope, parametre validation, schema enforcement.

Çıktı İşleme (Output Handling) ve Eklenti (Plugin) Tasarımı

Bu eğitim yolunun ilk modülünde mimari iskeleti, ikinci modülünde Tehdit Modellemesi (Threat Modeling) çerçevesini inceledik. Şimdi sıra somut üretim ve kodlama desenlerinde (Production Patterns).

Bu odada, siber güvenlikte genellikle el ele yürüyen iki kritik OWASP maddesini birlikte ele alıyoruz: LLM02 (Insecure Output Handling - Güvensiz Çıktı İşleme) ve LLM07 (Insecure Plugin Design - Güvensiz Eklenti Tasarımı).

Klasik web zafiyetlerinin (RCE, XSS, SQLi vb.) yapay zeka çağına sıçradığı asıl kapı tam olarak burasıdır.


LLM02 — Insecure Output Handling (Güvensiz Çıktı İşleme)

Tek Cümleyle Özeti: Modelin ürettiği metne/koda doğrudan güvenmek ve onu sistemde filtresiz kullanmaktır.

Pek çok LLM uygulamasında yazılımcılar, modelden dönen yanıtı hiçbir güvenlik süzgecinden (Sanitization) geçirmeden doğrudan sisteme verir. Bu durum, klasik web güvenliğinde kullanıcı girdisini (input) temizlemeden doğrudan SQL veritabanına yollamakla eşdeğer bir ölümcül hatadır.

❌ Anti-Pattern (Hatalı Kodlama): Model Çıktısı Doğrudan Dünyaya Gider

python
# ❌ Anti-pattern: LLM02 Zafiyetleri

# Örnek 1: XSS Zafiyeti
response = llm.chat("Bana bu sayfanın özetini ver")
return render_template("sonuc.html", icerik=response)  # Zafiyet! Gelen HTML doğrudan basılıyor.

# Örnek 2: RCE (Uzaktan Kod Çalıştırma) Zafiyeti
answer = llm.chat(user_question)
exec(answer)  # Zafiyet! Modelin ürettiği rastgele Python kodu çalıştırılıyor.

# Örnek 3: SQL Injection Zafiyeti
sql_query = llm.chat(f"Bu doğal dil sorgusunu SQL'e çevir: {user_text}")
cursor.execute(sql_query)  # Zafiyet! Uydurma veya zararlı SQL doğrudan çalıştırılıyor.

Hepsindeki temel hata aynıdır: Model, güvenilmez bir kullanıcı gibi ele alınmalıdır. Çıktısı normalize edilip denetlenmeden (Sanitize) hiçbir kritik alana (Veritabanı, Konsol, Arayüz) gönderilemez.

✅ Doğru Pattern: Çıktı = "Güvenilmeyen Girdi (Untrusted Input)"

python
# ✓ Doğru: Çıktıyı kullanmadan önce çok katmanlı denetimden geçir
response = llm.chat(user_question)

# 1. Şema ve Format Doğrulama (Schema Validation)
parsed = pydantic_model.parse_raw(response)  # Format hatalıysa işlemi direkt reddeder (Exception).

# 2. Beyaz Liste (Allow-list) URL Kontrolü
for url in extract_urls(parsed.content):
    if not is_allowed_domain(url):
        raise PolicyViolation(f"Güvenlik İhlali: İzin verilmeyen dış URL tespit edildi: {url}")

# 3. HTML Render edilecekse Temizleme (Sanitize / Escape)
html_safe = bleach.clean(parsed.content) # Zararlı script etiketlerini siler.

# 4. Kritik bir eylem (Aksiyon) tetiklenecekse → İnsan Onayı (HITL)
if parsed.action == "send_email":
    validate_email_params(parsed.params)      # Şemayı tekrar kontrol et
    queue_for_human_approval(parsed)          # Göndermeden önce yönetici onayına sun

Güvenli Geliştirme İçin Pratik Kurallar:

  • Asla eval(), exec() veya Function() kullanmayın: Eğer modelin ürettiği kodu çalıştırmak zorundaysanız, bunu ana sistemde değil, geçici ve yalıtılmış bir Sandbox ortamında (Örn: Docker container, WebAssembly - Wasm, izole VM) yapın.
  • HTML Çıktısı (Render): bleach veya DOMPurify gibi güvenilir araçlarla "Beyaz Liste" (Whitelist) tabanlı temizlik yapın.
  • Veritabanı (SQL): Model çıktısını asla ham (raw) SQL sorgusu olarak çalıştırmayın. Parametreli sorgular (Parameterized Queries) veya güvenli ORM (Object-Relational Mapping) katmanları kullanın.
  • Sistem Komutları (Shell): Asla os.system(model_ciktisi) şeklinde komut çalıştırmayın. Argümanları ayrı bir liste olarak verin ve shell=False parametresini kullanın.
  • Bağlantılar (URL): Dış dünyaya giden URL'ler için sıkı bir beyaz liste (Allow-list) oluşturun. Bilinmeyen veya şüpheli URL'leri otomatik reddedin.

LLM07 — Insecure Plugin Design (Güvensiz Eklenti Tasarımı)

Modern ajanlar (Agents) verilen görevi yapmak için dış araçlar (Plugin/Tool) çağırırlar. Bu eklentilerin tasarım kalitesi ve güvenlik sınırları, aslında ajanın tüm güvenlik kalitesini belirler.

❌ Anti-Pattern: Açık ve Sınırsız Araçlar

python
# ❌ Anti-pattern: LLM07 Zafiyetli Eklentiler

def execute_sql(query: str) -> dict:
    """Sistemdeki herhangi bir SQL sorgusunu yetkisiz çalıştırır."""
    return db.execute(query)

def run_shell(cmd: str) -> str:
    """Sunucuda her türlü sistem komutunu (bash) çalıştırır."""
    return subprocess.check_output(cmd, shell=True)

def send_email(to: str, body: str) -> bool:
    """Domain ayrımı yapmaksızın herhangi bir alıcıya mail gönderir."""
    smtp.sendmail(SENDER, to, body)

Bu şekilde tasarlanmış eklentiler, modele altın tepside sunulmuş silahlardır. Başarılı bir Prompt Injection (İstem Enjeksiyonu) saldırısında, hacker bu silahların tetiğini çeker.

✅ Doğru Pattern: Dar Kapsam ve Sıkı Doğrulama

python
# ✓ Doğru: Dar Kapsam + Parametre Doğrulama + Rol Bazlı Erişim (RBAC)

@validate_params(
    order_id=int,
    customer_id=int  # DİKKAT: Bu parametre modelden gelmez, güvenilir oturumdan (Session) alınır.
)
@require_role("support_agent")
def get_order_status(order_id: int, customer_id: int) -> dict:
    """Sadece sisteme giriş yapmış müşterinin kendi siparişinin durumunu döner."""
    order = db.orders.find(id=order_id, customer_id=customer_id)
    if not order:
        raise NotFound()
    return order.public_view()  # Şirket içi özel alanları (cost vb.) gizleyerek döner.

@validate_params(
    to=lambda x: x.endswith("@sirket.com"),  # Sadece kurum içi (internal) maillere izin ver.
    subject=str,
    body=str
)
@require_role("any_user")
@require_approval(category="email")  # HITL (Döngüde İnsan Onayı) şartı.
def send_internal_email(to: str, subject: str, body: str) -> bool:
    ...

Güvenli Eklenti (Plugin) Tasarım Kuralları

  1. Tek Görev, Tek Yetki: Bir eklenti sadece tek bir iş yapar. "Hem okusun hem yazsın" diyorsanız mimari hata yapıyorsunuz; ikisi için ayrı eklentiler yazın.
  2. Sıkı Parametre Tipleri: Pydantic, TypeScript Interfaces veya JSON Schema ile tip zorlaması yapın. Modelin ürettiği metin verisi otomatik olarak doğru kabul edilmez; veri tipine (Int, Boolean vb.) uygunluğu denetlenir.
  3. Yetkilendirme Bağlamı (Auth Context): Eklenti; modelin emrettiği yetkiyle değil, o isteği başlatan kullanıcının kendi gerçek rolüyle (RBAC) çalışmalıdır.
  4. Salt Okunur (Read-Only) vs. Yazma (Write): "Müşteri verisini görüntüle" fonksiyonu ile "Müşteri verisini güncelle" fonksiyonları kesinlikle ayrı araçlar olmalıdır. Saldırgan okuma aracını ele geçirse bile yazma/silme yapamaz.
  5. Beyaz Liste (Whitelist): Orkestratör motoru, yalnızca önceden güvenlik onayı almış eklentileri çalıştırabilmelidir. Dinamik eklenti yüklemeleri risklidir.
  6. Denetim İzi (Audit Log): Çalışan her eklenti; gönderilen parametreler, işlemi başlatan kullanıcı ve dönen sonuç ile birlikte merkezi bir SIEM sistemine loglanmalıdır.

Neden LLM02 ve LLM07 Sahada Birlikte Vurur?

Saldırganlar genellikle bu iki zafiyeti birleştirerek "Exploit Chain (Zafiyet Zinciri)" kurarlar. Klasik senaryo şöyledir:

  1. Saldırgan bir Prompt Injection ile modeli kandırır (LLM01).
  2. Model, saldırganın isteği üzerine "Şu shell komutunu çalıştır" şeklinde bir çıktı (JSON/Metin) üretir.
  3. Sistemde tanımlı run_shell eklentisinin tasarımı gereğinden fazla yetkiye sahiptir (LLM07).
  4. Uygulama, modelden gelen bu çıktıyı hiçbir şema veya güvenlik doğrulamasından (Sanitization) geçirmeden doğrudan sisteme verir (LLM02).
  5. Sonuç: Felaket (Sunucuda Uzaktan Kod Çalıştırma - RCE).

Gördüğünüz gibi bu beş halkalı bir felaket zinciridir. Defense-in-Depth (Derinlemesine Savunma) felsefesinde amaç; halkalardan en az birini kırarak saldırıyı ortasında durdurmaktır.


İki Yönlü Şema Doğrulaması (Schema Validation)

Sistem mimarlarının sıklıkla gözden kaçırdığı kritik bir detay vardır: Şema doğrulaması tek yönlü değil, iki yönlü (Hem girdi hem çıktı için) çalışmalıdır.

  • Modele Giden Veri (Input Validation): Kullanıcı prompt'u, veritabanından çekilen RAG belgesi veya diğer eklentilerden dönen sonuçlar... Modelin önüne konacak her veri önce normalize edilmeli ve beklenen şemaya uyup uymadığı denetlenmelidir.
  • Modelden Çıkan Veri (Output Validation):
    • Modelin ürettiği JSON, beklenen sözleşmeye (kontrat) uyuyor mu?
    • Üretilen URL'ler güvenlik beyaz listesinde var mı?
    • Araç (Tool) çağırmak için üretilen parametrelerin veri tipi (String, Integer) ve formatı doğru mu?

Endüstri Standardı Doğrulama Kütüphaneleri:

Dil / PlatformKullanılan Araç
PythonPydantic + instructor paketi
TypeScriptZod + zod-to-json-schema
Genel SistemlerJSON Schema + Structured Outputs (Yapılandırılmış Çıktılar)
OpenAIAPI seviyesinde response_format={"type":"json_schema"} kullanımı
AnthropicTool Use şema dayatmaları (Enforcement)

Mini Vaka: Halüsinasyonlu Araç Çağrısı

Model bir gün geçici bir karışıklık yaşayıp (Halüsinasyon) sisteme şu çıktıyı gönderir:

json
{
  "tool": "send_payment",
  "params": {
    "to_account": "TR12-0001-...",
    "amount": 50000,
    "currency": "TRY",
    "reason": "Test İşlemi veya İade"
  }
}

Eğer sisteminizde sıkı Şema Doğrulama (Schema Validation) kuralları varsa şu denetimler otomatik yapılır:

  1. to_account → Bu IBAN bizim kurum içi güvenli hesaplar beyaz listemizde mi? (Değilse iptal et).
  2. amount → 50.000 TL, bu kullanıcının günlük işlem limitinin (Rate-limit) içinde mi?
  3. reason → Açıklama kısmı boş mu veya güvensiz bir kelime içeriyor mu?
  4. HITL (Döngüde İnsan): 5.000 TL üzerindeki işlemler için insan onayı (Yönetici) kuralı var mı?

Bu kuralların her biri aslında "şema doğrulamasının birer güvenlik yan etkisidir". Böylece hem kötü niyetli bir saldırı hem modelin anlık saçmalaması (Halüsinasyon) hem de masum bir kullanıcı hatası aynı anda engellenmiş olur.


Bölüm Özeti

  • LLM02 (Güvensiz Çıktı İşleme): Modelin ürettiği çıktıya asla güvenilmez. O bir "Güvenilmeyen Girdi" (Untrusted Input) olarak kabul edilir ve filtrelenmeden Arayüze, SQL'e veya Sistem Shell'ine gönderilmez.
  • LLM07 (Güvensiz Eklenti Tasarımı): Eklentiler (Plugin/Tool) tasarlanırken Dar Kapsam, Sıkı Şema, Rol Bazlı Erişim (RBAC) ve Loglama (Audit) prensiplerine uyulmalıdır.
  • Zafiyet Zinciri: Bu iki zafiyet sahada her zaman birlikte çalışır. Bu zinciri kırmanın yolu Derinlemesine Savunma (Defense-in-Depth) kurgulamaktır.
  • Şema Doğrulaması (Schema Validation): Hem modele giden veride hem de modelden çıkan veride iki yönlü olarak katı şekilde uygulanmalıdır.

Sıradaki Oda: Bu eğitim yolunun son odasına, Ajan (Agent) Güvenliği ve Yetki Kısıtlamaya geçiyoruz. OWASP LLM08 (Aşırı Yetkilendirme) maddesini kapsamlı bir panelde inceleyecek ve meşhur Microsoft Copilot vakasını bir siber güvenlik mimarının gözünden yeniden analiz edeceğiz.

Görevler

Görevleri çözmek ve puan kazanmak için giriş yap ya da kayıt ol.
  1. 01
    LLM çıktısının uygulama tarafında doğrudan eval/exec/HTML render/SQL'e gitmesi hangi OWASP maddesidir?
    15 P
  2. 02
    Bir plugin/tool'a gelen parametrelerin tip, aralık, format ve şema açısından denetlenmesi pratiğine ne ad verilir? (iki kelime, İngilizce)
    15 P
  3. 03
    Plugin tasarımında 'Least Privilege' (en az ayrıcalık) prensibi pratikte ne demektir?
    15 P
  4. 04
    OWASP LLM02 (Insecure Output Handling) klasik web zafiyetleriyle nasıl bağlantılıdır?
    15 P