Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 54

GraphQL Güvenlik Zafiyetleri: Keşif, Derinlik ve Veri İfşası

Recommended resources

Merhaba! Önceki dersimizde Express.js, Django ve Flask gibi framework'lerde middleware zincirlerinin ve sıralamasının güvenlik üzerindeki kritik etkisini inceledik. Bir isteğin yaşam döngüsünü kontrol eden bu ara katmanların, uygulamanın savunma hattının temelini nasıl oluşturduğunu gördük.

Bu dersimizde ise API dünyasında farklı bir felsefeyi benimseyen GraphQL'e odaklanacağız. REST API'lerin aksine, GraphQL'de istemci, sunucudan tam olarak hangi veriyi hangi yapıda istediğini belirten bir sorgu gönderir. Bu esneklik, geliştirme süreçlerini hızlandırsa da güvenlik denetimi perspektifinden yeni ve karmaşık zorluklar ortaya çıkarır. Bu dersin amacı, GraphQL API'lerinde introspection kontrolü, derinlik/maliyet limiti eksiklikleri ve aşırı veri ifşası zafiyetlerini analiz etmektir. Kontrolün sunucudan istemciye geçtiği bu modelde, kötü niyetli bir istemcinin bu gücü nasıl istismar edebileceğini ve kod seviyesinde buna karşı hangi önlemleri arayacağımızı öğreneceğiz.

GraphQL: Esnekliğin İki Ucu Keskin Kılıcı

GraphQL'in neden var olduğunu anlamak, güvenlik risklerini kavramanın ilk adımıdır. REST API'lerde sıkça karşılaşılan "over-fetching" (ihtiyaçtan fazla veri alınması) ve "under-fetching" (ihtiyaç duyulan veri için birden fazla istek yapılması) sorunlarına bir çözüm olarak doğmuştur.

Mehmet İnce'nin "GraphQL Hacking diye bir şey aslında YOK" başlıklı videosu, bu teknolojinin arkasındaki temel motivasyonu ve evrimsel süreci harika bir şekilde özetliyor.

Web Security 0x24 | GraphQL Hacking diye bir şey aslında YOK. Kan-ter-gözyaşı var :)

Bu video, GraphQL'in hangi pratik ihtiyaçlardan doğduğunu, REST'ten temel farklarını ve neden "GraphQL Hacking" diye ayrı bir disiplin olmadığını, bunun aslında API güvenliğinin bir uzantısı olduğunu savunuyor. Bu bakış açısı, konuya temelden yaklaşmamız için değerli bir çerçeve sunuyor.

Lütfen videonun başından itibaren ilk altı dakikalık kısmını izleyerek GraphQL'in farklı istemci (mobil, web) ihtiyaçlarını karşılama ve geliştirme süreçlerindeki blokajları kaldırma hedefini dinleyin. Özellikle, verinin şekline sunucunun değil, istemcinin karar vermesi felsefesinin altı çiziliyor.

İzlediğiniz gibi, GraphQL'in temel vaadi, istemcilere tam olarak ihtiyaç duydukları veriyi, tek bir istekte alabilme gücü vermektir. Ancak bu güç, üç temel güvenlik riski kategorisini de beraberinde getirir: şemanın ifşası, kaynakların tüketilmesi ve hassas verilerin sızdırılması. Şimdi bu riskleri kod denetimi perspektifinden tek tek ele alalım.


1. Introspection: API'nin İç Haritasını Çıkarmak

Bir istemcinin neyi sorgulayabileceğini bilmesi için, API'nin şemasını (schema) — yani mevcut tipleri (types), sorguları (queries), mutasyonları (mutations) ve alanları (fields) — keşfedebilmesi gerekir. GraphQL, bu keşfi "introspection" adı verilen yerleşik bir mekanizma ile sağlar. Geliştirme ortamında son derece faydalı olan bu özellik, üretim ortamında açık bırakıldığında bir saldırgan için altın madeni değerindedir.

Saldırgan, standart bir introspection sorgusu göndererek uygulamanızın tüm veri yapısını, normalde gizli kalması gereken tipler ve alanlar da dahil olmak üzere öğrenebilir. Bu, saldırı yüzeyinin tam bir haritasını çıkarmak anlamına gelir.

Bir GraphQL API'sine gönderilen introspection sorgusunun yanıtı. Bu yanıt, `UserRole`, `BlogCategory`, `User` gibi tiplerin varlığını ve `email` gibi potansiyel olarak hassas alanları ifşa ederek API'nin tüm şemasını ortaya döküyor.

Bu mekanizmanın bir saldırgan tarafından nasıl kullanıldığını ve adım adım tüm şemanın nasıl deşifre edilebileceğini anlamak için Mehmet İnce'nin videosunun ilgili bölümüne göz atalım.

Web Security 0x24 | GraphQL Hacking diye bir şey aslında YOK. Kan-ter-gözyaşı var :)

Bu bölümde, bir istemcinin API'nin yeteneklerini nasıl öğrendiği ve introspection sorgularının bu süreçteki rolü pratik olarak gösteriliyor.

Lütfen bu bölümü izleyin. Konuşmacının, "istemci senin neler verebildiğini, şemalarını, bunun fiil tiplerini nereden bilecek?" sorusuna cevap olarak introspection'ı nasıl konumlandırdığına ve bir dizi sorguyla tüm şemanın nasıl ortaya çıkarılabileceğine dikkat edin.

SİTEY Blog'daki makale ve OWASP ASVS standardı da bu riski vurgular. Üretim ortamında introspection'ın devre dışı bırakılması temel bir güvenlik kontrolüdür.

API Sızma Testi: Kimlikli Akışlar ve İş Mantığı | SİTEY Blog

Bu kaynak, farklı API protokollerinin kendilerine özgü güvenlik risklerini karşılaştırıyor. GraphQL ile ilgili bölüm, bu derste ele aldığımız zafiyetleri net bir şekilde özetliyor.

Makalede "REST vs GraphQL vs gRPC Güvenlik" başlığı altındaki "GraphQL Güvenlik Riskleri" alt başlığını okuyun. İlk madde olarak "Introspection sorguları ile tüm şemanın keşfedilmesi; üretimde devre dışı bırakılmalıdır" uyarısını göreceksiniz.

Kod Denetiminde Neye Bakmalı?

Güvenli kod denetiminde göreviniz, GraphQL sunucu yapılandırmasında introspection'ın üretim ortamı için kapatıldığını doğrulamaktır. Bu, kullandığınız framework'e göre değişiklik gösterir:

  • Node.js (Apollo Server): ApolloServer kurucusuna introspection: process.env.NODE_ENV !== 'production' gibi bir konfigürasyon parametresi geçilip geçilmediğini kontrol edin.
  • Java (graphql-java): Genellikle varsayılan olarak açıktır. GraphQL nesnesini oluştururken introspection sorgularını engelleyen özel bir instrumentation katmanı eklenip eklenmediğine bakın.
  • Diğer Diller: Benzer şekilde, "introspection disable production" anahtar kelimeleriyle framework belgelerinde ilgili ayarı arayın. Eğer böyle bir ayar yoksa, API Gateway seviyesinde veya bir middleware katmanında introspection sorgularını içeren (__schema, __type alanları) isteklerin engellenip engellenmediğini kontrol edin.

2. Kaynak Tüketimi: Derinlik, Karmaşıklık ve Maliyet

GraphQL'in en tehlikeli özelliklerinden biri, istemcinin tek bir sorgu içinde iç içe geçmiş ilişkileri talep edebilmesidir. Bu, sunucuda beklenmedik bir kaynak tüketimine ve sonuç olarak hizmet reddi (Denial of Service - DoS) zafiyetine yol açabilir.

Bir saldırgan, kasıtlı olarak çok derin veya karmaşık bir sorgu oluşturarak veritabanını, CPU'yu veya belleği kilitleyebilir.

Bir kullanıcının arkadaşlarının, o arkadaşlarının da arkadaşlarının... adlarını talep eden derinlemesine iç içe geçmiş bir GraphQL sorgusu. Bu sorgu, basit görünmesine rağmen sunucu tarafında üstel sayıda veritabanı sorgusuna veya veri birleştirme işlemine yol açarak kaynakları tüketebilir.

Bu tür saldırılar, OWASP tarafından API4:2023 - Unrestricted Resource Consumption kategorisinde tanımlanır. OWASP ASVS standardı ise bu riske karşı uygulanması gereken somut kontrolleri belirtir.

[PDF] OWASP Uygulama Güvenliği Doğrulama Standardı 4.0

OWASP Uygulama Güvenliği Doğrulama Standardı (ASVS), GraphQL gibi modern API'ler için spesifik güvenlik gereksinimleri sunar. Bu bölüm, kaynak tüketimi saldırılarına karşı alınması gereken önlemleri net bir şekilde listeler.

PDF belgesinde V13.4 GraphQL ve Diğer Web Servisler Veri Katmanı Doğrulama Gereksinimleri başlığını bulun. Özellikle madde 13.4.1'i dikkatle okuyun. Bu madde, hizmet kesintisini önlemek için üç temel strateji önerir: sorgu beyaz listesi, derinlik/tutar limiti ve sorgu maliyet analizi.

Kod Denetiminde Neye Bakmalı?

ASVS'nin önerdiği bu üç stratejiyi kod tabanında aramalısınız:

  1. Sorgu Derinlik Limiti (Query Depth Limit): Bu en basit yaklaşımdır. Sorgunun izin verilen maksimum iç içe geçme seviyesini sınırlar. Örneğin, graphql-depth-limit gibi bir kütüphane kullanılarak "sorgu en fazla 7 seviye derin olabilir" gibi bir kural konulup konulmadığını kontrol edin.

    // Node.js'te graphql-depth-limit kullanımı örneği
    const depthLimit = require('graphql-depth-limit');
    
    app.use('/graphql', graphqlHTTP({
      schema: schema,
      validationRules: [ depthLimit(7) ] // Derinlik limiti 7 olarak ayarlanmış.
    }));
    
  2. Sorgu Karmaşıklığı/Maliyet Analizi (Query Cost Analysis): Bu, daha gelişmiş ve etkili bir yöntemdir. Şemadaki her alana bir "maliyet" atanır. Sunucu, gelen sorguyu çalıştırmadan önce toplam maliyetini hesaplar ve önceden belirlenmiş bir limiti aşıyorsa sorguyu reddeder. graphql-query-complexity gibi kütüphanelerin kullanılıp kullanılmadığını ve her alan için mantıklı maliyetlerin tanımlanıp tanımlanmadığını analiz edin.

    // graphql-query-complexity ile maliyet analizi kuralı oluşturma örneği
    const complexity = getComplexity({
        estimators: [
            simpleEstimator({ defaultComplexity: 1 }), // Her alan varsayılan 1 maliyetinde
            // 'posts(first: 10)' gibi argümanlara göre maliyeti artıran özel estimator'lar
        ]
    });
    
  3. Alias (Takma Ad) Kötüye Kullanımı: GraphQL'in alias özelliği, aynı alanın farklı argümanlarla tek bir sorguda birden çok kez istenmesine olanak tanır. Bu, istek sayısı tabanlı hız sınırlama (rate limiting) mekanizmalarını atlatmak için kullanılabilir. Kod denetiminde, maliyet analizi mekanizmasının alias kullanımını hesaba katıp katmadığını değerlendirmek önemlidir. Mehmet İnce'nin videosu bu konuyu pratik bir örnekle açıklar (<ts start="00:32:22" end="00:35:46">Alias ile Rate Limiting Atlatma</ts>).

Eğer bu kontrollerden hiçbiri mevcut değilse, API'nin kaynak tüketimi saldırılarına karşı savunmasız olduğu sonucuna varılabilir.


3. Aşırı Veri İfşası (Excessive Data Exposure)

GraphQL şemasında bir tip (örneğin, User) tanımlandığında, o tipe erişim yetkisi olan bir kullanıcı, prensipte o tipin şemada tanımlanmış tüm alanlarını talep edebilir. Geliştiriciler, passwordHash, is_admin, personal_identification_number gibi hassas bilgileri de aynı User tipi içine koyma hatasına düşebilirler. Normalde UI bu alanları istemese de, bir saldırgan özel bir sorgu hazırlayarak bu bilgilere erişebilir.

Bu zafiyet, OWASP tarafından API3:2023 - Broken Object Property Level Authorization olarak sınıflandırılır: sorun, nesneye erişim yetkisinin olması değil, o nesnenin belirli özelliklerine (property) yetkisiz erişimdir.

Web Security 0x24 | GraphQL Hacking diye bir şey aslında YOK. Kan-ter-gözyaşı var :)

Bu bölümde, konuşmacı kendi deneyiminden bir örnek vererek, basit bir sorgu alanını (password) eklemeyi unutmanın nasıl parolanın hash'lenmiş halinin ifşa olmasına yol açtığını anlatıyor. Bu, aşırı veri ifşasının ne kadar kolay gerçekleşebileceğini gösteren somut bir örnektir.

Lütfen bu bölümü izleyin. "Vatandaş unutmuş... ne istiyorsa veriyordu" ifadesi, bu zafiyetin temel nedenini özetlemektedir: varsayılan olarak "her şeyi ver" mantığı ve alan seviyesinde yetkilendirme eksikliği.

Kod Denetiminde Neye Bakmalı?

Bu, genellikle bir konfigürasyon bayrağı ile çözülemeyen, daha çok mimari ve kodlama disiplini gerektiren bir sorundur.

  1. Şema Analizi: Kod denetimine şema dosyalarını (.graphql veya kod içinde tanımlanmış tipler) inceleyerek başlayın. User, Account, Order gibi tiplerin içinde olmaması gereken hassas alanlar var mı? (password, hash, salt, secret, token, key gibi anahtar kelimeler aramak iyi bir başlangıçtır).

  2. Resolver Fonksiyonları: Zafiyetin çözümü, resolver seviyesinde yetkilendirme uygulamaktır. Hassas bir alan (örneğin email) için yazılmış resolver fonksiyonunu inceleyin. Bu fonksiyon, sadece veriyi veritabanından alıp döndürmekle mi yetiniyor, yoksa isteği yapan kullanıcının bu alanı görmeye yetkisi olup olmadığını kontrol ediyor mu?

    // GÜVENSİZ Resolver
    const resolvers = {
      User: {
        email: (parent) => parent.email, // Herkes kullanıcının e-postasını görebilir.
      }
    };
    
    // GÜVENLİ Resolver
    const resolvers = {
      User: {
        email: (parent, args, context) => {
          // 'context' objesi genellikle kimliği doğrulanmış kullanıcıyı içerir
          // Eğer isteği yapan kullanıcı admin ise VEYA kendi profilini görüntülüyorsa e-postayı döndür
          if (context.user.isAdmin || context.user.id === parent.id) {
            return parent.email;
          }
          return null; // Aksi halde null döndür.
        }
      }
    };
    

    Bu kontrol, OWASP ASVS 13.4.2 maddesindeki "yetkilendirme mantığının... iş mantığı katmanında olduğu doğrulanmalıdır" ilkesinin doğrudan bir uygulamasıdır. Güvenlik mantığı, GraphQL katmanının kendisine değil, veriyi sağlayan resolver'lara (iş mantığı katmanı) aittir.


Sonuç ve Sonraki Adımlar

Bu derste GraphQL'in esnek yapısının getirdiği üç temel güvenlik riskini ve bunları kod seviyesinde nasıl analiz edeceğimizi öğrendik. REST'in katı endpoint yapısından GraphQL'in dinamik sorgu modeline geçiş, güvenlik odağını da kaydırmıştır.

Anahtar Çıkarımlar:

  • Introspection: Üretim ortamları için mutlaka devre dışı bırakılmalıdır. Bu, en temel ve en kolay uygulanabilir GraphQL güvenlik önlemidir.
  • Kaynak Tüketimi: Her GraphQL API'si, sorgu derinliği, karmaşıklığı veya maliyet analizi gibi mekanizmalarla sınırsız sorgulara karşı korunmalıdır. Bu kontrollerin yokluğu, ciddi bir hizmet reddi zafiyetine işaret eder.
  • Aşırı Veri İfşası: GraphQL'de yetkilendirme sadece "tipe" erişimle bitmez. Hassas "alanlar" için resolver seviyesinde ek yetki kontrolleri yapılmalıdır. Şemada neyin ifşa edildiği, en az neyin nasıl işlendiği kadar önemlidir.

Bu konular, GraphQL güvenliğinin temel taşlarıdır. Bir sonraki dersimizde, bu temellerin üzerine inşa ederek daha incelikli zafiyetlere odaklanacağız. GraphQL resolver fonksiyonlarında yetkilendirme mantığı atlatma ve N+1 sorgu problemlerini tespit ederek hem güvenlik hem de performans açısından resolver'ların nasıl kritik bir rol oynadığını derinlemesine inceleyeceğiz.