Mert Özen Her Satırda Daha İleri
Güvenlik Threat Modeling ve Güvenli Geliştirme Süreci — #01

Threat Modeling Nedir? Dört Soru, Data Flow Diagram ve Trust Boundary ile Bookstore Checkout'unu Modellemek

Mert Özen 15 Kas 2026 10 dk 4 görüntülenme
Threat Modeling Nedir? Dört Soru, Data Flow Diagram ve Trust Boundary ile Bookstore Checkout'unu Modellemek

Threat modeling, kod yazılmadan önce sistemin bir temsili üzerinde "ne ters gidebilir?" diye sormaktır ve en çok tasarım aşamasında işe yarar. Threat Modeling Manifesto'nun dört sorusu, DFD elemanları ve trust boundary'lerle Bookstore'un checkout, ödeme ve kargo akışını modelliyor, modeli OWASP Threat Dragon 2.6.2 ile JSON olarak repo'da versiyonluyor ve PR template'ine bağlıyoruz.

Sürüm notu: Bu yazıdaki örnekler .NET 10 ve Threat Dragon 2.6.2 ile denenmiştir; Microsoft Threat Modeling Tool 7.3 yalnız Windows'ta çalıştığı için dokümantasyonundan anlatılmış, çalıştırılmamıştır.

Önce Tahmin Et

Sitedeki serilerin ortak örnek uygulaması Bookstore (bir online kitapçı) şimdiye kadar siparişi alıp stoğu ayırıyordu; ödeme yoktu. Bu sprint'te ekip bir ödeme sağlayıcıyla entegre oluyor: müşteri sağlayıcının hosted ödeme sayfasında kartını giriyor, sağlayıcı ödeme tamamlanınca Bookstore'a bir webhook gönderiyor, Bookstore siparişi Paid yapıyor ve kargo kaydını açıyor. Emre kodu iki günde yazıyor, testler yeşil, PR (pull request; kod değişikliği isteği) onaylanıyor.

Tahmin et: webhook endpoint'i internete açık bir URL. Sağlayıcıdan gelmeyen, elle hazırlanmış bir payment.succeeded isteği gelirse ne olur? Daha önemlisi, bu soruyu kim, hangi toplantıda sormalıydı? Code review sırasında reviewer endpoint'in kodunu okur ama "bu isteği kim gönderebilir?" sorusu kodun içinde yazmaz. Pentest bulabilir ama release'ten haftalar sonra. Bu sorunun doğal yeri tasarım aşamasıdır ve adı threat modeling'dir. Bu yazıda Bookstore checkout'unu dört soru, bir DFD (Data Flow Diagram; verinin sistemde nereden nereye aktığını gösteren diyagram) ve trust boundary'lerle modelliyoruz.

Threat Modeling Nedir?

Threat Modeling Manifesto (Kasım 2020'de on beş kişilik bir çalışma grubunun yayımladığı metin) tanımı kısa tutar: "Threat modeling is analyzing representations of a system to highlight concerns about security and privacy characteristics." Önemli kelime "representation": sistemin kendisini değil, bir temsilini (diyagram, liste, hatta beyaz tahtadaki bir çizim) inceliyorsun. Bu yüzden threat modeling kod yazılmadan önce yapılabilir; en çok işe yaradığı yer de orasıdır, çünkü tasarımdaki bir hatayı düzeltmek henüz bir commit kadar ucuzdur.

Manifesto, Adam Shostack'ın yıllardır kullandığı dört soruyu esas alır. Türkçe karşılıklarıyla:

  • Ne üzerinde çalışıyoruz? Sistemin, ya da bu sprint'te değişen parçasının, ortak bir resmi.
  • Ne ters gidebilir? Resmin üzerinde tehdit aramak; STRIDE (Spoofing, Tampering, Repudiation, Information disclosure, Denial of service, Elevation of privilege; altı tehdit türünün baş harfleri) burada devreye girer.
  • Buna karşı ne yapacağız? Her tehdit için bir karar: önlem, tasarım değişikliği, risk transferi ya da bilerek kabul.
  • Yeterince iyi bir iş çıkardık mı? Modelin, kararların ve testlerin gözden geçirilmesi.

Bu yazı ilk soruya odaklanıyor. İkinci ve üçüncü soru serinin ikinci yazısının konusu; STRIDE'a giriş zaten Web Güvenliği (OWASP Top 10) serisinin Insecure Design yazısında yapıldı, burada tekrar etmiyoruz. Dördüncü soru en çok atlanan sorudur ve "Sık Düşülen Tuzaklar"da geri döneceğiz.

Ne Zaman, Kimle, Ne Kadar?

Threat modeling'i "yılda bir yapılan güvenlik çalışması" olarak görürsen model hep eskimiş olur. Daha iyi işleyen yaklaşım, onu belirli tetikleyicilere bağlamaktır. Bookstore ekibinin kullandığı liste şu: yeni bir feature bir trust boundary'yi geçiyorsa (internetten gelen yeni bir endpoint, yeni bir veri deposu), yeni bir üçüncü parti entegrasyonu ekleniyor ya da değişiyorsa (ödeme, kargo, e-posta, kimlik sağlayıcı), mimari değişiyorsa (yeni bir service, queue, cache ya da deploy ortamı) ve bir incident ya da pentest bulgusu modelin yanlış olduğunu gösterdiyse. Bir bug fix ya da yalnız UI metni değiştiren bir iş için model açılmaz.

Katılımcılar küçük tutulur. Feature'ı yazacak geliştirici (burada Emre) şarttır, çünkü sistemi en iyi o bilir. Tech lead ya da mimariyi bilen biri bağlamı tamamlar. Product Owner (Selin) iş etkisini söyler: "sahte ödeme onayıyla bir sipariş kargoya çıkarsa ne kaybederiz?" sorusunun cevabı ondadır. Operasyonu bilen biri deploy ortamını, log'ları ve secret'ları bilir. Ayrı bir güvenlik ekibin yoksa bu dört kişi yeter; serinin üçüncü yazısında bu rolü düzenli üstlenen bir security champion'ı konuşacağız.

Süre için iyi bir ölçü: bir feature için 60–90 dakikalık tek oturum. Oturum uzuyorsa kapsam büyüktür; modeli böl. Checkout, Payments ve Shipping tek oturuma sığar; back office ve Kurumsal Sipariş ayrı oturumlardır.

DFD'nin Dört Elemanı ve Trust Boundary

Threat modeling'de en çok kullanılan temsil DFD'dir. Mimari diyagramdan farkı, sunucuları ya da class'ları değil verinin hareketini göstermesidir. Dört eleman ve bir çizgi yeter:

ElemanÇizimiBookstore'daki örneğiSorulacak ilk soru
External entityDikdörtgenMüşterinin tarayıcısı, ödeme sağlayıcı, kargo firmasıKontrolümüzde değil; kimliğini nasıl biliyoruz?
ProcessDaireOrdering, Payments, ShippingHangi girdiyi kabul ediyor, hangi yetkiyle çalışıyor?
Data storeİki paralel çizgiBookstoreDbKim okuyor, kim yazıyor, yedekleri nerede?
Data flowOkPOST /api/orders, webhookNe taşıyor, şifreli mi, değiştirilebilir mi?
Trust boundaryKesikli çizgiİnternet ile Bookstore.Api arasıBu çizgiyi geçen veriye neden güveniyoruz?

Trust boundary, iki tarafın farklı güven seviyesinde ya da farklı sahiplikte olduğu yerdir: internet ile API, API ile veritabanı, bizim kodumuz ile sağlayıcının sistemi. Tehditlerin büyük kısmı bu çizgileri geçen data flow'larda çıkar; çünkü çizginin bir tarafında yapılan varsayım öbür tarafta geçerli değildir. Angular SPA (Single Page Application; tarayıcıda çalışan tek sayfalık uygulama) bizim kodumuzdur ama müşterinin tarayıcısında çalışır, yani çizginin dışındadır: oradan gelen fiyat, toplam ya da rol bilgisine güvenilmez.

Bookstore Checkout'unu Modellemek

Oturumda Emre beyaz tahtaya önce müşteriyi, sonra müşterinin çağırdığı endpoint'i çizdi; ekip her oka "bu ne taşıyor?" diye sorarak ilerledi. Yarım saat sonra ortaya çıkan diyagram şu:

01-checkout-dfd-tr.png

Diyagramı çizmek tek başına üç karar ürettiğini gösterdi. Birincisi, 6 numaralı flow: kart bilgisi müşterinin tarayıcısından doğrudan sağlayıcının sayfasına gider, Bookstore.Api boundary'sine hiç girmez. Bu bir tasarım kararıdır ve Payment Card Industry Data Security Standard (kart verisi işleyen sistemler için sektör standardı) yükünü küçük tutan şey de budur. Diyagram bu kararı görünür kılar; ileride biri "kart formunu kendi sayfamıza alalım" derse modelin değişmesi gerektiği açıktır.

İkincisi, 7 numaralı flow. Webhook internetten gelir ve doğrudan siparişin durumunu değiştirir. "Önce Tahmin Et"teki sorunun yeri tam burası: bu boundary'yi geçen istek gerçekten sağlayıcıdan mı geliyor, aynı istek ikinci kez gönderilirse ne oluyor? Cevabı ikinci yazıda STRIDE ile arayacağız; şimdilik modelde bu flow'un public network'ten geldiği ve Payments'ın durum değiştiren bir process olduğu yazılı.

Üçüncüsü, 9 numaralı flow kargo firmasına ne gönderdiğimizi sordurdu. Kod siparişin tamamını, yani kitapları, fiyatları ve e-posta adresini gönderiyordu; kargo firmasının ihtiyacı ise ad, adres ve paket bilgisi. Bu bir güvenlik açığından çok veri minimizasyonu konusu ve ayrıntısı Veri Gizliliği ve KVKK (Kişisel Verilerin Korunması Kanunu) serisinde. Threat modeling'in iyi tarafı, güvenlik ve gizlilik sorularını aynı resim üzerinde sorabilmesidir.

Diyagramın neyi göstermediği de bir karar: back office, katalog yönetimi ve Notifications bu modelin dışında. threat-model/README.md bunu açıkça yazar; yazılmayan kapsam dışı alan, "modellenmiş" sanılır.

Araçlar: Threat Dragon, Microsoft Threat Modeling Tool, Beyaz Tahta

İlk oturum için en iyi araç beyaz tahtadır: herkes kalem tutabilir ve kimse çizimin güzelliğiyle uğraşmaz. Ama oturumdan sonra modelin bir dosyaya, tercihen koda yakın bir yere taşınması gerekir. Burada iki yaygın ücretsiz araç var.

OWASP (Open Worldwide Application Security Project; açık uygulama güvenliği topluluğu) Threat Dragon'ın son sürümü 10 Mayıs 2026'da yayımlanan 2.6.2. Windows için masaüstü kurulumu (Threat-Dragon-ng-Setup-2.6.2.exe) ve bir web uygulaması olarak Docker image'ı var. Modeli JSON olarak saklar; bu da onu Git'te versiyonlamayı ve PR'da diff'ini okumayı mümkün kılar. Web sürümünü yerelde çalıştırmak için üç anahtar gerekiyor; PowerShell 7 ile kriptografik olarak rastgele üretip bir td.env dosyasına yazıyoruz:

function New-HexKey { [Convert]::ToHexStringLower([Security.Cryptography.RandomNumberGenerator]::GetBytes(16)) }
@"
ENCRYPTION_JWT_REFRESH_SIGNING_KEY='$(New-HexKey)'
ENCRYPTION_JWT_SIGNING_KEY='$(New-HexKey)'
ENCRYPTION_KEYS='[{"isPrimary": true, "id": 0, "value": "$(New-HexKey)"}]'
NODE_ENV='development'
SERVER_API_PROTOCOL='http'
"@ | Set-Content -Encoding utf8NoBOM td.env

Dokümantasyondaki örnek anahtarları kopyalama; dosyayı repo'ya da koyma. Image'ı çekip digest'ini Docker Hub'daki v2.6.2 tag'inin digest'iyle karşılaştırdık, sonra container'ı yalnız 127.0.0.1'e bağlayarak başlattık:

docker image inspect owasp/threat-dragon:v2.6.2 --format '{{index .RepoDigests 0}}'
docker run --rm -d -p 127.0.0.1:8080:3000 -v "${PWD}/td.env:/app/.env:ro" owasp/threat-dragon:v2.6.2
owasp/threat-dragon@sha256:ec26312b472ad364d5b79283d795f946b9aff9639b798e9f76bfdd243810af17

http://127.0.0.1:8080/ adresinde "Login to Local Session" ile girip "Open an existing threat model" ekranına bookstore-checkout.json dosyasını bırakınca diyagram ve elemanların özellikleri (protocol, public network, encrypted) açılıyor. Kaydedince aynı JSON güncelleniyor. Dosya düz JSON olduğu için onu araçsız da okuyabilirsin; örneğin jq ile boundary'leri geçen flow'ları listelemek PR açıklamasına konacak bir özet çıkarır:

jq -r '.detail.diagrams[0].cells[] | select(.data.type == "tm.Flow") | [.data.name, .data.protocol, (if .data.isPublicNetwork then "public" else "internal" end)] | @tsv' threat-model/bookstore-checkout.json
1 POST /api/orders	HTTPS	public
2 order + payment URL	HTTPS	public
3 order, stock (SQL)	SQL/TLS	internal
4 start payment	in-process	internal
5 create payment session	HTTPS	public
6 card details (hosted page)	HTTPS	public
7 webhook payment.succeeded	HTTPS	public
8 mark order Paid	SQL/TLS	internal
9 create shipment	HTTPS	public
10 tracking webhook	HTTPS	public
11 paid orders, shipment	SQL/TLS	internal

Microsoft Threat Modeling Tool'un son sürümü 10 Kasım 2025 tarihli 7.3.51110.1. Yalnız Windows 10 ve üzerinde, .NET Framework 4.7.1 ile çalışır; modeli .tm7 dosyasında tutar ve seçtiğin template'e göre her elemana STRIDE tehditlerini kendisi önerir. Bu öneriler başlangıç için faydalıdır ama listeyi uzatır; ekibin her birini tek tek "bizde geçerli mi?" diye değerlendirmesi gerekir. Bu yazıyı hazırlarken Windows ortamı olmadığı için araç çalıştırılmadı; anlatım Microsoft Learn'deki sürüm notlarına ve dokümana dayanıyor.

Bir de "threat model as code" araçları var: modeli diyagram aracında çizmek yerine kodla tarif edip diyagramı ve tehdit listesini ondan üretiyorsun. OWASP pytm'de sistem Python ile tarif edilir (son sürüm 1 Kasım 2026'da çıkan 1.4.1); Threagile'da YAML (insan tarafından okunabilir bir veri biçimi) ile. İkisi de diff'i okunabilir dosyalar üretir ama ekibin bir dil daha öğrenmesini gerektirir. Bu seride ikisini de çalıştırmadık; Bookstore için Threat Dragon'ın JSON'u yeterli.

Modeli Repo'da Yaşatmak

Model koda yakın durursa güncel kalma şansı artar. Bookstore'da modeli threat-model/ klasörüne koyduk ve ne zaman güncelleneceğini aynı klasördeki README'ye yazdık. Asıl hatırlatıcı ise PR template'i, çünkü herkes onu her PR'da görüyor:

## What changes

<!-- Bir iki cümle. Backlog item'ının linkini ekle. -->

## Threat model check

- [ ] This change adds or changes a data flow, a data store, an external entity or a trust boundary.
      If checked: `threat-model/bookstore-checkout.json` is updated in this PR, or the reason is written here.
- [ ] New threats found in this PR are in the model or in the backlog (link).

Kutucuk işaretlendiği hâlde model değişmemişse reviewer'ın sorması gereken soru basit: "Bu PR yeni bir ok çiziyor mu?" Code Review serisinin reviewer checklist'i yazısındaki sorulara bu maddeyi eklemek yeterli; ayrı bir süreç kurmaya gerek yok.

Üretimde Dikkat

Threat model, sistemin zayıf noktalarının bir haritasıdır. Bookstore'un kodu public bir GitHub reposunda duruyor ve bu seri öğretmek için yazıldığı için modelin tamamını, açık tehditlerle birlikte orada tutuyoruz. Gerçek bir üründe bunu yapma: DFD'nin public olması çoğu zaman sorun değildir, çünkü saldırganın zaten görebileceği akışları anlatır; ama açık tehditlerin ayrıntısı ("webhook imzası henüz doğrulanmıyor") saldırgana yapılacaklar listesi verir. Public bir repoda yalnız diyagramı bırak, tehditleri ve kararları private bir repoda ya da yalnız ekibin görebildiği backlog'da tut; düzeltilen tehdit, testiyle birlikte modele işlenir.

Modelin üstüne son gözden geçirme tarihini yaz. Altı aydır dokunulmamış bir model ya sistemin hiç değişmediğini ya da modelin unutulduğunu gösterir; ikincisi çok daha olası. Bir incident sonrasında yapılan ilk işlerden biri de modeli açıp "bu flow modelde var mıydı?" diye bakmak olmalı. Yoksa, sorun yalnız kodda değil süreçtedir.

Son olarak, modelin sahibi bir kişi olsun. "Ekibin modeli" pratikte kimsenin modeli olmaz. Bookstore'da modelin sahibi tech lead; feature'ı yazan geliştirici kendi oklarını günceller, sahip ise kapsamı ve tarihi takip eder.

Sık Düşülen Tuzaklar

En sık hata, bütün sistemi tek seferde modellemeye çalışmak. Duvar boyu bir diyagram ilk oturumda bitmez, ikinci oturuma kimse gelmez ve sonunda kimsenin okumadığı bir resim kalır. Bir feature'la, bir akışla başla; modeli parça parça büyüt.

İkinci hata, diyagramı bir kez çizip unutmak. Bir yıl önce çizilmiş, webhook'un olmadığı bir DFD, olmayan bir güven duygusu verir; hiç model olmamasından daha zararlıdır. Modeli tetikleyicilere ve PR template'ine bağlamak bu yüzden önemli.

Üçüncü hata, aracı işin kendisi sanmak. Araç önerdiği yüz tehdidi listeler, ekip hepsine "N/A" yazar ve model "tamamlandı" sayılır. Değer, aracın ürettiği listede değil ekibin yaptığı konuşmada. Dördüncüsü de dördüncü sorunun atlanması: oturum sonunda "neyi kaçırmış olabiliriz, kararları kim takip edecek?" diye sorulmazsa kararlar backlog'a hiç girmez.

Küçük Bir Deneme

★☆☆ Bookstore sipariş onayı e-postalarını bir e-posta sağlayıcısı üzerinden göndermeye başlıyor. Diyagrama hangi external entity, hangi process ve hangi flow eklenir? Kaç trust boundary geçiliyor?

★★☆ bookstore-checkout.json dosyasını Threat Dragon'da aç, 10 numaralı tracking webhook'u için bir açıklama yaz ve kaydet. Git diff'inde hangi alanların değiştiğine bak; PR'da reviewer'ın bu diff'i okuyabilmesi için ne gerekir?

★★★ Yukarıdaki jq sorgusunu CI'da çalışan bir kontrole dönüştür: protocol alanı boş olan ya da public network'ten gelip şifresiz işaretlenmiş bir flow varsa build başarısız olsun. Bu kontrol hangi tür hatayı yakalar, hangisini asla yakalayamaz?

Elimizde artık ortak bir resim var: ilk soruyu cevapladık. Serinin ikinci yazısında aynı diyagramın her elemanına STRIDE'ı uyguluyor, webhook ve kurumsal satın alma onayı için tehditleri buluyor, bunları olasılık ve etkiye göre sıralıyor ve en önemlisini bir teste dönüştürüyoruz.