OWASP Top 10:2025 - Blue Team Perspektifi
Mahmut ATSIZ · @MAHMUT-ATSIZ
Meriç Aytaş · @benmevic
Melisa Kumral · @melisakumral
Bir uygulamayı güvenli hale getirmeye çalışırken genellikle ilk olarak SQL Injection, XSS veya yetki kontrolleri gibi doğrudan göze çarpan zafiyetlere odaklanırız. Ancak modern yazılım mimarilerinde en yıkıcı ihlaller; çoğu zaman eksik nesne yetkilendirmelerinden, yanlış yapılandırılmış altyapılardan, kodun dış dünyayla kurduğu sessiz güven ilişkilerinden, kör noktada kalan loglardan veya beklenmedik bir hata anında sistemin verdiği yanlış tepkilerden doğar.
OWASP Top 10'un 2025 güncellemesi de mimarideki bu değişimi doğrudan yansıtıyor. 2021 listesinde ayrı bir başlık olan SSRF (Server-Side Request Forgery) artık A01:2025 - Broken Access Control çatısı altına alınırken, yazılım tedarik zinciri saldırıları A03:2025 - Software Supply Chain Failures adıyla bağımsız ve geniş bir kategoriye dönüştürüldü. Listenin 10. sırasına ise tamamen yeni bir başlık olarak A10:2025 - Mishandling of Exceptional Conditions eklendi.
Bu kapsamlı rehberde, OWASP Top 10:2025 listesindeki tüm zafiyetleri saldırı senaryoları, sahadaki gerçek vakalar ve kod düzeyindeki savunma mekanizmalarıyla birlikte adım adım inceleyeceğiz:
- A01:2025 - Broken Access Control (Bozuk Erişim Kontrolü)
- A02:2025 - Security Misconfiguration (Güvenlik Yapılandırma Hataları)
- A03:2025 - Software Supply Chain Failures (Yazılım Tedarik Zinciri İhlalleri)
- A04:2025 - Cryptographic Failures (Kriptografik Hatalar)
- A05:2025 - Injection (Enjeksiyon Zafiyetleri)
- A06:2025 - Insecure Design (Güvensiz Tasarım)
- A07:2025 - Authentication Failures (Kimlik Doğrulama Hataları)
- A08:2025 - Software or Data Integrity Failures (Yazılım veya Veri Bütünlüğü İhlalleri)
- A09:2025 - Security Logging and Alerting Failures (Güvenlik Günlükleme ve Uyarı İhlalleri)
- A10:2025 - Mishandling of Exceptional Conditions (İstisnai Durumların Yanlış Yönetilmesi)
Bu Rehberde Ne Öğreneceksiniz?
- Erişim, Yapılandırma ve Kriptografi (A01–A04): Nesne düzeyinde yetkilendirme (IDOR/BOLA), SSRF koruması, güvenli CORS ve container sıkılaştırması, tedarik zinciri (
pip --require-hashes, SBOM) ile Argon2id ve AES-GCM uygulamaları. - Enjeksiyon, Tasarım ve Kimlik Doğrulama (A05–A07): Parametrik sorgular, katmanlı URL doğrulaması, iş mantığı ve yarış durumu (TOCTOU) önlemleri, güvenli JWT ve çerez (
HttpOnly,Secure,SameSite) mimarisi. - Bütünlük, Görünürlük ve Hata Yönetimi (A08–A10): A03 ile A08 arasındaki sınır, SRI hash üretimi ve sınırları, yapılandırılmış JSON loglama, MITRE ATT&CK (
T1110,T1070,T1562.001) korelasyon mantığı, Heartbeat alarmları ve Fail-Closed (403/503ayrımı) hata yönetimi.
A01:2025 - Broken Access Control (Bozuk Erişim Kontrolü)
İlgili Temel CWE'ler: CWE-862 (Missing Authorization), CWE-639 (Authorization Bypass Through User-Controlled Key), CWE-863 (Incorrect Authorization), CWE-918 (Server-Side Request Forgery)
Kimlik doğrulama, kullanıcının kim olduğunu belirler. Yetkilendirme ise o kullanıcının hangi işlem ve kaynaklara erişebileceğini kontrol eder. A01, ikinci aşamanın hiç uygulanmaması veya yanlış uygulanması sonucunda oluşur. Sisteme giriş yapmış olmak, sistemdeki bütün dosyalara, API uç noktalarına veya diğer kullanıcıların kayıtlarına erişme hakkı vermez.
Temel ilke şudur: Her istek, sunucuda, ilgili işlem ve nesne düzeyinde yetkilendirilmelidir.
Bozuk Erişim Kontrolünün Yaygın Biçimleri
- IDOR / BOLA: Bir kaynağın numarasını veya kimliğini değiştirerek başka kullanıcıya ait veriye erişmek.
- Dikey yetki yükseltme: Normal kullanıcının yönetici işlemlerini gerçekleştirebilmesi.
- Yatay yetki yükseltme: Aynı yetki düzeyindeki başka kullanıcının bilgilerini görüntüleme veya değiştirme.
- Eksik işlev düzeyinde yetkilendirme (BFLA): Yönetici API'sinin yalnızca arayüzde gizlenmesi, fakat sunucuda korunmaması.
- Mass assignment kaynaklı yetki değişikliği: İstek gövdesindeki
role,is_admingibi alanların kontrolsüz biçimde modele yazılması. - SSRF: Sunucunun kullanıcı tarafından belirlenen hedeflere istek yapmasının kötüye kullanılması; 2025 sınıflandırmasında A01 kapsamındadır.
Saldırı Senaryosu: IDOR ile Başka Kullanıcının Faturasını Okuma
Bir fatura uygulamasında kullanıcı, kendisine ait faturayı şu uç noktadan görüntülüyor olsun:
GET /api/invoices/1042 HTTP/1.1
Host: example.test
Cookie: session=...Sunucu, oturumun geçerli olduğunu kontrol ediyor ancak faturanın kime ait olduğuna bakmıyorsa saldırgan URL'deki 1042 değerini 1043 olarak değiştirerek başka bir müşterinin faturasına erişebilir.
Güvensiz Flask örneği:
@app.get("/api/invoices/<int:invoice_id>")
@login_required
def get_invoice(invoice_id):
invoice = db.execute(
"SELECT id, owner_id, amount FROM invoices WHERE id = ?",
(invoice_id,)
).fetchone()
if invoice is None:
abort(404)
return jsonify(dict(invoice))Buradaki @login_required kullanıcının oturum açtığını doğrular; fakat invoice.owner_id ile oturumdaki kullanıcı arasında hiçbir ilişki kurmaz. Dolayısıyla authentication vardır, authorization yoktur.
Savunma: Nesne Düzeyinde Sunucu Taraflı Yetkilendirme
@app.get("/api/invoices/<int:invoice_id>")
@login_required
def get_invoice(invoice_id):
current_user_id = session["user_id"]
invoice = db.execute(
"""
SELECT id, amount
FROM invoices
WHERE id = ? AND owner_id = ?
""",
(invoice_id, current_user_id)
).fetchone()
if invoice is None:
# Kaynağın varlığını yetkisiz kullanıcıya açıklamamak için
# 'yok' ve 'erişim yok' durumları aynı biçimde ele alınır.
abort(404)
return jsonify(dict(invoice))Neden İşe Yarar?
- Kimlik sunucu oturumundan gelir: Saldırgan
user_idparametresini değiştirerek başka bir kullanıcı gibi davranamaz. - Sahiplik kontrolü veri sorgusuna gömülür: Sorgu yalnızca hem istenen ID'ye hem de oturum sahibine uyan faturayı döndürür.
- Varsayılan davranış reddetmektir: Eşleşmeyen kayıt için içerik dönülmez. Başka kullanıcının geçerli bir fatura ID'sini bilmek yeterli olmaz.
- Tek bir merkezi politika gerekir: Aynı kontrol sadece
GETişleminde değil, indirme, düzenleme, silme ve toplu API işlemlerinde de uygulanmalıdır.
Önemli Ayrım
Tahmin edilmesi zor UUID kullanmak numaralandırmayı zorlaştırır ama yetkilendirmenin yerini tutmaz. Kaynağın ID'si bir şekilde sızarsa sahiplik kontrolü hâlâ zorunludur.
İkinci Senaryo: SSRF ile İç Servise Erişim
Bir uygulama, kullanıcının girdiği URL'den bağlantı önizlemesi oluşturuyor olabilir:
response = requests.get(user_supplied_url, timeout=3)URL kontrol edilmezse saldırgan http://127.0.0.1:8080/admin gibi dış dünyaya kapalı servisleri hedefleyebilir. Bulut ortamlarında 169.254.169.254 gibi metadata adreslerine yapılan istekler de tehlikelidir.
Güvenli tasarım: Mümkünse yalnızca belirli güvenilir hedef alan adlarına izin verilmelidir. Genel URL erişimi gerekiyorsa http/https şema kontrolü, tüm DNS yanıtlarının IP sınıfı kontrolü, yönlendirmelerin devre dışı bırakılması veya her adımda yeniden doğrulanması ve ağ seviyesinde egress engellemesi birlikte uygulanmalıdır. DNS çözümlemesi ile gerçek bağlantı arasında hedef değişebileceğinden yalnızca uygulama seviyesindeki ön kontrol yeterli değildir; bağlantının doğrulanan IP'ye yapılması veya güvenli çıkış proxy'si kullanılması gerekir.
A01 Deploy Öncesi Kontrol Listesi
- [ ] Her uç noktada kimlik doğrulama ve işlem düzeyinde yetkilendirme yapılıyor mu?
- [ ] Kullanıcıya ait her nesne için sahiplik veya erişim politikası denetleniyor mu?
- [ ]
role,is_admin,owner_idgibi hassas alanlar istemciden doğrudan kabul edilmiyor mu? - [ ] Yetki kontrolleri
GET,POST,PUT,PATCH,DELETEve toplu işlemlerin tamamında geçerli mi? - [ ] Dış URL getiren özelliklerde SSRF için ağ çıkış kısıtları mevcut mu?
- [ ] Yetkisiz erişim denemeleri hassas veri sızdırmadan loglanıyor mu?
A02:2025 - Security Misconfiguration (Güvenlik Yapılandırma Hataları)
İlgili Temel CWE'ler: CWE-16 (Configuration), CWE-942 (Permissive Cross-domain Policy with Untrusted Domains)
Bir uygulamanın kaynak kodu güvenli olabilir; ancak onu çalıştıran web sunucusu, container, bulut servisi veya framework yanlış ayarlanmışsa uygulama yine savunmasız kalır. A02, güvenlik açısından kritik ayarların varsayılan, eksik, aşırı izinli veya ortamlar arasında tutarsız bırakılması sonucunda doğar.
Yapılandırma Hatalarının Yaygın Biçimleri
- Üretimde
debugmodunun açık bırakılması ve ayrıntılı hata çıktıları. - Varsayılan parolalar, örnek hesaplar veya unutulmuş yönetim arayüzleri.
- Gereksiz HTTP yöntemlerinin ve servislerin dışarıya açılması.
- Aşırı geniş CORS politikaları.
- Eksik güvenlik başlıkları (
Content-Security-Policy,X-Content-Type-Optionsvb.). - Container'ın root yetkisiyle, gereksiz Linux capabilities ile veya ayrıcalıklı modda çalışması.
- Bulut nesne depolarının ve veritabanlarının yanlışlıkla genel erişime açılması.
- Geliştirme ve üretim ortamları arasında konfigürasyon sapması (configuration drift).
Saldırı Senaryosu: Hatalı CORS Politikası
CORS, tarayıcının başka bir origin'den yapılan isteklerin yanıtını JavaScript'e açıp açmayacağını belirler. Sunucunun bir isteği işleyip işlememesiyle aynı şey değildir.
Aşağıdaki yapılandırma, gelen her Origin değerini güvenilir kabul ediyor:
@app.after_request
def add_cors_headers(response):
origin = request.headers.get("Origin")
if origin:
response.headers["Access-Control-Allow-Origin"] = origin
response.headers["Access-Control-Allow-Credentials"] = "true"
return responseKullanıcı uygulamada oturum açmışken saldırganın yönettiği sayfa API'ye tarayıcı üzerinden istek yapabilir. Eğer oturum çerezi isteğe dahil ediliyorsa ve CORS kontrolü saldırganın origin'ini onaylıyorsa, tarayıcı hassas yanıtı saldırganın JavaScript koduna erişilebilir hale getirebilir.
Not
Bu saldırının gerçekleşmesi çerezin SameSite davranışına, istek türüne ve tarayıcı koşullarına bağlıdır. CORS, kimlik doğrulama veya CSRF korumasının yerine geçmez.
Savunma: Açık Origin Listesi
ALLOWED_ORIGINS = {
"https://app.example.com",
"https://admin.example.com",
}
@app.after_request
def add_cors_headers(response):
origin = request.headers.get("Origin")
response.headers.add("Vary", "Origin")
if origin in ALLOWED_ORIGINS:
response.headers["Access-Control-Allow-Origin"] = origin
response.headers["Access-Control-Allow-Credentials"] = "true"
return responseNeden İşe Yarar?
- Kullanıcı girdisi bir yetki politikasına dönüşmez:
Originbaşlığı doğrudan yansıtılmak yerine önceden belirlenmiş güvenilir origin'lerle karşılaştırılır. - Kimlik bilgisi içeren yanıtların okunması sınırlandırılır: Tarayıcı yalnızca izin verilen origin'lerdeki JavaScript'e CORS erişimi sağlar.
Vary: Originönbellek karışmasını azaltır: CDN ve ara önbelleklerin farklı origin'ler için üretilmiş yanıt başlıklarını yanlış kullanmasının önüne geçmeye yardımcı olur.
CORS dışındaki kontroller de önemlidir. Örneğin üretim ortamında DEBUG=False olmalı, hata ayıklama panelleri yayımlanmamalı, gereksiz servisler kapatılmalı ve güvenlik başlıkları uygulamanın gereksinimlerine göre tanımlanmalıdır.
İkinci Senaryo: Üretimde Hata Ayıklama ve Container Yetkileri
Geliştirme sırasında faydalı olan ayrıntılı hata sayfaları, üretimde dizin yollarını, iç mimariyi veya gizli değerleri ortaya çıkarabilir. Aynı biçimde container'ı privileged: true ile çalıştırmak, bir uygulama ihlalinin etkisini ciddi ölçüde artırabilir.
Örnek bir Kubernetes securityContext:
securityContext:
runAsNonRoot: true
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
seccompProfile:
type: RuntimeDefaultBu ayarlar yetki yükseltme ve dosya sistemi üzerinde değişiklik yapma imkânlarını sınırlar. Ancak uygulamanın yazma ihtiyacı olan dizinler için ayrıca kontrollü volume tanımlanmalı ve kullanılan container imajının root olmayan bir kullanıcıyla çalışabildiği test edilmelidir.
Savunma Mimarisi
Konfigürasyon yalnızca sunucuya elle girilen ayarlar bütünü olarak görülmemelidir. Infrastructure as Code (IaC) kullanılarak sürüm kontrolüne alınmalı; değişiklikler incelemeden geçmeli; CI ortamında yapılandırma taraması yapılmalı; üretimde de beklenen ayarlarla gerçek durum karşılaştırılmalıdır.
A02 Deploy Öncesi Kontrol Listesi
- [ ] Debug modu ve ayrıntılı hata mesajları üretimde kapalı mı?
- [ ] Varsayılan kullanıcılar, parolalar ve örnek servisler kaldırıldı mı?
- [ ] CORS politikasında açık ve dar bir origin listesi kullanılıyor mu?
- [ ] Güvenlik başlıkları ve TLS yapılandırması test ediliyor mu?
- [ ] Container'lar ve bulut kimlikleri en az yetkiyle çalışıyor mu?
- [ ] IaC değişiklikleri denetleniyor ve yapılandırma sapmaları izleniyor mu?
A03:2025 - Software Supply Chain Failures (Yazılım Tedarik Zinciri İhlalleri)
İlgili Temel CWE'ler: CWE-1104 (Use of Unmaintained Third Party Components), CWE-1395 (Dependency on Vulnerable Third-Party Component)
Modern bir uygulama geliştiricinin yazdığı koddan çok daha fazlasını içerir: paket yöneticilerinden indirilen kütüphaneler, geçişli bağımlılıklar, CI/CD eklentileri, derleme makineleri, container temel imajları ve dağıtım sistemleri. Bu zincirdeki tek bir güvenilmeyen halka, uygulamanın kaynak kodunda hata olmasa bile üretime zararlı kod taşınmasına neden olabilir.
A03 kapsamı yalnızca eski kütüphaneler değildir; bağımlılık seçimi, geliştirici hesaplarının ele geçirilmesi, paket yayınlama süreçleri, CI/CD sırları, build altyapısı ve dağıtım kanalı dahil tüm yaşam döngüsüdür.
Temel Saldırı Biçimleri
- Dependency confusion: İç ağdaki özel paket adıyla aynı isimde herkese açık bir paket yayımlayarak yanlış paketin kurulmasını sağlamak.
- Typosquatting: Sık kullanılan paketlerin adına çok benzeyen zararlı paketler oluşturmak.
- Ele geçirilmiş maintainer hesabı: Güvenilir görünen gerçek bir paketin yeni sürümüne zararlı kod eklemek.
- Geçişli bağımlılık riski: Doğrudan seçilmeyen ancak bir başka paketin kurduğu bağımlılık üzerinden etkilenmek.
- CI/CD compromise: Derleme servisinin, GitHub Actions iş akışının veya yayımlama anahtarlarının kötüye kullanılması.
- Güncellenmemiş bileşenler: Bilinen CVE'leri barındıran eski kütüphanelerin kullanılmaya devam edilmesi.
Saldırı Senaryosu: Dependency Confusion
Bir şirket kendi özel paket deposunda company-internal-utils adında Python paketi kullanıyor olsun. Paket yöneticisinin yapılandırması özel depoyla birlikte herkese açık PyPI deposunu da tarıyorsa ve kaynak önceliği doğru yönetilmiyorsa saldırgan aynı isimli bir paketi herkese açık depoya yayımlayabilir. Kurulum süreci yanlış kaynaktan paketi seçerse saldırganın kodu CI ortamında çalıştırılabilir.
Riskli örnek:
pip install \
--extra-index-url https://packages.example.internal/simple \
company-internal-utils--extra-index-url, özel depoya mutlak öncelik garantisi vermez. Böyle bir tasarımda aynı isimli paketin birden fazla kaynaktan çözümlenmesi tehlikelidir.
Savunma: Kontrollü Paket Çözümleme
Özel bağımlılıklar için yalnızca kurumun yönettiği, güvenilir paketleri aynalayan veya açıkça onaylayan bir depo üzerinden çözümleme yapmak daha güvenlidir:
python -m pip install \
--index-url https://packages.example.internal/simple \
-r requirements.txtBuna ek olarak bağımlılıklar kilit dosyasıyla sabitlenmeli, mümkün olan akışlarda hash doğrulaması kullanılmalı ve paket deposunda hangi kaynaklardan hangi paketlerin alınabileceği açıkça yönetilmelidir.
pip için hash kontrollü kurulum örneği:
# requirements.txt — örnek biçim; hash gerçek dosyadan üretilmelidir
example-package==1.2.3 \
--hash=sha256:<dogrulanmis-paket-hashi>python -m pip install --require-hashes -r requirements.txtÖnemli
Hash kilitleme, sonradan değiştirilen bir paketi yakalayabilir; fakat baştan zararlı seçilmiş bir paketi güvenli hale getirmez. Paketin güvenilirliği ayrıca değerlendirilmelidir. Üretim lock dosyası tüm doğrudan ve geçişli bağımlılıkları uygun biçimde sabitlemelidir.
Neden İşe Yarar?
- Kaynak denetimi: Şirketin özel paket adı herkese açık depodaki aynı isimli pakete rastgele yönlenmez.
- Sürüm sabitleme: Bir derleme sırasında farkında olmadan yeni bir bağımlılık sürümüne geçilmesi engellenir.
- Hash doğrulama: Kurulan dağıtım dosyasının beklenen içerikle aynı olduğu kontrol edilir.
- CI erişim sınırlaması: Paket kurma aşamasında kullanılan iş akışına üretim sırları ve gereksiz yetkiler verilmezse ihlalin etkisi küçülür.
Gerçek Vaka: SolarWinds Orion (2020)
SolarWinds vakasında saldırganlar yazılımın derleme ve dağıtım sürecini hedefleyerek güvenilir bir güncelleme kanalından arka kapılı bileşenlerin müşterilere ulaşmasını sağladı. Olay, yalnızca uygulamanın kaynak kodunu taramanın yeterli olmadığını; build sisteminin, imzalama ve yayımlama sürecinin de güvenlik sınırı olduğunu gösterdi.
A03 açısından ders, bağımlılıkları sadece CVE taramasından geçirmek değil, uygulamanın üretim yolculuğunun tamamına güvenilirlik kontrolleri yerleştirmektir.
Katmanlı Savunma
- SBOM (Software Bill of Materials): Doğrudan ve geçişli bileşenlerin envanterini tutun.
- SCA (Software Composition Analysis): Bilinen açıklara ve riskli bağımlılıklara yönelik sürekli analiz yapın.
- Sabit sürümler ve kontrollü güncelleme: Sürüm değişimlerini kod incelemesi ve CI testlerine bağlayın.
- Asgari CI yetkisi: Pipeline token'larına sadece gerekli repository, paket ve dağıtım izinleri verin.
- Kısa ömürlü kimlik bilgileri: Mümkün olduğunda OIDC tabanlı geçici kimlik doğrulama kullanın.
- Build provenance ve artefakt imzalama: Bir paketin hangi kaynak, iş akışı ve derleme ortamından çıktığına ilişkin doğrulanabilir kanıt üretin.
- Depo politikaları: Paket adlarını ve güvenilir kaynakları kontrollü proxy veya özel registry ile yönetin.
A03–A08 Farkı
A03, güvenilir yazılımı nasıl geliştirdiğimiz, bağımlılıklara nasıl ulaştığımız ve nasıl dağıttığımızla ilgilenir. A08 ise indirilen veya aktarılan kod/verinin bütünlüğünün ve özgünlüğünün doğrulanmamasına odaklanır. İmzalanmamış bir güncelleme iki kategoriyi de ilgilendirebilir.
A03 Deploy Öncesi Kontrol Listesi
- [ ] Doğrudan ve geçişli bağımlılıklar SBOM ile envanterleniyor mu?
- [ ] Bağımlılıklar güvenilir kaynaklardan, sabit sürümlerle kuruluyor mu?
- [ ] Dependency confusion ve typosquatting için paket kaynağı politikaları mevcut mu?
- [ ] CI/CD token'ları en az yetkiyle ve mümkün olduğunca kısa süreli mi?
- [ ] Bağımlılık ve container imajları düzenli olarak taranıyor mu?
- [ ] Derleme kaynakları, artefakt bütünlüğü ve dağıtım onayı doğrulanıyor mu?
A04:2025 - Cryptographic Failures (Kriptografik Hatalar)
İlgili Temel CWE'ler: CWE-327 (Use of a Broken or Risky Cryptographic Algorithm), CWE-326 (Inadequate Encryption Strength), CWE-321 (Use of Hard-coded Cryptographic Key), CWE-798 (Use of Hard-coded Credentials)
Kriptografik hata yalnızca hiç şifreleme yapılmaması anlamına gelmez. Güçlü bir algoritmanın yanlış modda kullanılması, anahtarların kod içinde saklanması, rastgele sayıların öngörülebilir olması veya parolaların çok hızlı hash algoritmalarıyla tutulması da veri gizliliğini ve bütünlüğünü tehlikeye atar.
Şifreleme (encryption), hash, MAC ve parola hashleme birbirinden farklı güvenlik amaçlarına hizmet eder. Doğru algoritma, doğru amaç için kullanılmalıdır.
Sık Rastlanan Hatalar
- HTTP üzerinden hassas veri iletmek veya TLS doğrulamasını devre dışı bırakmak.
- MD5/SHA-1 gibi algoritmaları parola depolamak için kullanmak.
- AES-ECB gibi veri örüntülerini koruyan uygun olmayan şifreleme kiplerini kullanmak.
- AES-CBC gibi kipleri ayrıca doğrulama/MAC olmadan kullanmak.
- IV/nonce değerlerini yanlış üretmek veya aynı anahtarla tekrar kullanmak.
- Şifreleme anahtarlarını doğrudan kodda veya herkese açık repoda saklamak.
- Kriptografik amaçlı rastgele değerler için
randombenzeri deterministik üreteçlere güvenmek. - Anahtar rotasyonu, yedekleme ve erişim kontrolü süreçlerini tasarlamamak.
Saldırı Senaryosu: Parolaların Hızlı Hash ile Saklanması
Bir uygulama kullanıcı parolasını şu şekilde depoluyor olsun:
import hashlib
password_hash = hashlib.sha256(password.encode()).hexdigest()SHA-256 kriptografik bir hash algoritmasıdır; ancak parola saklamak için tek başına uygun değildir. Çok hızlı hesaplandığı için bir veritabanı ihlalinden sonra saldırgan büyük miktarda parola tahminini çevrimdışı deneyebilir. Aynı parolalar aynı özeti ürettiği için tuz (salt) kullanılmaması saldırıyı daha da kolaylaştırır.
Savunma: Argon2id ile Parola Hashleme
Aşağıdaki örnek argon2-cffi kütüphanesini kullanır:
python -m pip install argon2-cffifrom argon2 import PasswordHasher
from argon2.exceptions import InvalidHashError, VerificationError
hasher = PasswordHasher() # Parametreler risk ve sistem kapasitesine göre ayarlanmalıdır.
def store_password(password: str) -> str:
return hasher.hash(password)
def verify_password(stored_hash: str, candidate: str) -> bool:
try:
return hasher.verify(stored_hash, candidate)
except (VerificationError, InvalidHashError):
return FalseNeden İşe Yarar?
- Her parola için benzersiz salt: Aynı parolayı seçen kullanıcıların hash değerleri farklı olur; önceden hesaplanmış tabloların verimliliği düşer.
- Bellek maliyeti: Argon2id yalnızca işlemci zamanı değil bellek de tüketir. Paralel parola tahmini saldırılarını daha maliyetli hale getirir.
- Ayarlanabilir parametreler: Zaman, bellek ve paralellik maliyetleri altyapıya göre dengelenebilir; gerektiğinde yükseltilebilir.
- Standart kütüphane kullanımı: Salt üretimi ve hash biçimi gibi ayrıntılar elle yazılan kriptografik mantığa bırakılmaz.
Dikkat
Parola hashleme geri çözülebilir şifreleme değildir. Kimlik doğrulamada parolanın kendisi geri elde edilmez; aday parola doğrulanır. Argon2id için OWASP Password Storage Cheat Sheet'teki güncel parametre tavsiyeleri izlenmelidir.
İkinci Senaryo: AES-GCM Nonce Tekrar Kullanımı
Bir uygulama hassas verileri AES-GCM ile şifreliyor fakat her işlem için sabit nonce kullanıyorsa aynı anahtar altında nonce tekrarına yol açar. GCM'de nonce tekrar kullanımı gizlilik ve bütünlük garantilerini ciddi şekilde bozar.
Güvenli örnek:
import os
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
# Anahtar örneğin secret manager'dan okunur; kaynak koduna yazılmaz.
key = bytes.fromhex(os.environ["DATA_ENCRYPTION_KEY_HEX"])
if len(key) != 32:
raise ValueError("AES-256 anahtarı 32 bayt olmalıdır.")
aesgcm = AESGCM(key)
def encrypt(plaintext: bytes, associated_data: bytes) -> bytes:
nonce = os.urandom(12)
ciphertext = aesgcm.encrypt(nonce, plaintext, associated_data)
return nonce + ciphertext
def decrypt(blob: bytes, associated_data: bytes) -> bytes:
if len(blob) < 28:
raise ValueError("Geçersiz şifreli veri.")
nonce, ciphertext = blob[:12], blob[12:]
return aesgcm.decrypt(nonce, ciphertext, associated_data)Bu örnek her şifrelemede 96 bitlik rastgele nonce üretir ve nonce ile şifreli metni birlikte saklar. GCM'nin kimlik doğrulama etiketi ciphertext içinde taşınır. associated_data, şifrelenmeyen ancak bütünlüğü doğrulanan bağlam bilgisidir; çözme sırasında aynı değer verilmelidir.
Neden İşe Yarar?
- AEAD yaklaşımı: AES-GCM hem gizlilik hem bütünlük doğrulaması sağlar. Şifreli veride değişiklik yapılırsa çözme işlemi hata verir.
- Rastgele nonce: Tekrar ihtimalini azaltır. Ancak çok yüksek sayıda şifreleme yapılan sistemlerde rastgele nonce için çakışma olasılığı yönetilmeli ve gerekirse sayaca dayalı benzersiz nonce stratejisi kullanılmalıdır.
- Anahtar ayrı saklanır: Anahtarın uygulama kodundan ayrılması güvenlik için önemlidir; üretimde ortam değişkeni yerine erişim denetimli bir secret manager/HSM tercih edilebilir.
- Bağlamsal bütünlük:
associated_data, verinin yanlış kullanıcı veya işlem bağlamında yeniden kullanılmasını tespit etmeye yardımcı olur; erişim kontrolünün yerine geçmez.
TLS Güvenliği ve Anahtar Yaşam Döngüsü
Kriptografi yalnızca veritabanıyla sınırlı değildir. Hassas trafik HTTPS/TLS üzerinden taşınmalı, sunucu sertifikaları doğrulanmalı ve istemcilerde verify=False gibi ayarlar kullanılmamalıdır. Anahtarların kim tarafından üretildiği, hangi servislerin kullanabildiği, nasıl yenilendiği ve ihlal durumunda nasıl iptal edileceği dokümante edilmelidir.
A04 Deploy Öncesi Kontrol Listesi
- [ ] Parolalar Argon2id, scrypt veya uygun şekilde yapılandırılmış bcrypt gibi parola hash algoritmalarıyla korunuyor mu?
- [ ] Gizli veriler için standart, kimlik doğrulamalı şifreleme (AEAD) tercih ediliyor mu?
- [ ] Nonce/IV tekrar kullanımını önleyen bir strateji var mı?
- [ ] Anahtarlar koddan ayrılmış ve erişim politikasıyla korunuyor mu?
- [ ] TLS sertifika doğrulaması ve güvenli protokol yapılandırması aktif mi?
- [ ] Anahtar yenileme, yedekleme, iptal ve ihlal müdahalesi süreçleri tanımlı mı?
A05:2025 - Injection (Enjeksiyon Zafiyetleri)
İlgili Temel CWE'ler: CWE-89 (SQL Injection), CWE-79 (Cross-site Scripting - XSS), CWE-78 (OS Command Injection), CWE-94 (Code Injection)
Enjeksiyon zafiyetlerinin tamamı aynı temel hatadan doğar: uygulama, kullanıcıdan gelen veriyi bir yorumlayıcıya komutun parçası olarak teslim eder. Bu yorumlayıcı bir SQL motoru, bir işletim sistemi kabuğu, bir şablon motoru ya da bir URL çözümleyici olabilir. Girdi bu sınırı aştığı anda kullanıcı artık veri girmiyor, kod yazıyordur.
OWASP Top 10:2025 listesinde A05 sırasında yer alan bu kategoriye karşı savunmanın temel ilkesi nettir: veri veri olarak, kod kod olarak kalmalıdır.
Enjeksiyon Türleri
Enjeksiyon tek bir saldırı değil, hedef aldığı yorumlayıcıya göre farklılaşan bir saldırı ailesidir.
SQL Injection
En bilinen ve en yaygın enjeksiyon türüdür. Kullanıcı girdisi SQL sorgusunun söz dizimine karıştığında saldırgan sorgunun mantığını değiştirebilir. Saldırganın sonucu nasıl elde ettiğine göre üç ana gruba ayrılır:
- In-band SQL Injection: Saldırgan sonucu doğrudan uygulamanın yanıtında görür.
- Error-based: Veritabanı hata mesajları üzerinden bilgi sızdırılır.
- Union-based:
UNION SELECTile başka tablolardaki veriler mevcut sorgunun sonucuna eklenir.
- Blind (Kör) SQL Injection: Uygulama veri döndürmez, ancak davranışı değişir.
- Boolean-based: Yanıtın doğru/yanlış olmasına göre karakter karakter çıkarım yapılır.
- Time-based:
SLEEP()gibi fonksiyonlarla yanıt süresi üzerinden çıkarım yapılır.
- Out-of-band SQL Injection: Veri, DNS veya HTTP gibi harici bir kanal üzerinden saldırganın sunucusuna gönderilir.
NoSQL Injection
MongoDB gibi doküman tabanlı veritabanlarında sorgu operatörlerinin kullanıcı girdisi üzerinden enjekte edilmesidir. Örneğin parola alanına metin yerine {"$ne": null} nesnesi gönderilerek "parolası boş olmayan herhangi bir kullanıcı" koşulu oluşturulur ve kimlik doğrulama atlatılır.
OS Command Injection
Kullanıcı girdisinin işletim sistemi kabuğunda çalıştırılan bir komuta eklenmesidir. ping komutuna hedef adres olarak 8.8.8.8; cat /etc/passwd gönderilmesi, kabuğun ikinci komutu da çalıştırmasına yol açar.
LDAP Injection
Dizin servislerine gönderilen LDAP filtrelerinin manipüle edilmesidir. *)(uid=*))(|(uid=* gibi girdilerle filtre mantığı bozularak kimlik doğrulama atlatılabilir veya dizindeki kayıtlar listelenebilir.
Server-Side Template Injection (SSTI)
Kullanıcı girdisinin Jinja2, Twig veya Freemarker gibi bir şablon motorunda şablon olarak işlenmesidir. 49 girdisinin sayfada 49 olarak görünmesi zafiyetin klasik işaretidir ve çoğu durumda sunucuda uzaktan kod çalıştırmaya (RCE) kadar ilerler.
Cross-Site Scripting (XSS)
OWASP sınıflandırmasında enjeksiyon ailesine dahil edilen XSS, bu kez hedef yorumlayıcının kullanıcının tarayıcısı olduğu bir enjeksiyondur. Yansıtılan (Reflected), depolanan (Stored) ve DOM tabanlı olmak üzere üç türü vardır.
Türlerin Karşılaştırması
| Tür | Hedef Yorumlayıcı | Örnek Girdi | Temel Savunma |
|---|---|---|---|
| SQL Injection | İlişkisel veritabanı | ' OR '1'='1' -- | Parametrik sorgular |
| NoSQL Injection | Doküman veritabanı | {"$ne": null} | Tip doğrulama, operatör filtreleme |
| OS Command Injection | İşletim sistemi kabuğu | ; cat /etc/passwd | Kabuk kullanmadan argüman listesi |
| LDAP Injection | Dizin servisi | *)(uid=*))(|(uid=* | Filtre karakterlerinin kaçışlanması |
| SSTI | Şablon motoru | 49 | Girdiyi şablon olarak işlememek |
| XSS | Tarayıcı | <script>alert(1)</script> | Bağlama duyarlı çıktı kodlama, CSP |
Aşağıda bu ailenin iki kritik örneği, saldırı senaryosu ve kod düzeyindeki savunmasıyla birlikte ele alınmaktadır.
Boolean-Based Blind SQL Injection
Saldırı Senaryosu
Bir kullanıcı adının sistemde kayıtlı olup olmadığını kontrol eden basit bir API uç noktası düşünün. Yanıt olarak yalnızca {"exists": true} ya da {"exists": false} döner. İlk bakışta zararsız görünür.
Arka planda sorgu şu şekilde oluşturuluyorsa durum değişir:
query = f"SELECT id FROM users WHERE username = '{username}'"
cursor.execute(query)Saldırgan kullanıcı adı alanına şu girdiyi gönderir:
admin' AND SUBSTR((SELECT password FROM users LIMIT 1),1,1)='a' --Yanıt true dönerse parolanın ilk karakteri "a"dır. false dönerse sıradaki karaktere geçilir. Ekranda hiçbir veri görünmez, ancak her istek tek bir soruyu yanıtlar ve birkaç yüz otomatik istekle parola karakter karakter ortaya çıkar. Boolean-based blind SQL injection'ın özü budur: uygulamanın veri göstermesine gerek yoktur, "evet" ve "hayır" demesi yeterlidir.
Savunma: Parametrik Sorgular
query = "SELECT id FROM users WHERE username = ?"
cursor.execute(query, (username,))
user = cursor.fetchone()
return jsonify({"exists": bool(user)})Neden İşe Yarar?
Parametrik sorguda (Prepared Statement) SQL şablonu veritabanına girdiden önce gönderilir, ayrıştırılır ve sorgu ağacı oluşturulur. username değeri bu ağaç kurulduktan sonra yerine bağlanır (parameter binding). Bu yüzden girdinin içinde tırnak işareti, AND/OR operatörleri, yorum satırı veya alt sorgu bulunması bir şey değiştirmez. Motor bunların hepsini tek parça bir metin (literal string) olarak değerlendirir. Sorgunun mantıksal yapısı değişemediği için saldırganın çıkarım yapabileceği bir soru da kalmaz.
Tamamlayıcı Önlemler
- Tablo ve sütun adları parametre olarak bağlanamaz. Sorguda dinamik bir tanımlayıcı gerekiyorsa sabit bir beyaz liste (allowlist) kullanılmalıdır.
- Uygulamanın veritabanı kullanıcısı en az yetki ilkesine (Least Privilege) göre yapılandırılmalıdır.
- Hata mesajları genel tutulmalı, veritabanı hata çıktıları istemciye yansıtılmamalıdır.
SSRF Üzerinden Yerel Dosya Okuma (SSRF -> LFI)
Saldırı Senaryosu
A01 bölümünde SSRF'in iç servislere erişim riskine değinmiştik; burada ise olayı URI şeması enjeksiyonu boyutuyla ele alacağız. Birçok uygulamada kullanıcının verdiği bir URL'nin içeriğini sunucu üzerinden çeken özellikler bulunur: bağlantı önizlemeleri, webhook testleri, uzak dosya içe aktarma gibi. Python'da bu genellikle şu kadar basittir:
response = urllib.request.urlopen(url, timeout=3)Sorun şu ki urllib yalnızca web sayfası açmaz, file:// ve ftp:// gibi şemaları da işler. Saldırganın gönderebileceği bazı girdiler:
| Girdi | Sonuç |
|---|---|
file:///etc/passwd | Sunucudaki kullanıcı listesi okunur. |
file:///C:/Windows/win.ini | Windows yapılandırma dosyası okunur. |
http://127.0.0.1:8080/admin | Dışarıya kapalı iç servislere erişilir. |
http://169.254.169.254/latest/meta-data/ | Bulut ortamında sunucunun kimlik bilgileri tehlikeye girer. |
Sunucu artık saldırgan için hem bir dosya okuyucu hem de iç ağa açılan bir kapıdır. SSRF'nin LFI'a dönüştüğü nokta budur.
Savunma: Katmanlı URL Doğrulama
import ipaddress, socket, urllib.error, urllib.request
from urllib.parse import urlparse
ALLOWED_SCHEMES = {"http", "https"}
def is_safe_url(url):
parsed = urlparse(url)
if parsed.scheme.lower() not in ALLOWED_SCHEMES or not parsed.hostname:
return False
try:
addresses = socket.getaddrinfo(parsed.hostname, None)
except socket.gaierror:
return False
for addr in addresses:
ip = ipaddress.ip_address(addr[4][0])
if (ip.is_private or ip.is_loopback or ip.is_link_local
or ip.is_reserved or ip.is_multicast or ip.is_unspecified):
return False
return True
class NoRedirect(urllib.request.HTTPRedirectHandler):
def redirect_request(self, *args, **kwargs):
return None
if not is_safe_url(url):
return jsonify({"error": "Erişim Engellendi: Yalnızca harici HTTP/HTTPS adreslerine izin verilir."}), 400
opener = urllib.request.build_opener(NoRedirect)
try:
response = opener.open(url, timeout=3)
except urllib.error.HTTPError as exc:
if 300 <= exc.code < 400:
return jsonify({"error": "Yönlendirmelere izin verilmez."}), 400
return jsonify({"error": "Uzak sunucu hata döndürdü."}), 502
except urllib.error.URLError:
return jsonify({"error": "Uzak sunucuya ulaşılamadı."}), 502
content = response.read(1_000_000).decode("utf-8", errors="replace")
return jsonify({"status": "success", "data": content})Neden İşe Yarar?
- Şema beyaz listesi: URL
urlparseile bileşenlerine ayrılır ve yalnızcahttpilehttpsşemalarına izin verilir.file://,gopher://,dict://gibi talepler sunucu tek bir istek bile üretmeden400 Bad Requestile reddedilir. Basit birstartswith()kontrolü yerine gerçek ayrıştırma kullanmak, büyük/küçük harf farklılıkları gibi atlatma denemelerine karşı daha sağlamdır. - Hedef IP kontrolü: Yalnızca şemayı kontrol etmek yeterli değildir, çünkü
http://127.0.0.1/adminde geçerli bir HTTP adresidir. Bu nedenle alan adı DNS üzerinden çözümlenir ve dönen her IP adresi denetlenir. Loopback, özel ağ blokları (RFC 1918) ve bulut metadata servisinin bulunduğu link-local aralığa yönelen istekler reddedilir. - Yönlendirme kontrolü: Saldırgan, doğrulamadan geçen harici bir sunucu kurup oradan
302yanıtıyla isteği127.0.0.1adresine yönlendirebilir. Otomatik yönlendirmeyi devre dışı bırakanNoRedirectsınıfı bu atlatma yolunu kapatır. - Kaynak sınırı: Zaman aşımı ve okuma boyutu limiti, özelliğin kaynak tüketme saldırılarında kötüye kullanılmasını önler.
Not
Doğrulama ile asıl bağlantı arasında DNS yanıtının değiştirilmesi (DNS Rebinding) teorik olarak mümkündür. Üretim ortamlarında bu kontrol, ağ katmanındaki çıkış (egress) filtreleriyle desteklenmelidir. Ayrıca OWASP Top 10:2025'te SSRF, Broken Access Control (A01) kategorisine dahil edilmiştir. Burada saldırının özü kullanıcı kontrolündeki URI şemasının enjeksiyonu olduğu için enjeksiyon başlığı altında ele alınmıştır.
A05 Deploy Öncesi Kontrol Listesi
- [ ] Tüm veritabanı sorgularında metin birleştirme yerine parametrik sorgular (Prepared Statements) kullanılıyor mu?
- [ ] Dinamik tablo/sütun adları ve URL şemaları katı bir beyaz listeyle (allowlist) sınırlandırıldı mı?
- [ ] İşletim sistemi komutları kabuk (
shell=True) kullanılmadan argüman dizisi olarak çalıştırılıyor mu? - [ ] Kullanıcı girdileri şablon motorlarına doğrudan şablon kodu olarak verilmekten kaçınılıyor ve çıktılar bağlama duyarlı biçimde kodlanıyor mu?
A06:2025 - Insecure Design (Güvensiz Tasarım)
İlgili Temel CWE'ler: CWE-840 (Business Logic Errors), CWE-362 (Concurrent Execution using Shared Resource with Improper Synchronization / Race Condition), CWE-602 (Client-Side Enforcement of Server-Side Security)
Güvensiz tasarım diğer zafiyet kategorilerinden farklıdır. Ortada hatalı yazılmış bir satır olmayabilir. Kod tam olarak yazıldığı gibi çalışır, sorun neyin yazılması gerektiğinin baştan düşünülmemiş olmasıdır. Tehdit modellemesi yapılmamış, iş kuralları sınırlandırılmamış, istemciye gereğinden fazla güvenilmiştir. Bu nedenle güvensiz tasarım, kusursuz bir implementasyonla bile düzeltilemez. Çözüm tasarımın kendisini değiştirmektir.
OWASP Top 10:2025 listesinde A06 sırasında yer alan bu kategorinin temel ilkesi şudur: İstemciden gelen her veri, saldırgandan gelmiş gibi ele alınmalıdır. Formdaki kısıtlamalar kullanıcı deneyimi içindir, güvenlik için değil.
Güvensiz Tasarımın Yaygın Biçimleri
İş Mantığı Hataları (Business Logic Flaws)
Uygulamanın teknik olarak doğru çalışan, ancak iş kurallarının beklenmeyen girdilerle kötüye kullanılabildiği durumlardır. Negatif adetle sipariş vermek, bir indirim kuponunu birden fazla kez uygulamak veya ödeme adımını atlayıp doğrudan sipariş onay sayfasına gitmek tipik örneklerdir.
İstemci Tarafı Kontrollere Güvenmek
Fiyat, adet, rol veya yetki gibi kritik değerlerin yalnızca tarayıcıda doğrulanmasıdır. HTML kısıtlamaları ve JavaScript doğrulamaları bir proxy aracıyla saniyeler içinde atlatılabilir.
Tasarımda Eksik Nesne Yetkilendirmesi (IDOR / BOLA)
A01 bölümünde kod düzeyinde incelediğimiz nesne yetkilendirmesinin, mimari tasarım aşamasında göz ardı edilerek yalnızca URL veya ID gizlemeye dayandırılmasıdır.
Yarış Durumları (Race Conditions)
Kontrol ve işlem adımlarının ayrı ayrı yapıldığı akışlarda, eş zamanlı isteklerle aradaki zaman penceresinin istismar edilmesidir (TOCTOU: Time-of-Check to Time-of-Use). Aynı bakiyenin birden fazla kez harcanması bunun klasik örneğidir.
Oran Sınırlamasının Olmaması
Giriş, parola sıfırlama veya SMS doğrulama gibi akışlarda deneme sayısının sınırlandırılmamasıdır. Bu, kaba kuvvet saldırılarını ve kaynak tüketimini mümkün kılar.
Gizleme Yoluyla Güvenlik (Security through Obscurity)
Gerçek bir kontrol mekanizması yerine bilgiyi saklamaya güvenmektir. Kimlikleri hash'lemek, gizli bir yönetim paneli adresi kullanmak veya API uç noktalarını belgelememek bu yaklaşımın örnekleridir.
Aşağıda bu biçimlerin iki somut örneği ele alınmaktadır.
İş Mantığı Hatası: Negatif Adet ile Bakiye Manipülasyonu
Saldırı Senaryosu
Bir e-ticaret sayfasında adet alanının HTML'de min="1" ile sınırlandığını düşünün. Tarayıcı arayüzünden negatif bir değer girmek mümkün değildir. Ancak Burp Suite gibi bir proxy ile isteği yakalayıp gövdeyi {"qty": -10} olarak değiştirmek birkaç saniye sürer.
Sunucu tarafındaki hesaplama şöyleyse:
total_cost = unit_price * qty # 50 * -10 = -500
current_balance -= total_cost # 100 - (-500) = 600Saldırgan ürün satın almamış, üstüne bakiyesini artırmıştır. Bu bir kodlama hatası değil, sınırları hiç tanımlanmamış bir iş mantığıdır.
Savunma: Sunucu Taraflı Doğrulama ve Atomik İşlem
MAX_QTY = 100
qty = request.json.get("qty", 1)
if type(qty) is not int or not 1 <= qty <= MAX_QTY:
return jsonify({"error": "Geçersiz sipariş: Adet 1 ile 100 arasında pozitif bir tamsayı olmalıdır."}), 400
unit_price = get_product_price(product_id) # Fiyat istemciden değil, veritabanından alınır
total_cost = unit_price * qty
cur = conn.execute(
"UPDATE wallets SET balance = balance - ? WHERE user_id = ? AND balance >= ?",
(total_cost, current_user_id, total_cost),
)
if cur.rowcount == 0:
conn.rollback()
return jsonify({"error": "Yetersiz bakiye."}), 400
conn.commit()Neden İşe Yarar?
- Tip ve sınır kontrolü: İlk akla gelen çözüm
int(qty)ile dönüştürüpqty <= 0kontrolü yapmaktır. Ancakint()fonksiyonu2.7değerini sessizce2yapar vetruedeğerini1olarak kabul eder. Bu nedenletype(qty) is not intile tip katı şekilde denetlenir, ardından değerin 1 ile üst sınır arasında olduğu doğrulanır. Kurala uymayan istek hesaplamaya hiç girmez. - Sunucu taraflı fiyat: Birim fiyat asla istek gövdesinden okunmaz. Aksi halde saldırgan adet yerine doğrudan fiyatı manipüle eder (Price Tampering).
- Atomik bakiye işlemi: Bakiye önce okunup kontrol edilir, sonra ayrı bir adımda düşülürse aynı anda gönderilen 20 istek aynı bakiyeyi 20 kez harcayabilir. Kontrol ile düşümü tek bir
UPDATE ... WHERE balance >= ?sorgusunda birleştirmek bu zaman penceresini tamamen kapatır.
Hashed IDOR: Gizlemek Korumak Değildir
Saldırı Senaryosu
Rapor adreslerinin /report/1, /report/2 gibi tahmin edilebilir olmaması için kimliklerin MD5 ile özetlendiğini düşünün. URL'ler artık /report/c4ca4238a0b923820dcc509a6f75849b biçimindedir ve karmaşık görünür.
Ancak MD5 anahtarsız ve deterministik bir fonksiyondur. Saldırgan MD5("1"), MD5("2"), MD5("3") değerlerini saniyeler içinde üretip sırayla deneyebilir. Sunucu raporun kime ait olduğunu kontrol etmediği sürece her birini teslim eder. Bu, kapıyı kilitlemek yerine kapının yerini saklamaya çalışmaktır.
Savunma: Nesne Düzeyinde Erişim Denetimi
current_user_id = session["user_id"] # Kimlik istekten değil, sunucu oturumundan alınır
report = db.execute(
"SELECT content FROM reports WHERE report_hash = ? AND owner_id = ?",
(report_hash, current_user_id),
).fetchone()
if report is None:
# A01'deki gibi: "rapor yok" ve "erişim yok" aynı yanıtı alır
return jsonify({"error": "Rapor bulunamadı."}), 404
return jsonify({"data": report["content"]})Neden İşe Yarar?
Nesne düzeyinde erişim denetimi (Object-Level Access Control) ile saldırgan geçerli bir özet bilse bile, raporun sahibi oturumdaki kullanıcı değilse 404 Not Found yanıtı alır; böylece raporun var olup olmadığı da sızdırılmaz.
Kritik bir detay: current_user_id değeri istek parametresinden değil, sunucu tarafında doğrulanmış oturumdan okunmalıdır. Kimlik istemciden alınırsa saldırgan raporun ID'sini değil kendi ID'sini değiştirir ve kontrol anlamsız kalır.
Tamamlayıcı Önlemler
- Ardışık ID'lerin özetleri yerine
uuid.uuid4()gibi rastgele tanımlayıcılar kullanmak numaralandırmayı (enumeration) zorlaştırır. Ancak bu bir derinlemesine savunma (Defense in Depth) katmanıdır ve yetkilendirme kontrolünün yerini tutmaz.
A06 Deploy Öncesi Kontrol Listesi
- [ ] Kritik iş mantığı adımları için tehdit modellemesi yapıldı ve sınır değerleri (negatif sayı, maksimum adet vb.) sunucuda doğrulandı mı?
- [ ] Fiyat, indirim oranı ve kullanıcı kimliği gibi değerler istemci yerine doğrudan sunucu/veritabanı kaynağından alınıyor mu?
- [ ] Bakiye düşümü ve kupon kullanımı gibi eş zamanlılığa duyarlı işlemler atomik sorgularla veya veritabanı kilitleriyle yarış durumlarına (TOCTOU) karşı korundu mu?
- [ ] Güvenlik modeli ID gizleme (obscurity) yerine açık sahiplik ve yetki denetimine dayandırıldı mı?
A07:2025 - Authentication Failures (Kimlik Doğrulama Hataları)
İlgili Temel CWE'ler: CWE-287 (Improper Authentication), CWE-307 (Improper Restriction of Excessive Authentication Attempts), CWE-384 (Session Fixation), CWE-521 (Weak Password Requirements)
Kimlik doğrulama, uygulamanın ön kapısıdır. Kullanıcının kim olduğunu doğrulayan ve bu kimliği oturum boyunca taşıyan mekanizmalardaki her zayıflık, saldırganın başka bir kullanıcının yerine geçmesine yol açar. Kapı ne kadar sağlam olursa olsun, anahtar paspasın altındaysa bir anlamı kalmaz.
OWASP Top 10:2025 listesinde A07 sırasında yer alan bu kategori; parola politikalarından token imzalama anahtarlarına, oturum yönetiminden çerez yapılandırmasına kadar geniş bir alanı kapsar.
Kimlik Doğrulama Hatalarının Türleri
Kaba Kuvvet ve Credential Stuffing
Giriş uç noktasında deneme sınırı olmadığında saldırgan yaygın parolaları (brute force) veya başka sitelerden sızdırılmış kullanıcı adı-parola çiftlerini (credential stuffing) otomatik olarak dener.
Zayıf Parola Depolama
Parolaların düz metin olarak veya MD5, SHA-1 gibi hızlı ve tuzsuz (unsalted) özet fonksiyonlarıyla saklanmasıdır. Veritabanı sızdığında bu parolalar gökkuşağı tabloları (Rainbow Tables) veya GPU tabanlı kırma araçlarıyla kısa sürede çözülür.
Zayıf Token İmzalama Anahtarları
JWT gibi imzalı token'larda tahmin edilebilir veya kısa bir anahtar kullanılmasıdır. Anahtar kırıldığında saldırgan istediği kimlik ve rolle geçerli token üretebilir.
Token Doğrulama Zafiyetleri
Token'ın imzasının hiç doğrulanmaması, alg: none değerinin kabul edilmesi veya algoritma karışıklığı (Algorithm Confusion) ile açık anahtarın HMAC sırrı olarak kullanılması bu gruptadır.
Oturum Yönetimi Hataları
Girişten sonra oturum kimliğinin yenilenmemesi (Session Fixation), çıkış yapıldığında oturumun sunucu tarafında sonlandırılmaması ve token'ların süresiz geçerli olması bu türün örnekleridir.
Güvensiz Çerez Yapılandırması
Oturum çerezinde HttpOnly, Secure ve SameSite bayraklarının eksik olmasıdır. Bu durum XSS ile token hırsızlığına, ağ dinlemeye ve CSRF saldırılarına zemin hazırlar.
Çok Faktörlü Doğrulamanın (MFA) Olmaması
Kritik hesaplarda yalnızca parolaya güvenilmesidir. Parola ele geçirildiğinde saldırganın önünde başka bir engel kalmaz.
Aşağıda bu türlerden zayıf JWT anahtarı ve güvensiz çerez yapılandırması birlikte ele alınmaktadır, çünkü gerçek saldırılarda çoğu zaman birlikte görülürler.
Zayıf JWT Anahtarı ve Eksik Çerez Bayrakları
Saldırı Senaryosu
Sık rastlanan bir yapılandırma:
SECRET_KEY = "secret123"Saldırgan önce normal bir kullanıcı olarak giriş yapar ve kendi token'ını alır. Elinde artık imzalı bir örnek vardır. Bu token'ı Hashcat'e (-m 16500) verip bir sözlük dosyasıyla çevrimdışı kırmaya başlar. secret123 gibi bir anahtar saniyeler içinde bulunur.
Sonrası kısadır: payload içindeki "role": "user" değeri "role": "admin" yapılır, bulunan anahtarla yeniden imzalanır ve saldırgan yönetici yetkisi kazanır. Çerezde HttpOnly bayrağı da yoksa, uygulamadaki herhangi bir XSS açığı diğer kullanıcıların token'larını document.cookie üzerinden çalmak için yeterlidir.
Savunma: Güçlü Anahtar, Katı Doğrulama, Güvenli Çerez
import os, datetime, jwt
# Anahtar bir kez secrets.token_hex(32) ile üretilir ve ortam değişkeninde saklanır
SECRET_KEY = os.environ["JWT_SECRET_KEY"]
now = datetime.datetime.now(datetime.timezone.utc)
payload = {
"sub": str(user_id),
"role": user_role, # Rol veritabanından alınır
"iat": now,
"exp": now + datetime.timedelta(minutes=30),
}
token = jwt.encode(payload, SECRET_KEY, algorithm="HS256")
response = make_response(jsonify({"msg": "Giriş başarılı"}))
response.set_cookie(
"auth_token",
token,
httponly=True,
secure=True,
samesite="Lax",
max_age=1800,
)Doğrulama tarafında algoritma açıkça sabitlenir:
claims = jwt.decode(token, SECRET_KEY, algorithms=["HS256"])Neden İşe Yarar?
- 256 bit entropi:
secretsmodülü işletim sisteminin kriptografik olarak güvenli rastgele sayı üretecini (CSPRNG) kullanır.secrets.token_hex(32)32 baytlık, yani 256 bitlik bir anahtar üretir.2^256olasılıklı bir anahtar uzayını kaba kuvvetle taramak hesaplama açısından pratik olarak imkânsızdır ve Hashcat'in deneyebileceği bir sözlük yoktur. - Anahtar kodda yaşamaz: Sık yapılan bir hata,
SECRET_KEY = secrets.token_hex(32)satırını doğrudan uygulamanın içine yazmaktır. Bu durumda sunucu her yeniden başladığında yeni bir anahtar üretilir ve tüm oturumlar düşer. Birden fazla worker ile çalışan uygulamalarda her worker farklı bir anahtar kullanır ve oturumlar rastgele geçersizleşir. Doğru yaklaşım, anahtarı bir kez üretip ortam değişkeninde veya bir secret manager'da saklamaktır. - Algoritma sabitleme:
algorithms=["HS256"]parametresi olmadan bazı yapılandırmalar token başlığındakialgalanına güvenebilir. Saldırgan bu alanınoneyaparak imzasız token gönderebilir ya da algoritma karışıklığı saldırısı deneyebilir. Kabul edilen algoritmayı açıkça belirtmek bu yolu kapatır. - Sınırlı token ömrü:
expalanı sayesinde çalınmış bir token sonsuza kadar geçerli kalmaz. Süre dolduğundaExpiredSignatureErrorile reddedilir. - HttpOnly: Çerez JavaScript'in erişimine kapatılır. Uygulamada bir XSS açığı bulunsa bile saldırgan
document.cookieile token'ı okuyup dışarı gönderemez. Ancak HttpOnly token hırsızlığını engeller, XSS'in kendisini engellemez. Saldırgan yine kullanıcının oturumu içinde işlem yapabilir. Bu nedenle çıktı kodlama (Output Encoding) ve Content Security Policy (CSP) ile birlikte uygulanmalıdır. - Secure: Çerez yalnızca HTTPS bağlantıları üzerinden gönderilir. Şifrelenmemiş trafikte ağı dinleyen birinin token'ı yakalaması mümkün olmaz.
- SameSite=Lax: Başka bir siteden tetiklenen POST, PUT, DELETE istekleri ve arka plan istekleri çerezi taşımaz. Bu, CSRF saldırılarının büyük bölümünü etkisiz bırakır. Lax modunda üst düzey GET gezinmelerinde çerez gönderilmeye devam eder, bu yüzden durum değiştiren hiçbir işlem GET metoduyla tasarlanmamalıdır. Daha katı bir politika gerektiğinde
SameSite=Stricttercih edilebilir.
Tamamlayıcı Önlemler
- Parolalar bcrypt veya Argon2 gibi yavaş ve tuzlu (salted) fonksiyonlarla saklanmalıdır.
- Giriş uç noktasına oran sınırlaması (Rate Limiting) uygulanarak kaba kuvvet ve credential stuffing saldırıları yavaşlatılmalıdır.
- Başarılı girişten sonra oturum kimliği yenilenmeli, çıkışta oturum sunucu tarafında geçersiz kılınmalıdır.
- Hatalı girişlerde "kullanıcı bulunamadı" ile "parola yanlış" ayrımı yapılmamalı, genel bir hata mesajı döndürülerek kullanıcı adı numaralandırmasının önüne geçilmelidir.
- Yönetici hesapları başta olmak üzere kritik hesaplarda çok faktörlü doğrulama zorunlu tutulmalıdır.
A07 Deploy Öncesi Kontrol Listesi
- [ ] Giriş ve parola sıfırlama uç noktalarında kaba kuvvet saldırılarına karşı oran sınırlaması (Rate Limiting) ve MFA aktif mi?
- [ ] JWT imzalama anahtarları yüksek entropili (en az 256 bit) olarak üretilip kod dışında (Secret Manager / ortam değişkeni) saklanıyor mu?
- [ ] Token doğrulamasında kabul edilen algoritmalar açıkça sabitlendi ve
expsüresi tanımlandı mı? - [ ] Oturum çerezlerinde
HttpOnly,SecureveSameSitebayrakları eksiksiz yapılandırıldı mı?
A05–A07 Özet Tablosu
| Zafiyet | Kök Neden | Uygulanan Savunma | Engellenen Saldırı |
|---|---|---|---|
| Boolean-Based Blind SQL Injection | Metin birleştirme ile sorgu oluşturma | Parametrik sorgular | Doğru/yanlış çıkarımıyla veri sızdırma |
| SSRF -> LFI | Doğrulanmamış URL girdisi | Şema beyaz listesi, iç IP engelleme, yönlendirme kontrolü | Yerel dosya okuma, iç ağ ve metadata erişimi |
| İş Mantığı Hatası | İstemci taraflı sınır kontrolü | Sunucu taraflı tip/sınır doğrulaması, atomik işlem | Negatif adetle bakiye artırma, yarış durumu |
| Hashed IDOR | Gizlemeye dayalı erişim modeli | Nesne düzeyinde sahiplik kontrolü | Başka kullanıcıların verilerine erişim |
| Zayıf JWT Anahtarı ve Eksik Çerez Bayrakları | Düşük entropili anahtar, eksik bayraklar | 256 bit anahtar, algoritma sabitleme, exp, HttpOnly/Secure/SameSite | Token kırma ve rol yükseltme, XSS ile token çalma, CSRF |
A08:2025 - Software or Data Integrity Failures (Yazılım veya Veri Bütünlüğü İhlalleri)
İlgili Temel CWE'ler: CWE-502 (Deserialization of Untrusted Data), CWE-494 (Download of Code Without Integrity Check), CWE-345 (Insufficient Verification of Data Authenticity)
Modern uygulamalar tek parça (monolitik) bir kod yığınından ibaret değildir; dışarıdan çekilen betikler, container imajları, otomatik güncellemeler ve servisler arası dolaşan serileştirilmiş verilerle sürekli etkileşim halindedir. A08:2025, uygulamanın bu dış artefaktları veya verileri güven sınırlarından (trust boundaries) geçirirken değiştirilip değiştirilmediğini kriptografik olarak doğrulamaması sonucunda ortaya çıkan zafiyetleri kapsar.
Kapsam Notu: A03:2025 ve A08:2025 Ayrımı
A03 bölümünde incelediğimiz üzere A03:2025 - Software Supply Chain Failures tedarik zincirinin genelini (bileşen envanteri, SBOM yönetimi, geçişli bağımlılıkların takibi, CI/CD ve geliştirici araçlarının sıkılaştırılması) kapsar. A08:2025 ise daha alt seviyede; kodun, dağıtılan artefaktların, güncellemelerin ve uygulama içi verilerin bütünlüğünün doğrulanmasına odaklanır. Pratikte iki kategori iç içedir: örneğin imzasız bir artefakt hem bir tedarik zinciri hem de bir bütünlük sorunudur.
Güven Sınırları Nerede Kırılıyor?
Sahada bütünlük ihlallerinin genellikle dört ana noktada patlak verdiğini görüyoruz:
- Doğrulanmamış Harici Betikler ve CDN Kullanımı (
CWE-494): Web sayfalarına veya dağıtım hatlarına dış kaynaklardan dahil edilen betiklerin özet (hash) kontrolü yapılmadığında, karşı sunucuya veya alan adına sızan bir saldırgan doğrudan sizin istemcilerinizde kod çalıştırabilir. - Güvensiz Deserialization - Seriden Çıkarma (
CWE-502): İstemciden veya harici bir servisten gelen serileştirilmiş nesnelerin (Pythonpickle, PHPunserialize(), JavaObjectInputStream, .NETBinaryFormatter) hiçbir bütünlük ve tip kontrolünden geçmeden doğrudan bellekte nesneye dönüştürülmesi en tehlikeli senaryolardan biridir. - İmzasız Güncellemeler ve Container İmajları: Otomasyon hattı (pipeline) sonunda üretilen paketlerin veya Kubernetes ortamına çekilen imajların dijital imzaları kontrol edilmediğinde, dağıtım aşamasında araya giren bir saldırgan imajı kendi zararlı sürümüyle değiştirebilir.
- İstemci Tarafında Korunmasız Durum Verisi (
CWE-345): Çerezlerde (cookies), gizli form alanlarında veya URL parametrelerinde tutulan kritik verilerin (örneğin kullanıcı rolü veya sepet tutarı) sunucu tarafında bütünlük imzası aranmadan kabul edilmesidir.
Uyarı
Güvensiz Deserialization zafiyetleri, kullanılan programlama diline ve sistemdeki mevcut sınıf zincirlerine (gadget chains) bağlı olarak çoğu zaman Uzaktan Kod Çalıştırma (Remote Code Execution - RCE), ayrıca yetki atlatma ve Hizmet Reddi (DoS) ile sonuçlanır.
Sahadan İki Vaka: Codecov (2021) ve Polyfill.io (2024)
Bütünlük kontrolünün neden "olsa iyi olur" değil de zorunlu bir adım olduğunu yakın geçmişteki iki büyük olay çok net özetliyor:
- Eksik Bütünlük Doğrulaması – Codecov Bash Uploader (2021): 31 Ocak – 1 Nisan 2021 tarihleri arasında saldırganlar, CI hatlarında test kapsam raporu yüklemek için kullanılan
bash uploaderbetiğini dönemsel olarak değiştirdi. Betiğe eklenen tek bir satır, CI ortamındaki ortam değişkenlerini vegit remotebilgisini saldırganların sunucusuna gönderiyordu. Betiği her seferinde doğrudan indirip özetini doğrulamayan hatlar; kimlik bilgilerinin, token'ların ve anahtarların ifşa olması riskiyle karşı karşıya kaldı. Olay, bir müşterinin indirdiği betiğin özetini (shasum) GitHub'daki orijinal değerle karşılaştırıp uyuşmazlığı fark etmesiyle ortaya çıktı. Vaka, basit bir özet doğrulamasının ne kadar ucuz ama etkili bir kontrol olduğunu kanıtlar. - Dinamik Betikler ve SRI Sınırı – Polyfill.io CDN Saldırısı (2024): Şubat 2024'te popüler
polyfill.ioservisinin alan adı yeni bir sahibe geçti; 25 Haziran 2024'te ise Sansec, bu alan adı üzerinden 100.000'den fazla sitede zararlı kod dağıtıldığını duyurdu. Siteler, eski tarayıcı uyumluluğu için doğrudan<script src="https://cdn.polyfill.io/...">etiketiyle bu servisi çağırıyordu. Üstelik servis, gelen tarayıcınınUser-Agentbaşlığına göre dinamik JavaScript ürettiği için sabit bir dosya özeti yoktu; dolayısıyla SRI (Subresource Integrity) kullanılması teknik olarak mümkün değildi. Bu olay bize dinamik üçüncü parti betiklerde SRI'nin koruma sağlayamadığını; bu tür kütüphaneleri kendi altyapımızda sabit sürümle barındırmanın (self-hosting), gereksizse tamamen kaldırmanın ve bağımlı olduğumuz alan adlarının sahiplik değişimlerini izlemenin şart olduğunu gösterdi.
Bütünlüğü Nasıl Doğrularız? (Savunma Yöntemleri)
flowchart LR
subgraph CICD["Dağıtım Hattı (Artefakt Bütünlüğü)"]
A["Derlenen İmaj / Paket"] -->|"Cosign ile İmzala"| B("İmzalı Artefakt")
B -->|"Kabul Kontrolü"| C{"İmza Geçerli mi?"}
C -->|"Evet"| D["Production Ortamı"]
C -->|"Hayır"| E["Dağıtımı Reddet"]
end
subgraph Client["İstemci Tarafı (SRI Bütünlüğü)"]
F["CDN'den Gelen Statik JS"] --> G{"Tarayıcı SHA-384 Kontrolü"}
G -->|"Eşleşti"| H["Kodu Çalıştır"]
G -->|"Uyuşmazlık"| I["Çalıştırmayı Engelle"]
end1. Alt Kaynak Bütünlüğü (Subresource Integrity - SRI) Kullanımı
Harici bir kaynaktan yüklenen tüm statik CSS ve JavaScript dosyalarında integrity özniteliği zorunlu tutulmalıdır. Öncelikle terminalde ilgili dosyanın SHA-384 özetini (base64 formatında) üretin:
openssl dgst -sha384 -binary framework.min.js | openssl base64 -AArdından üretilen değeri sha384- önekiyle HTML etiketine ekleyin:
<script src="https://cdn.example.com/framework.min.js"
integrity="sha384-<openssl çıktısı>"
crossorigin="anonymous"></script>SRI Sınırlaması
SRI yalnızca statik dosyalarda çalışır. polyfill.io vakasında görüldüğü üzere; içeriği her istekte dinamik olarak üretilen veya uzaktan güncellenen betiklerde (örneğin Tag Manager, analitik araçları) dosya özeti sabit olmadığı için SRI kullanılamaz. Kritik kütüphaneler sabit sürümle kendi sunucunuzda barındırılmalıdır (self-hosted). Content Security Policy (CSP) bir saldırı gerçekleştiğinde hasarı sınırlayabilir (örneğin connect-src ve form-action ile veri sızdırma ve yönlendirme hedeflerini kısıtlayarak); ancak izin verdiğiniz alan adının kendisi ele geçirildiyse tek başına saldırıyı engelleyemez.
2. Artefakt, Betik ve Container İmaj İmzalama
Dışarıdan indirilen kurulum betikleri doğrudan curl | bash ile çalıştırılmak yerine önce indirilmeli ve özet doğrulamasından geçirilmelidir. Beklenen özet değeri, betiğin kendisinden ayrı bir kanaldan (örneğin kaynak deposundaki sürüm notu veya imzalı bir yayın) alınmalıdır; aksi halde saldırgan betiği ve özetini birlikte değiştirebilir:
curl -fsSLo install.sh https://example.com/install.sh
echo "<BEKLENEN_SHA256> install.sh" | sha256sum -c -Üretim ortamına çıkacak tüm derlenmiş paketler, güncellemeler ve container imajları ise Sigstore / Cosign veya GPG ile kriptografik olarak imzalanmalı; Kubernetes kümesinde (Kyverno veya OPA Gatekeeper ile) yalnızca imzası doğrulanan imajların ayağa kalkmasına izin verilmelidir.
3. Güvenli Veri Serileştirme ve Sabit Zamanlı İmza Doğrulama
Güvenilmeyen bir kaynaktan gelen veriler için dil-spesifik yerel serileştiriciler (pickle, unserialize vb.) kesinlikle kullanılmamalıdır. (Not: pickle verisini HMAC ile imzalamak bile güvenli bir çözüm değildir; gizli anahtar sızdığı veya imza doğrulama mantığında ufak bir hata yapıldığı anda RCE zafiyeti geri döner.)
- Veri alışverişinde yalnızca saf veri taşıyan JSON veya Protocol Buffers kullanılmalı ve katı şema doğrulaması yapılmalıdır.
- İstemciye gönderilip geri alınan verilerin bütünlüğü HMAC-SHA256 ile doğrulanırken, zamanlama saldırılarını önlemek için standart
==operatörü yerine mutlaka sabit zamanlı karşılaştırma (hmac.compare_digest) kullanılmalıdır. - Eğer JWT kullanılıyorsa, kütüphanede izin verilen algoritmalar açıkça beyaz listeye alınmalı;
alg: "none"saldırılarına ve asimetrik anahtarın simetrik anahtar gibi kullanıldığı Algorithm Confusion (RS256->HS256) tuzaklarına karşı doğrulama katmanı sıkılaştırılmalıdır.
A08 Deploy Öncesi Kontrol Listesi
- [ ] Uygulamada güvenilmeyen girdiler üzerinde
pickle,unserialize,BinaryFormattergibi yerel serileştiriciler tamamen kaldırıldı mı? - [ ] Harici CDN'lerden çağrılan tüm statik
.jsve.cssdosyalarındaintegrity(SRI) vecrossoriginetiketleri tanımlı mı; dinamik kütüphaneler kendi altyapımıza (self-hosted) alındı mı? - [ ] JWT doğrulamalarında
algbaşlığı sunucu tarafında katı bir beyaz listeyle (örn. yalnızcaRS256) sınırlandırıldı mı? - [ ] Otomatik güncelleme mekanizmaları, indirilen betikler ve container imajları çalıştırılmadan önce özetleri/dijital imzaları doğrulanıyor mu?
A09:2025 - Security Logging and Alerting Failures (Güvenlik Günlükleme ve Uyarı İhlalleri)
İlgili Temel CWE'ler: CWE-778 (Insufficient Logging), CWE-117 (Improper Output Neutralization for Logs), CWE-532 (Insertion of Sensitive Information into Log File)
Bir saldırıyı engellemek her zaman mümkün olmayabilir; ancak saldırı gerçekleşirken onu tespit edebilmek ve zamanında müdahale edebilmek savunmanın bel kemiğidir. A09:2025; güvenlik açısından kritik olayların kaydedilmemesi, logların bütünlüğünün korunamaması, loglama sisteminin kendisinin bir saldırı yüzeyine dönüşmesi ve en önemlisi toplanan kayıtlardan eyleme dönüştürülebilir gerçek zamanlı uyarılar (alerting) üretilememesi sorunlarını ele alır.
Bilgi
OWASP'ın 2021 listesinde "Security Logging and Monitoring Failures" olarak yer alan bu kategori, 2025'te pasif izlemenin (monitoring) tek başına yeterli olmadığını vurgulamak amacıyla "Alerting" (Uyarı) odaklı olacak şekilde yeniden adlandırılmıştır.
Loglama Neden Tek Başına Yetmez? (Kök Nedenler)
Bir sistemde log dosyalarının oluşuyor olması, o sistemin izlendiği anlamına gelmez. Pratikte bizi kör bırakan başlıca hatalar şunlardır:
- Sadece Hata Ayıklama (Debug) Odaklı Loglama (
CWE-778): Yazılım geliştirilirken loglar genellikle sadece uygulama çöktüğünde hatayı bulmak için kurgulanır. Başarısız giriş denemeleri, yetki yükseltme hareketleri veya kritik veri sorguları hiç kaydedilmez. - Log Enjeksiyonu (
CWE-117): Kullanıcı girdileri temizlenmeden doğrudan metin tabanlı loglara yazıldığında, saldırgan girdiye satır sonu karakterleri (\r\n) ekleyerek log dosyasına sahte kayıtlar enjekte edebilir veya SIEM ayrıştırıcılarını bozabilir. - Loglarda Hassas Veri İfşası (
CWE-532): Gelen HTTP isteklerinin filtrelenmeden kaydedilmesi; parolaların, API anahtarlarının veya oturum tokenlarının açık metin olarak diske yazılmasına yol açar. Üstelik kullanıcılar başarısız giriş denemelerinde yanlışlıkla kullanıcı adı alanına parolalarını yazabildikleri için, hamusernamealanını doğrudan loglamak da dolaylı parola ifşası yaratır. - Yerel Depolama ve İzlerin Silinmesi: Loglar yalnızca uygulama sunucusunun yerel diskinde tutuluyorsa, sunucuyu ele geçiren bir saldırganın ilk işi bu dosyaları silerek adli analiz (forensics) izlerini yok etmek olacaktır.
- Uyarı Körlüğü (Alert Fatigue) veya Alarm Eksikliği: Loglar merkezi bir SIEM'e aksa bile anlamlı eşik (threshold) kuralları yazılmadığında veya günde binlerce yanlış pozitif (false-positive) alarm üretildiğinde kritik saldırılar gürültü arasında kaybolur.
Sahadan İki Vaka: Target (2013) ve Log4Shell (2021)
- Uyarı İhlali – Target Veri İhlali (2013): 2013 yılında yaklaşık 40 milyon kredi ve banka kartı hesabının etkilendiği Target ihlalinde, ABD Senatosu Ticaret Komitesi'nin çoğunluk personel raporuna (basın haberleri ve uzman analizlerine dayanan bir değerlendirme) göre şirketin FireEye saldırı tespit sistemi, zararlı yazılımların kurulumu sırasında acil uyarılar üretmişti; ancak Target bu uyarılara zamanında yanıt vermemiş görünüyor. Yani sistemde loglama ve tespit çalışmış, fakat Alerting / Müdahale zinciri kırılmıştı.
- Saldırı Yüzeyi Olarak Loglama – Log4Shell (CVE-2021-44228): OWASP bu zafiyeti temelinde A03 (bileşen/tedarik zinciri zafiyeti) altında sınıflandırsa da, loglama katmanının kendisinin bir saldırı yüzeyi olması bağlamında çok kritik bir örnektir. Saldırganların HTTP
User-Agentbaşlığına veya giriş formlarına${jndi:ldap://...}yükleri göndererek JavaLog4jkütüphanesi üzerinden RCE elde etmesi, loglanan girdilerin işlenirken ne kadar tehlikeli olabileceğini kanıtlamıştır.
Görünürlük ve Uyarı Mimarisi Nasıl Kurulur?
flowchart LR
A["Uygulama"] -->|"Yapılandırılmış JSON + Maskeleme"| B["TLS / Syslog-ng / Filebeat"]
B --> C["Merkezi SIEM"]
C --> D{"Korelasyon Motoru"}
D -->|"MITRE T1110 / T1070"| E["Kritik Alarm: SOC / SOAR"]
F["Heartbeat İzleyici"] -->|"Log Akışı Kesildi!"| E1. Yapılandırılmış (Structured) Loglama ve Girdi Kırpma
Düz metin satırları yerine SIEM araçlarının doğrudan ayrıştırabileceği JSON formatı kullanılmalıdır. Kullanıcı girdilerindeki kontrol karakterleri (\r, \n) temizlenmeli ve kullanıcı adı alanına yanlışlıkla parola yazılması ihtimaline karşı başarısız girişlerde user_identifier alanı uzunluk sınırına tabi tutulmalı veya kısmen maskelenmelidir:
{
"timestamp": "2026-10-16T14:22:05.123Z",
"level": "WARN",
"event_type": "AUTHENTICATION_FAILURE",
"user_identifier": "mahmut_a",
"source_ip": "198.51.100.42",
"request_id": "req-9f8d7a6b-5c4e",
"reason": "Invalid MFA token",
"attempt_count": 4
}2. MITRE ATT&CK Odaklı SIEM Korelasyon Mantığı
Toplanan loglar üzerinde tekil olaylara bakmak yerine saldırgan davranış örüntülerini yakalayan korelasyon kuralları işletilmelidir:
T1110(Brute Force): Örneğin; 60 saniye içinde aynı kaynak IP'den 5 başarısız giriş denemesi (AUTHENTICATION_FAILURE) gelmesi ve hemen ardından aynı IP'den başarılı bir giriş (AUTHENTICATION_SUCCESS) gerçekleşmesi durumunda yüksek öncelikli alarm üretilmesi.T1070(Indicator Removal): Sunucu üzerindeki log dosyalarının silinmesi, boyutunun aniden küçülmesi veya geçmiş kayıtların temizlenmesi girişimlerinin dosya bütünlüğü izleme (FIM) ile anında tespit edilmesi.
3. "Log Kaynağı Sustu" (Heartbeat / Dead-Man's Switch) Alarmı
Saldırganlar bir sunucuyu ele geçirdiklerinde ilk olarak SIEM ajanını (wazuh-agent, auditd, rsyslog) durdurarak görünmez olmaya çalışırlar (MITRE T1562.001 - Disable or Modify Tools). Bu nedenle SIEM üzerinde "Son 5 dakikadır X sunucusundan hiç log veya heartbeat sinyali gelmedi" koşulu, en az aktif saldırı kuralları kadar kritik bir uyarı olarak yapılandırılmalıdır.
A09 Deploy Öncesi Kontrol Listesi
- [ ] Kimlik doğrulama, yetki reddi (
403) ve kritik iş mantığı hataları yapılandırılmış JSON formatında loglanıyor mu? - [ ] Parola, oturum tokenı, API anahtarı ve PII verileri loglama katmanında otomatik olarak
***REDACTED***ile maskeleniyor mu? - [ ] Loglar yerel diskte kalmayıp anlık olarak uzak ve salt-eklenir (append-only) bir SIEM sunucusuna aktarılıyor mu?
- [ ] SIEM üzerinde hem saldırı desenleri hem de log akışının kesilmesi (heartbeat kaybı) için aktif alarmlar tanımlı mı?
A10:2025 - Mishandling of Exceptional Conditions (İstisnai Durumların Yanlış Yönetilmesi)
İlgili Temel CWE'ler: CWE-636 (Not Failing Securely / 'Fail Open'), CWE-209 (Generation of Error Message Containing Sensitive Information), CWE-755 (Improper Handling of Exceptional Conditions), CWE-460 (Improper Cleanup on Thrown Exception)
Yazılımlar her zaman ideal laboratuvar koşullarında çalışmaz; veritabanı bağlantısı kopabilir, üçüncü parti bir servis zaman aşımına uğrayabilir veya saldırgan kasten bozuk bir veri tipi gönderebilir. OWASP Top 10 listesine 2025'te yeni giren A10:2025; uygulamanın bu olağandışı hata anlarında güvenli bir duruma geçememesi (Fail-Open), çok adımlı işlemleri yarım bırakarak veri tutarsızlığı yaratması, sistem iç yapısını dışarıya sızdırması veya kaynakları serbest bırakmayarak çökmesi gibi zafiyetleri kapsar.
Hata Anında Sistem Neden Çöker veya Açık Kalır?
- Güvensiz Hata Durumu - Fail-Open (
CWE-636): Bir güvenlik kontrolü (yetkilendirme, WAF, rate-limiter) çalışırken beklenmeyen bir istisna (Exception) fırlatıldığında,catchbloğunun işlemi durdurmak yerine akışı devam ettirmesidir. - İstisna Anında Eksik Temizlik ve Yarım Kalan İşlemler (
CWE-460): İşlem sırasında hata fırlatıldığında, o ana kadar yapılan kısmi veritabanı değişikliklerinin geri alınmaması (rollback eksikliği) veya açılmış olan bağlantıların/kilitlerin (locks)finallybloğunda kapatılmamasıdır. - Detaylı Hata Mesajları ile Bilgi İfşası (
CWE-209): Yakalanan hatanın veya Stack Trace çıktısının doğrudan HTTP yanıtında kullanıcıya dönülmesidir. Bu çıktı; veritabanı şemasını, dosya dizin yapısını (/var/www/...), kullanılan kütüphane sürümlerini ve iç IP adreslerini doğrudan saldırgana verir. - Hata ve Zamanlama Farklılıkları (Oracle Yan Kanalları): Farklı hata durumlarında farklı mesajlar veya ölçülebilir düzeyde farklı yanıt süreleri dönülmesidir. (Not: Kullanıcı adı tespiti klasik olarak
A07 - Authentication Failures, kriptografik Padding Oracle iseA04 - Cryptographic Failureskapsamına daha yakındır; ancak her ikisinin de temel tetikleyicisi istisnai durumların tutarsız ele alınmasıdır.)
Örnek Senaryo (Kurgusal): Mikroservis Zaman Aşımı ile Yetki Atlatma ve Havuz Tüketimi
Modern bir e-ticaret uygulamasında, sipariş iptali uç noktasının (API Gateway) kullanıcının yetkisini doğrulamak için dahili bir Policy Servisine gRPC isteği attığını düşünelim. Saldırgan, sisteme eşzamanlı olarak bozuk karakterler içeren yüzlerce paralel istek gönderdiğinde Policy Servisi istisna anında veritabanı bağlantılarını finally bloğunda kapatmadığı için (CWE-460) bağlantı havuzu (connection pool) dolar ve servis TimeoutError fırlatmaya başlar. Ana uygulama bu istisnayı yakalarken "Servis yanıt vermiyorsa müşteriyi bekletmeyelim" mantığıyla (Fail-Open) hata bloğundan True dönerse, saldırgan başkasının siparişlerini yetkisiz olarak iptal edebilir.
Güvenli Hata Yönetimi Nasıl Tasarlanır?
flowchart TD
A["İstemci İsteği"] --> B["Yetki Kontrol Servisi"]
B --> C{"İstisna / Timeout Oluştu mu?"}
C -->|"Hayır"| D{"Yetki Var mı?"}
D -->|"Evet"| E["200 OK: İşlemi Yürüt"]
D -->|"Hayır"| F["403 Forbidden: Yetkisiz"]
C -->|"Evet - YANLIŞ: Fail-Open"| E
C -->|"Evet - DOĞRU: Fail-Closed"| G["Erişimi Reddet ve Rollback"]
G --> H["Log: AUTH_SERVICE_UNAVAILABLE + SIEM Alarmı"]
H --> I["503 Service Unavailable + Reference ID"]1. Oluştuğu Yerde "Fail-Closed" Tasarımı ve Kesinti Görünürlüğü (A09 ile Köprü)
Hatalar öncelikle oluştukları bağlamda yakalanmalı ve yetki kontrolünde bir sorun çıktığında erişim kesinlikle reddedilmelidir (Fail-Closed). Ayrıca doğrulama fonksiyonunun dönüş değeri gevşek doğruluk (bool(...)) yerine katı boolean kontrolü (result is True) ile denetlenmelidir; aksi halde servisin yanlışlıkla döndürdüğü boş olmayan bir hata nesnesi veya metni True kabul edilebilir.
Bununla birlikte, hata anında sadece return False dönmek, "Kullanıcının yetkisi yok (403)" durumu ile "Yetki servisi çöktü (503)" durumunu birbirine karıştırır ve sistem kesintisini gizler. Doğru yaklaşımda; beklenen iletişim hataları (TimeoutError, ConnectionError) yakalanarak A09 kapsamında alarm üretecek özel bir olay koduyla kaydedilmeli ve üst katmana 503 Service Unavailable üretecek istisna fırlatılmalıdır:
class AuthorizationServiceError(Exception):
"""Yetki servisine ulaşılamadığında fırlatılacak özel istisna (HTTP 503 için)."""
pass
# YANLIŞ YAKLAŞIM (Fail-Open): Hata durumunda kontrol atlanıyor!
def check_permission_insecure(user, resource):
try:
return auth_service.verify(user.id, resource.id)
except TimeoutError:
log.warning("Auth servisi zaman aşımına uğradı, akış devam ediyor.")
return True # KRİTİK ZAFİYET!
# DOĞRU YAKLAŞIM (Fail-Closed + Katı Kontrol + Operasyonel Görünürlük):
def check_permission_secure(user, resource):
try:
result = auth_service.verify(user.id, resource.id)
return result is True
except (TimeoutError, ConnectionError) as exc:
# A09 Köprüsü: SIEM'de alarm üretecek spesifik event_type ile stack trace'i kaydet
log.exception(
"Yetki servisi iletişim hatası nedeniyle erişim reddedildi (Fail-Closed)",
extra={"event_type": "AUTH_SERVICE_UNAVAILABLE", "user_id": user.id}
)
# 403 (Yetkisiz) ile karışmaması için üst katmana 503 üretecek istisnayı fırlat
raise AuthorizationServiceError("Yetkilendirme servisi geçici olarak kullanılamıyor.") from exc2. Atomik İşlemler (Rollback) ve Garantili Kaynak Temizliği (CWE-460)
Çok adımlı iş mantıklarında (örneğin bakiye düşme ve sipariş oluşturma) ara adımlardan birinde istisna fırlatılırsa, işlem mutlaka başlangıç durumuna geri alınmalıdır (transaction.rollback()). Veritabanı bağlantıları, ağ soketleri ve dosya işlemleri ise otomatik kaynak yöneticileriyle (Python with, Java try-with-resources, C# using, Go defer) kullanılarak istisna anında bile sızıntı olmadan kapatılmalıdır.
3. Zamanlama (Timing) Kanallarını Kapatma
Kimlik doğrulama gibi hassas uç noktalarda yalnızca aynı hata mesajını ve aynı HTTP durum kodunu (401 Unauthorized) dönmek yeterli değildir. Kullanıcı veritabanında bulunamadığında, şifre doğrulama adımı (örn. Argon2 veya bcrypt) atlanırsa yanıt çok hızlı döner ve saldırgan yanıt süresinden kullanıcının varlığını tespit eder. Bunu önlemek için kullanıcı bulunamadığında bile bellekte sahte (dummy) bir hash karşılaştırması yapılmalı ve karşılaştırmalar sabit zamanlı fonksiyonlarla yürütülmelidir.
4. Son Güvenlik Ağı Olarak Merkezi İstisna Yönetimi (Global Exception Handler)
Alt katmanlarda özel olarak ele alınmayan beklenmeyen çalışma zamanı hatalarını (örn. TypeError, KeyError) yakalamak için uygulamanın en dış katmanında merkezi bir filtre bulunmalıdır. Bu katman her hata için benzersiz bir reference_id üretmeli; detaylı Stack Trace'i bu ID ile iç loglara yazarken istemciye yalnızca standart bir JSON yanıtı dönmelidir. Beklenmeyen hatalar 500 ile döner:
{
"status": 500,
"error": "Internal Server Error",
"message": "İşleminiz gerçekleştirilirken beklenmeyen bir hata oluştu.",
"reference_id": "err-4b82a19c-7d2e"
}Aynı merkezi handler, yukarıdaki AuthorizationServiceError gibi bilinen kesinti istisnalarını ise aynı yapıda 503 Service Unavailable olarak döner; böylece yetki reddi (403), servis kesintisi (503) ve beklenmeyen hata (500) birbirinden net biçimde ayrışır.
İpucu
İstemciye yalnızca reference_id dönmek, hata mesajları üzerinden gerçekleşen bilgi ifşasını (CWE-209) büyük ölçüde azaltır ve gerçek bir kullanıcı destek ekibine başvurduğunda geliştiricilerin SIEM üzerinden ilgili hatayı saniyeler içinde bulmasını sağlar.
A10 Deploy Öncesi Kontrol Listesi
- [ ] Güvenlik ve yetki kontrolleri katı boolean kontrolü (
result is True) ve Fail-Closed (varsayılan reddetme) prensibiyle çalışıyor mu? - [ ] Çok adımlı işlemlerde hata oluştuğunda tüm işlem güvenli bir şekilde geri alınıyor mu (transaction rollback)?
- [ ] Veritabanı, soket ve dosya kaynakları istisna anında sızıntı yapmaması için
with/try-with-resources/finallyile korunuyor mu? - [ ] Sistem hatalarında (
500/503) yetki reddi (403) ile karışmayan ayrı log olayları üretiliyor ve son güvenlik ağı olarak Global Exception Handler (reference_idile) devreye giriyor mu?
A08–A10 Özet Karşılaştırma ve CWE Tablosu
İncelediğimiz son üç zafiyetin kök nedenlerini, CWE karşılıklarını, tespit yöntemlerini ve birincil savunma mekanizmalarını aşağıdaki tabloda özetleyebiliriz:
| Zafiyet Kodu ve Adı | İlgili CWE Kodları | Temel Kök Neden | Nasıl Test / Tespit Edilir? | Birincil Savunma Yöntemi |
|---|---|---|---|---|
| A08:2025 Software or Data Integrity Failures | CWE-502CWE-494CWE-345 | Harici betiklere, imzasız artefaktlara ve serileştirilmiş verilere doğrulamasız güvenmek. | SAST: Kodda pickle/unserialize taraması.DAST: Statik <script> etiketlerinde eksik integrity kontrolü ve JWT alg=none testleri. | Statik dosyalarda SRI, dinamik betiklerde self-hosting, Cosign ile imzalama, JSON şeması ve hmac.compare_digest. |
| A09:2025 Security Logging and Alerting Failures | CWE-778CWE-117CWE-532 | Kritik olayların loglanmaması, hassas veri loglama ve gerçek zamanlı alarm üretilmemesi. | Purple Team Tatbikatı: Brute-force (T1110) veya ajan durdurma (T1562.001) simülasyonu yapıp SOC ekranına alarm düşüp düşmediğini doğrulama. | Yapılandırılmış JSON loglama, PII maskeleme, SIEM korelasyon kuralları ve Heartbeat alarmları. |
| A10:2025 Mishandling of Exceptional Conditions | CWE-636CWE-209CWE-755CWE-460 | Hata anında açık kalma (Fail-Open), yarım kalan işlemler, detaylı stack trace ifşası ve kaynak sızıntısı. | Kaos Mühendisliği & Fuzzing: Alt servisleri kapatarak yetki kontrolünün davranışını (Fail-Open) ve bozuk girdilerle 500 yanıtlarındaki bilgi sızıntısını test etme. | Fail-Closed tasarım (result is True), rollback, try-with-resources, sabit zamanlı yanıtlar ve Global Exception Handler. |
Sonuç
İncelediğimiz tüm bu kategorilerde ortak ders aynıdır: güvenlik sonradan eklenen bir filtre değil, kodun, mimarinin ve operasyonel tasarımın kendisidir. WAF'lar, ağ filtreleri ve izleme araçları değerli birer savunma katmanıdır; ancak saldırgan bu katmanları aşmayı başardığında uygulamayı ayakta tutacak olan şey, doğru kurgulanmış güven sınırları, görünürlük mekanizmaları ve backend'de güvenli şekilde yazılmış o birkaç satırdır.
Kaynakça
OWASP Top 10 2025 İçin Kaynakça
- OWASP Top 10:2025
- OWASP Top 10:2025 – Introduction & Release Notes
- OWASP A01:2025 – Broken Access Control
- OWASP A02:2025 – Security Misconfiguration
- OWASP A03:2025 – Software Supply Chain Failures
- OWASP A04:2025 – Cryptographic Failures
- OWASP A05:2025 – Injection
- OWASP A06:2025 – Insecure Design
- OWASP A07:2025 – Authentication Failures
- OWASP A08:2025 – Software or Data Integrity Failures
- OWASP A09:2025 – Security Logging and Alerting Failures
- OWASP A10:2025 – Mishandling of Exceptional Conditions
- OWASP Authorization Cheat Sheet
- OWASP Server Side Request Forgery Prevention Cheat Sheet
- OWASP HTTP Headers Cheat Sheet
- OWASP Software Supply Chain Security Cheat Sheet
- OWASP Password Storage Cheat Sheet
- OWASP Cryptographic Storage Cheat Sheet
- OWASP Deserialization Cheat Sheet
- OWASP Logging Cheat Sheet
- OWASP Error Handling Cheat Sheet
- NPR – The Untold Story Of The SolarWinds Hack (2021)
- Codecov – Bash Uploader Security Update (2021)
- Sansec Research – Polyfill supply chain attack hits 100K+ sites (2024)
- InfoQ – Over 100K+ Sites Hit by Polyfill.io Supply Chain Attack
- NIST NVD – CVE-2021-44228 (Apache Log4j2 / Log4Shell)
- MITRE ATT&CK – T1110 (Brute Force)
- MITRE ATT&CK – T1070 (Indicator Removal)
- MITRE ATT&CK – T1562.001 (Impair Defenses: Disable or Modify Tools)

