Tartışma

GraphQL "Blind" Injection: Introspection Kapalıyken Veri Sızdırma Teknikleri

Başlatan İMRAN · 06 Oca 2026 20:13 · 6 Görüntülenme · 0 Yanıtlar
Konuyu Açan #0

1) “Blind” GraphQL sızdırma ne demek?


Introspection kapalıyken saldırgan şemayı doğrudan listeleyemez; ama yine de şu yollarla ne var ne yok anlayıp veri çekebilir:


  • Meşru istemci trafiğinden şema çıkarma: Web/Mobile uygulamanın attığı sorgular zaten alan adlarını/type’ları ifşa eder.
  • Hata mesajı/validasyon farklarından çıkarım: “Böyle bir alan yok” / “Bu argüman beklenmiyor” gibi detaylı hatalar, alan isimlerini tahmin etmeyi kolaylaştırır.
  • Persisted queries / operation registry: Prod’da allowlist varsa bile, registry yanlış yönetilirse “fazla geniş” operasyonlar veri sızdırır.
  • Asıl kritik nokta: Broken access control
    GraphQL’de sızdırmaların büyük kısmı field-level / object-level authorization eksikliğidir:

    • “Ben görebiliyorum” varsayımıyla resolver’da kontrol yoksa,
    • Bir alan (ör. email, roles, billing) sadece UI’da gizlenmiş ama API’da serbestse,
      introspection kapalı olsa bile veri çıkar.

Özet: Introspection kapatmak “keşfi” zorlaştırır; ama yetkilendirme ve veri minimizasyonu zayıfsa sızıntıyı engellemez.




2) Uygulamada (yasal) nasıl test edilir?


Bu bölüm “kendi sistemin / izinli pentest / staging” için savunmacı test planı.


A) Şema keşfi yerine “meşru kaynaklardan envanter çıkar”


  • Frontend repo’da *.graphql, Apollo/Relay query’leri, codegen çıktıları
  • Mobil uygulama network log’ları (izinli cihazınızda)
  • API gateway log’larında görünen operationName’ler
    Amaç: “Zaten uygulamanın kullandığı alanlar” listesini çıkarıp risk analizi yapmak.

B) “Kör sızıntı” açısından en riskli 6 kontrol


  1. Object-level auth (BOLA): Bir objeyi isteme hakkı var mı? (ör. başka kullanıcı profili)
  2. Field-level auth: Objeyi görse bile her alanı görebilir mi? (email, telefon, invoice, token vb.)
  3. Tenant izolasyonu: org_id / tenant_id her resolver’da enforced mı?
  4. Error mesajı sızıntısı: Prod’da detaylı şema/stack trace dönüyor mu?
  5. Query complexity / depth: Aşırı nested sorgularla veri “toplu” çekilebiliyor mu?
  6. Batching / alias abuse: Tek istekle çok sayıda entity çekilebiliyor mu? (Limit/Rate yoksa)

C) Negatif test yaklaşımı (en güvenlisi)


“Payload” değil, rol ve yetki senaryosu test edilir:


  • Rol A kullanıcı → Rol B alanlarına erişebiliyor mu?
  • Kendi tenant’ı dışındaki objeleri görebiliyor mu?
  • UI’da görünmeyen alanlar API’dan geliyor mu?
  • Aynı endpoint farklı “operation” ile fazla veri döndürüyor mu?



3) Introspection kapalıyken sızıntıyı büyüten yaygın hatalar


  • Tek endpoint, tek auth kontrol: “Giriş yaptıysa her şeye erişir” tarzı.
  • Resolver’da kontrol yerine UI’da gizleme: “Buton yok ki erişemesin” yanılgısı.
  • Aşırı geniş search/filter argümanları: Sunucu tarafı validation yok.
  • Union/interface alanlarında “default resolver” ile beklenmedik alanlar açılması
  • Debug mod / tracing açık (Apollo tracing, stack trace, error extensions)
  • Persisted query allowlist var ama operasyonlar fazla kapsamlı (tek operasyon her şeyi getiriyor)



4) Koruma: “Next-Gen” GraphQL hardening paketi


Aşağıdaki önlemler birlikte uygulanınca “blind sızıntı” sınıfı ciddi düşer.


4.1 Yetkilendirme: her resolver’da zorunlu


  • Object-level: Bu objeye erişim hakkı var mı?
  • Field-level: Bu alanı görme hakkı var mı?
  • Default deny: Belirsiz durumda kapat.
Kural: “GraphQL auth middleware var” yetmez; resolver içi kontrol şart.

4.2 Veri minimizasyonu ve şema hijyeni


  • Hassas alanları (email/phone/billing) ayrı type/field yap ve role bağlı aç.
  • Admin alanları ayrı endpoint veya ayrı schema (mümkünse).
  • Public type’larda gereksiz ilişki zincirlerini kısalt.

4.3 Hata ve gözlemlenebilirlik


  • Prod’da detaylı hata mesajı kapalı, stack trace yok.
  • extensions içinde şema ipuçları dönmesin.
  • X-Request-ID + operationName + kullanıcı/tenant + complexity logla.

4.4 Query kontrolü (DoS + toplu sızıntı önleme)


  • Depth limit (örn. max depth)
  • Query complexity limit
  • Rate limit (user/IP)
  • Timeout / circuit breaker
  • Batching kontrolü (gerek yoksa kapat)

Aşağıda savunma amaçlı örnek (Node/Apollo mantığıyla) – saldırı değil, limit koyma:



CODE
12345678910111213141516171819// Savunma odaklı: depth + complexity + hata sanitizasyonu (örnek yaklaşım)
import depthLimit from "graphql-depth-limit";
import { createComplexityLimitRule } from "graphql-validation-complexity";

const validationRules = [
  depthLimit(8),
  createComplexityLimitRule(1000, {
    onCost: cost => console.log("GraphQL cost:", cost),
    formatErrorMessage: cost => `Query too complex: ${cost}`,
  }),
];

const formatError = (err) => {
  // prod: detay sızdırma
  return { message: "Request failed.", code: "GRAPHQL_ERROR" };
};

// Apollo Server gibi bir yerde:
// new ApolloServer({ schema, validationRules, formatError })

Yanıt vermek için giriş yapmış olmalısınız.

0 alıntı seçildi