Skip to main content

Welcome to a Grasp lesson

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

Lesson 2

Veri Akışı Analizi ile Kod İnceleme Teknikleri

Recommended resources

Merhaba, "İleri Seviye Güvenli Kod İnceleme Metodolojisi" modülümüzün ikinci dersine hoş geldiniz. Bir önceki dersimizde, güvenilmeyen verinin program içindeki yolculuğunu takip etmek için temel bir zihinsel model olan Taint Analysis kavramını ve bu analizin kaynak (source), hedef (sink) ve temizleyici (sanitizer) bileşenlerini incelemiştik. Bu kavramsal temel, güvenlik zafiyetlerini sezgisel olarak anlamamızı sağladı.

Bu derste ise sezgiden sistematiğe geçiş yapacağız. "Ne?" sorusundan "Nasıl?" sorusuna ilerleyerek, Veri Akış Analizi (Data Flow Analysis - DFA) adı verilen ve SAST (Static Application Security Testing) araçlarının arkasındaki temel motoru oluşturan teknikleri uygulamalı olarak ele alacağız. Dersin sonunda, bir programdaki verinin fonksiyon çağrıları, atamalar ve karmaşık ifadeler arasında nasıl aktığını sistematik olarak izlemek için kullanılan temel algoritmik yaklaşımları anlamış olacaksınız.

Veri Akış Analizine Giriş: Sezginin Formalleşmesi

Önceki derste tartıştığımız Taint Analysis, aslında bir tür veri akışı analizidir. Ancak DFA, bu takibi çok daha yapısal ve matematiksel bir temele oturtan genel bir çerçevedir. DFA'nın temel amacı, bir programdaki her noktada değişkenlerin alabileceği olası değerler kümesi hakkında bilgi toplamaktır. Güvenlik bağlamında bu "değer", bir değişkenin "kirli" olup olmadığı, belirli bir aralıkta bir sayı olup olmadığı veya potansiyel olarak tehlikeli karakterler içerip içermediği gibi özellikler olabilir.

Bu analizi otomatize edebilmek için, öncelikle programın yapısını makinenin anlayabileceği bir formata dönüştürmemiz gerekir. Bu format Kontrol Akış Grafiği (Control Flow Graph - CFG) olarak adlandırılır.

  • Kontrol Akış Grafiği (CFG): Bir programın tüm olası yürütme yollarını gösteren bir yönlü grafiktir.
    • Düğümler (Nodes): Arada hiçbir dallanma (if, for, while) veya atlama olmayan, ardışıl komut dizilerinden oluşan temel bloklardır (basic blocks).
    • Kenarlar (Edges): Bir temel bloktan diğerine kontrol akışının olası transferini temsil eder. Örneğin, bir if ifadesi, bir bloktan iki farklı bloğa giden iki kenar oluşturur.

Sizin gibi SAST araçlarının yönetimi ve çıktıları konusunda deneyimli bir profesyonel için, CFG'yi bu araçların analize başlamadan önce koddan ürettiği temel bir "harita" olarak düşünebilirsiniz. Analiz, bu harita üzerinde verinin yollarını takip ederek yapılır.


Veri Akışı Analizinin Mekaniği: Ulaşan Tanımlar (Reaching Definitions)

CFG'yi oluşturduktan sonra, "veri akışı denklemlerini" çözerek bilgiyi grafik üzerinde yaymaya başlarız. Bu konuyu somutlaştırmak için en klasik veri akışı analizi problemlerinden biri olan Ulaşan Tanımlar Analizi (Reaching Definitions Analysis) tekniğini inceleyelim.

Bu teknik şu soruyu cevaplar: "Programın herhangi bir noktasında bir değişken kullanıldığında, bu değişkene o anki değeri atamış olabilecek tüm tanım (atama) satırları hangileridir?"

Bu konunun teknik detaylarını ve pratik uygulamasını anlamak için aşağıdaki makaleyi incelemenizi rica ediyorum. Makale, PHP örnekleri üzerinden veri akışı analizi ve makine öğrenimini birleştirerek zafiyet tespiti üzerine odaklanıyor, ancak bizim için önemli olan kısım, veri akışı tekniklerini açıkladığı bölümdür.

[PDF] Discovering software vulnerabilities using data-flow analysis and ...

Bu makale, veri akışı analiz tekniklerinin zafiyet tespiti için nasıl özellikler (features) çıkardığını anlatıyor. Konunun temellerini anlamak için oldukça faydalı bir kaynak.

Lütfen makalenin Bölüm 3'ün giriş paragrafını okuyarak başlayın. Bu bölüm, analiz sürecinin PHP kodunu bir CFG'ye dönüştürmekle başladığını açıklıyor. Ardından, Bölüm 3.1'in tamamını dikkatle okuyun. Bu bölümde "reaching definitions" analizinin ne olduğu, bir değişkenin tanımının nasıl "öldürüldüğü" (killed) ve kullanım-tanım zincirlerinin (use-definition chains - UD chains) nasıl oluşturulduğu bir CFG örneği (Figure 1) ve tablo (Table 1) üzerinden somut bir şekilde gösterilmektedir.

Okuduğunuz bölümde de gördüğünüz gibi, ulaşan tanımlar analizi iteratif bir süreçtir. Matematiksel altyapınızla bu süreci bir sabit nokta (fixed-point) iterasyon problemi olarak düşünebilirsiniz. Her temel blok için dört temel küme tanımlanır:

  • GEN[b]: b bloğu içinde oluşturulan (atama yapılan) tanımlar kümesi.
  • KILL[b]: b bloğundaki bir tanım tarafından "öldürülen" (geçersiz kılınan), programın başka yerlerindeki tanımlar kümesi. Örneğin, x = ... ataması, x değişkeninin önceki tüm tanımlarını öldürür.
  • IN[b]: b bloğunun girişindeki ulaşan tanımlar kümesi. Bu, b'ye giden tüm önceki blokların OUT kümelerinin birleşimidir.
  • OUT[b]: b bloğunun çıkışındaki ulaşan tanımlar kümesi. OUT[b] = GEN[b] ∪ (IN[b] - KILL[b]) formülüyle hesaplanır.

Algoritma, OUT kümeleri değişmeyene kadar (sabit noktaya ulaşana kadar) bu denklemleri tüm bloklar için tekrar tekrar hesaplar. Süreç tamamlandığında, programdaki her bir değişken kullanımı için ona hangi tanımların ulaşabildiğini biliriz. Bu UD zincirleri, bir verinin kökenini takip etmenin temelini oluşturur. Örneğin, mysql_query($query) çağrısındaki $query kullanımı için UD zincirini takip ederek, bu değişkenin $_GET['id'] gibi bir kaynaktan gelen bir tanıma ulaşıp ulaşmadığını belirleyebiliriz.


Veri Akışı Analizinin Güvenlikteki Rolü: Değer Analizi ve Program Dilimleme

Ulaşan tanımlar, temel bir tekniktir. Modern SAST araçları, Değer Analizi (Value Analysis) gibi daha gelişmiş DFA türlerini kullanır. Değer analizi, bir değişkenin tam değerini değil, alabileceği olası değerler kümesini (örneğin, "0 ile 100 arasında bir tamsayı" veya "sadece 'admin' veya 'user' string'lerinden biri") soyut olarak hesaplar. Bu, basit bir "kirli/temiz" etiketinden çok daha fazla bilgi sağlar.

Aşağıdaki akademik makale, Heartbleed gibi karmaşık bir zafiyetin tespitinde statik analizin (değer ve taint analizi dahil) ve program dilimlemenin nasıl bir arada kullanıldığını gösteriyor.

[PDF] Combining Static and Dynamic Analyses for Vulnerability Detection

Bu makale, statik analizin pratik uygulamalarını ve sınırlarını Heartbleed zafiyeti üzerinden tartışıyor. Veri akışı analizinin gerçek dünyadaki karmaşıklığını anlamak için değerli bir örnektir.

İlk olarak, Bölüm 4'ün ilk paragrafını okuyun. Burada, FRAMA-C aracının değer analizini kullanarak memcpy çağrısı için potansiyel hataları (alarmları) nasıl ürettiği açıklanıyor. Değer analizinin, değişkenler için "aşırı yaklaştırılmış" (over-approximated) değer aralıkları hesapladığına dikkat edin. Ardından, aynı bölümün devamını okuyun. Bu kısım, değer analizinin ürettiği sayısız alarm arasından, taint analizi kullanılarak güvenlikle ilgili olanların nasıl ayırt edildiğini anlatıyor. Son olarak, Bölüm 5'in ilk paragrafını okuyun. Burada, Program Dilimleme (Program Slicing) tekniği tanıtılıyor. Bu teknik, veri akışı analizinden elde edilen bağımlılık bilgilerini kullanarak, kodun ilgisiz kısımlarını atar ve analizi yalnızca belirli bir değişkeni veya satırı etkileyen kod parçalarıyla (dilim) sınırlar.

Bu okumalardan çıkarabileceğimiz önemli noktalar şunlardır:

  1. Değer Analizi: Bir değişkenin sadece "kirli" olup olmadığını değil, aynı zamanda türü ve olası değer aralığı gibi daha zengin bilgileri de takip eder. Bu, yanlış pozitifleri (false positives) azaltmada kritik bir rol oynar.
  2. Analizlerin Birleşimi: Taint analizi, değer analizinin ürettiği genel alarmlar arasından güvenlik açısından riskli olanları (örneğin, kullanıcı girdisinden etkilenenler) önceliklendirmek için kullanılır.
  3. Program Dilimleme: Veri akışı analizi, devasa bir kod tabanını analiz ederken odaklanmamızı sağlar. Bir sink (hedef) belirleyip, o sink'i etkileyen tüm kod satırlarını geriye doğru bir dilim olarak çıkarabiliriz. Bu, manuel kod incelemesini büyük ölçüde basitleştiren güçlü bir tekniktir.

Zorluklar ve Sınırlar

Veri akışı analizi güçlü bir araç olsa da, her derde deva değildir. Özellikle Heartbleed gibi karmaşık zafiyetler, standart DFA tekniklerinin sınırlarını zorlar. LINK numaralı makalenin "Discussion" bölümünde belirtildiği gibi, başlıca zorluklar şunlardır:

  • Prosedürler Arası Analiz (Inter-procedural Analysis): Veri akışını fonksiyon çağrıları boyunca takip etmek, özellikle binlerce fonksiyona sahip büyük projelerde, durum uzayının patlamasına (state explosion) neden olabilir ve hesaplama maliyetini aşırı artırır.
  • İşaretçiler ve Takma Adlar (Pointers and Aliasing): C/C++ gibi dillerde, birden fazla işaretçi değişkeninin aynı bellek konumunu göstermesi (aliasing), hangi değişkenin hangi veriyi etkilediğini kesin olarak belirlemeyi çok zorlaştırır.
  • Yaklaştırma ve Kesinlik (Approximation and Precision): Analizi sonlu bir sürede tamamlayabilmek için SAST araçları her zaman "aşırı yaklaştırma" (over-approximation) yapmak zorundadır. Bu, tüm olası zafiyetleri bulmayı garanti eder (soundness) ancak çoğu zaman gerçekte var olmayan zafiyetler (false positives) raporlamalarına neden olur. Sizin de SAST çıktılarını yorumlarken sıkça karşılaştığınız bu durum, bu temel teorik takastan kaynaklanır.

Özet ve Sonraki Adımlar

Bu derste, Taint Analysis'in arkasındaki resmi mekanizma olan Veri Akışı Analizi'ni derinlemesine inceledik.

  • DFA, program kodunu bir Kontrol Akış Grafiği (CFG) üzerinde temsil ederek verinin yolculuğunu sistematik olarak izler.
  • Ulaşan Tanımlar Analizi gibi teknikler, bir değişkene değerini hangi atamaların verdiğini takip etmek için kullanım-tanım (UD) zincirleri oluşturur.
  • Değer Analizi ve Program Dilimleme gibi daha gelişmiş uygulamalar, analizin kesinliğini artırır ve büyük kod tabanlarında incelemeyi pratik hale getirir.
  • Bu teknikler SAST araçlarının çekirdeğini oluştursa da, prosedürler arası analiz, işaretçiler ve doğruluk/kesinlik takası gibi doğal zorluklara sahiptirler.

Verinin program içinde nerelere aktığını anladığımıza göre, bir sonraki dersimizde odağımızı programın yürütme mantığına çevireceğiz. Kontrol Akış Analizi (Control Flow Analysis) tekniklerini kullanarak, programın dallanma ve karar verme yapısındaki mantık hatalarından kaynaklanan güvenlik zafiyetlerini nasıl tespit edebileceğimizi inceleyeceğiz. Bu, özellikle yetkilendirme atlatma (authorization bypass) gibi zafiyetleri bulmada kritik bir rol oynar.