OWASP API Security Top 10: 2019'dan 2023'e Ne Değişti ve Eski Raporları Bugünün Diliyle Okumak
2023 sürümünde Excessive Data Exposure ile Mass Assignment BOPLA'da birleşti, API2, API4 ve API9'un adı değişti, Injection ve Logging listeden çıktı, hassas iş akışları, SSRF ve güvensiz tüketim eklendi; bu yüzden eski bulgular numaraya göre değil anlama göre eşlenir. Eşleme tablosu, eski raporları çeviren ve değerlendirilmemiş kategorileri gösteren küçük bir araç ve yeni sürüm geldiğinde ne yapacağımız.
Sürüm notu: Bu yazı OWASP (Open Worldwide Application Security Project) API (Application Programming Interface) Security Top 10'un 2019 ve 2023 sürümlerine göre yazılmıştır; Kasım 2026 itibarıyla duyurulmuş yeni bir sürüm yok. Örnek araç .NET 10 ile derlenip denenmiştir.
Önce Tahmin Et
Masana 2022 tarihli bir pentest raporu geliyor. İçinde API6 (Mass Assignment) ve API8 (Injection) başlıklı bulgular var. Ekipte biri bunları 2023 listesinin aynı numaralarına göre etiketleyip takip sistemine giriyor: API6 artık "Unrestricted Access to Sensitive Business Flows", API8 ise "Security Misconfiguration". İki bulgu da yanlış kategoride ve kimse fark etmiyor, çünkü numaralar tanıdık.
Bir de sessiz bir sorun var: rapor 2019 listesine göre hazırlandığı için SSRF (Server Side Request Forgery), hassas iş akışları ve üçüncü parti API'lerin güvensiz tüketimi hiç değerlendirilmemiş. Raporda bu başlıklarda bulgu olmaması "temiz" demek değil, "bakılmamış" demek.
Bu yazı serinin son yazısı ve bir sürüm yazısı: 2019'dan 2023'e listede ne değiştiğini, eski raporları ve dokümanları bugünün kategorilerine nasıl çevireceğini ve bir sonraki sürüm geldiğinde bu seriyi nasıl güncelleyeceğimizi konuşuyoruz. Web Top 10'un sürümleri arasındaki değişiklikleri Web Güvenliği (OWASP Top 10) serisinde iki ayrı yazıda ele almıştık; burada aynı alışkanlığı API listesine uyguluyoruz.
İki Liste Yan Yana
İlk API Security Top 10 2019'da yayımlandı, ikinci sürüm dört yıl sonra 2023'te geldi. OWASP'ın sürüm notlarına göre üç madde aynı kaldı, üçünün adı değişti, ikisi birleşti, ikisi listeden çıktı ve üç yeni madde eklendi:
| 2019 | 2023 | Değişiklik |
|---|---|---|
| API1 (Broken Object Level Authorization) | API1 (Broken Object Level Authorization) | Aynı |
| API2 (Broken User Authentication) | API2 (Broken Authentication) | Ad değişti, kapsam genişledi |
| API3 (Excessive Data Exposure) | API3 (Broken Object Property Level Authorization) | API6 ile birleşti |
| API4 (Lack of Resources & Rate Limiting) | API4 (Unrestricted Resource Consumption) | Ad değişti, kapsam genişledi |
| API5 (Broken Function Level Authorization) | API5 (Broken Function Level Authorization) | Aynı |
| API6 (Mass Assignment) | API3 (Broken Object Property Level Authorization) | API3 ile birleşti |
| API7 (Security Misconfiguration) | API8 (Security Misconfiguration) | Numarası değişti |
| API8 (Injection) | yok | Listeden çıktı |
| API9 (Improper Assets Management) | API9 (Improper Inventory Management) | Ad değişti |
| API10 (Insufficient Logging & Monitoring) | yok | Listeden çıktı |
| yok | API6 (Unrestricted Access to Sensitive Business Flows) | Yeni |
| yok | API7 (Server Side Request Forgery) | Yeni |
| yok | API10 (Unsafe Consumption of APIs) | Yeni |
Tablodan çıkan ilk ders: numaralar sabit bir kimlik değil. API6 ve API8 iki sürümde tamamen farklı şeyleri anlatıyor. Bir bulguyu kaydederken numarayı her zaman sürümle birlikte yazmak (API6:2019, API6:2023) bu karışıklığı baştan önlüyor; OWASP'ın kendi belgeleri de bu biçimi kullanıyor.
Sürüm notlarında yöntemle ilgili dürüst bir not da var: 2023 için ilk kez açık bir veri çağrısı yapılmış ama veri gelmemiş. Liste proje ekibinin deneyimi, API güvenliği uzmanlarının incelemesi ve release candidate'e gelen topluluk geri bildirimiyle oluşturulmuş. Yani bu liste istatistiksel bir sıralamadan çok, uzmanların "API'lerde en sık ve en pahalı neyi görüyoruz" sorusuna verdiği cevap.
Birleşenler: BOPLA'nın Doğuşu
2019'daki Excessive Data Exposure (fazla veri döndürmek) ve Mass Assignment (fazla veri kabul etmek) 2023'te tek bir maddede, API3 BOPLA'da (Broken Object Property Level Authorization) birleşti. Gerekçe, ikisinin aynı kök nedene sahip olması: property seviyesinde yetkilendirme eksikliği. Bu birleşme yalnız bir isim düzenlemesi değil, savunmanın nasıl düşünüleceğini de değiştiriyor.
2019 bakışıyla iki ayrı düzeltme yapardın: response'tan alanları çıkar, request'te bilinmeyen alanları reddet. 2023 bakışıyla tek bir soru soruyorsun: bu rol bu alanı okuyabilir mi, yazabilir mi? Dördüncü yazıda kurduğumuz yapı tam bu soruya cevap veriyordu: rol başına response tipi ve rol başına yazılabilir alan allowlist'i. Eski bir raporda iki ayrı bulgu olarak gelen sorunların çoğu aynı tasarım eksikliğinin iki yüzü ve tek bir düzeltmeyle kapanıyor.
Yeniden Adlandırılanlar: Ad Değişince Kapsam da Değişir
API2'nin adındaki "User" kelimesinin düşmesi küçük görünüyor ama önemli: 2023'te authentication yalnız insan kullanıcılar için değil, servisler ve makineler için de değerlendiriliyor. Üçüncü yazıdaki API key tasarımı ve HMAC (Hash-based Message Authentication Code) imzalı istekler 2019'un dilinde bu başlığa tam oturmuyordu; 2023'te doğrudan API2'nin konusu.
API4'ün adı "kaynak eksikliği ve rate limiting"ten "sınırsız kaynak tüketimi"ne döndü. Odak, istek sayısından istek başına maliyete kaydı: sayfa boyutu, sorgu karmaşıklığı, upload boyutu ve en önemlisi SMS (Short Message Service), e-posta ya da LLM (Large Language Model) çağrısı gibi para harcatan dış servisler. Beşinci yazıdaki plan bazlı SMS kotası 2019'un başlığına sığmazdı.
Burada bir eşleme inceliği var. 2019'da "login endpoint'inde rate limit yok" bulgusu API4'e yazılırdı. 2023'te API2'nin önleme listesi, kimlik doğrulama endpoint'leri için genel rate limit'ten daha sıkı brute force kontrolleri istiyor. Bu yüzden kimlik doğrulama akışındaki eksik limiti 2023'te API2 altında takip etmek daha doğru.
API9'un "Assets" yerine "Inventory" demesi de kapsamı netleştiriyor: yalnız eski sürümler ve unutulmuş host'lar değil, verinin hangi üçüncü tarafa aktığı da envanterin parçası.
Listeden Çıkanlar: Injection ve Logging Nereye Gitti?
2019'un API8 Injection ve API10 Insufficient Logging & Monitoring maddeleri 2023'te listede yok. OWASP sürüm notları bunun sebebini ayrıca açıklamıyor; yalnız API listesinin diğer Top 10 listelerinin yerine geçmediğini hatırlatıyor. Pratik sonuç şu: bu iki konu önemini kaybetmedi, API'ye özgü olmadıkları için genel listeye bırakıldı. Injection bugün OWASP Top 10:2025'te A05 Injection, logging ise A09 Security Logging and Alerting Failures olarak takip ediliyor; ikisini de Web Güvenliği serisinde ayrıntılı ele almıştık.
Eski bir rapordaki injection bulgusunu "artık listede yok" diye kapatmak en kötü sonuç olur. Doğru yaklaşım onu Web Top 10 kategorisine taşımak, ve veri bir üçüncü taraftan geliyorsa API10'u da kontrol etmek: bir partner API'sinin döndürdüğü veriyi doğrulamadan SQL (Structured Query Language) sorgusuna koymak hem injection hem de güvensiz tüketim.
Yeni Gelenler: Akışlar, SSRF ve Başkalarının API'leri
Üç yeni madde, API'lerin son yıllardaki değişimini yansıtıyor. API6 (hassas iş akışları), teknik olarak kusursuz ama iş açısından kötüye kullanılabilen akışları ilk kez listeye aldı: bot'ların sınırlı stoğu toplaması, kuponların tekrar tekrar kullanılması. Yedinci yazıdaki Idempotency-Key ve akış bazlı limitler bu maddeye cevap.
API7 (SSRF), webhook'ların, URL'den (Uniform Resource Locator) içerik çeken özelliklerin ve bulut metadata servislerinin yaygınlaşmasıyla API'lere özgü bir risk hâline geldi. Web Top 10'da da SSRF 2021'de ayrı bir madde olmuş, 2025'te A01 Broken Access Control altına alınmıştı; API listesinde ise ayrı bir madde olarak duruyor.
API10 (güvensiz tüketim) ise yönü tersine çeviriyor: bizim çağırdığımız ya da bizi çağıran üçüncü taraflardan gelen veriye kullanıcı girdisi kadar şüpheyle bakmak. Sekizinci yazıdaki kargo API'si ve ödeme webhook'u bu maddenin örnekleriydi.
Eski Raporu Bugünün Diliyle Okumak
Eşlemeyi elle yapmak birkaç bulguda kolay, yüzlerce bulguda hataya açık. Lab'a küçük bir araç ekledim: eski raporun bulgularını bir CSV (Comma-Separated Values) dosyasından okuyor, 2023 kategorisine çeviriyor ve rapordan çıkmayan 2023 kategorilerini listeliyor.
// 2019 -> 2023: aynı kalan, adı değişen, birleşen ve listeden çıkan maddeler
private static readonly Dictionary<string, (string Category, string Note)> Direct = new()
{
["API1:2019"] = ("API1:2023", "unchanged"),
["API2:2019"] = ("API2:2023", "renamed: Broken User Authentication -> Broken Authentication"),
["API3:2019"] = ("API3:2023", "merged: Excessive Data Exposure -> BOPLA"),
["API4:2019"] = ("API4:2023", "renamed: Lack of Resources & Rate Limiting -> Unrestricted Resource Consumption"),
["API5:2019"] = ("API5:2023", "unchanged"),
["API6:2019"] = ("API3:2023", "merged: Mass Assignment -> BOPLA"),
["API7:2019"] = ("API8:2023", "renumbered: Security Misconfiguration"),
["API8:2019"] = ("outside", "Injection left the API list: track under OWASP Top 10:2025 A05 Injection"),
["API9:2019"] = ("API9:2023", "renamed: Improper Assets Management -> Improper Inventory Management"),
["API10:2019"] = ("outside", "Logging left the API list: track under OWASP Top 10:2025 A09 Security Logging and Alerting Failures")
};
private static readonly string[] AuthenticationPaths = ["/login", "/token", "/password-reset", "/account/"];
public static MappedFinding Map(Finding finding)
{
// Kimlik doğrulama akışındaki eksik limit 2023'te API2'nin "brute force'a karşı daha sıkı kontrol" maddesi
if (finding.Category2019 == "API4:2019"
&& AuthenticationPaths.Any(p => finding.Endpoint.Contains(p, StringComparison.OrdinalIgnoreCase)))
{
return new MappedFinding(finding, "API2:2023", "rate limiting on an authentication flow belongs to API2 in 2023");
}
(string category, string note) = Direct.TryGetValue(finding.Category2019, out (string Category, string Note) entry)
? entry
: ("unknown", $"unknown 2019 category '{finding.Category2019}'");
return new MappedFinding(finding, category, note);
}
// Eski raporda hiç bulgu çıkmayan 2023 kategorileri: "temiz" değil, "değerlendirilmemiş" olabilir
public static IReadOnlyList<string> NotAssessed(IEnumerable<MappedFinding> mapped)
{
HashSet<string> seen = mapped.Select(m => m.Category2023).ToHashSet();
return Categories2023.Where(c => !seen.Contains(c)).ToList();
}
Lab için 2022 tarihli, dokuz bulguluk hayalî bir pentest raporu yazdım; bulguların çoğu bu serinin başında lab'ın ilk hâlinde karşılaştığımız sorunlara benziyor. Aracın çıktısı:
$ dotnet run --project tools/Bookstore.Security.ReportMapper
F-01 API1:2019 -> API1:2023 GET /orders/{id} unchanged
F-02 API3:2019 -> API3:2023 GET /orders/{id} merged: Excessive Data Exposure -> BOPLA
F-03 API6:2019 -> API3:2023 PUT /orders/{id} merged: Mass Assignment -> BOPLA
F-04 API4:2019 -> API2:2023 POST /account/password-reset rate limiting on an authentication flow belongs to API2 in 2023
F-05 API4:2019 -> API4:2023 GET /orders renamed: Lack of Resources & Rate Limiting -> Unrestricted Resource Consumption
F-06 API7:2019 -> API8:2023 GET /reports/daily-sales renumbered: Security Misconfiguration
F-07 API8:2019 -> outside GET /orders Injection left the API list: track under OWASP Top 10:2025 A05 Injection
F-08 API9:2019 -> API9:2023 GET /v1/catalog/books renamed: Improper Assets Management -> Improper Inventory Management
F-09 API10:2019 -> outside GET /orders/{id} Logging left the API list: track under OWASP Top 10:2025 A09 Security Logging and Alerting Failures
Not assessed by this report: API5:2023, API6:2023, API7:2023, API10:2023
Son satır çıktının en değerli kısmı. API6, API7 ve API10 2019'da yoktu; bu rapor onları hiçbir zaman değerlendiremezdi. API5 ise listede vardı ama raporda bulgu yok: ya gerçekten sorun yoktu ya da test edilmedi. Hangisi olduğunu raporun kapsam bölümünden öğrenmen gerekiyor. Bu dört kategori, bir sonraki testin kapsamına açıkça yazılmalı.
Eşleme kuralları da test ediliyor, böylece biri tabloya yanlış bir satır eklerse fark ediliyor:
[Fact]
public void Categories_new_in_2023_are_never_covered_by_a_2019_report()
{
IEnumerable<MappedFinding> all = Enumerable.Range(1, 10)
.Select(i => ApiTop10Mapping.Map(new Finding("F", $"API{i}:2019", "t", "GET /x")));
Assert.Equal(["API6:2023", "API7:2023", "API10:2023"], ApiTop10Mapping.NotAssessed(all));
}
Lab'ın test projesi bu yazıyla 194 teste ulaştı; hepsi yeşil.
Sonraki Sürüm Geldiğinde
Kasım 2026 itibarıyla OWASP API Security projesinin sitesinde 2023'ten yeni bir sürüm ya da duyurulmuş bir takvim yok. Yeni bir sürüm geldiğinde bu seriyi üç adımda güncelleyeceğiz. Önce bu yazıdaki eşleme tablosuna yeni bir sütun ekleyip her maddenin aynı mı kaldığını, birleştiğini mi, yeniden adlandırıldığını mı, çıktığını mı yazacağız. Sonra lab'daki kontrol listesindeki (onuncu yazının tablosu) her satırı yeni kategoriye bağlayacağız; savunmalar değişmeyecek, etiketler değişecek. Son olarak yeni gelen her madde için lab'a bir savunma ve onu koruyan bir test ekleyip ayrı bir yazı yazacağız.
Kendi projende de aynı yaklaşım işe yarar: bulgu ve kontrolleri sürüm etiketli kategorilerle tut, savunmaları kategoriden bağımsız kodla ve testle koru. Kategori bir sınıflandırma; asıl iş, onuncu yazıdaki tablonun sağ sütunundaki testler.
Üretimde Dikkat
Takip sisteminde kategoriyi serbest metin yerine sürümlü bir etiket olarak tut (owasp-api:API3:2023 gibi). Yeni bir sürüm geldiğinde etiketleri toplu olarak çevirmek, serbest metinleri tek tek okumaktan çok daha kolay.
Pentest sözleşmesinde hangi sürüme göre test yapılacağını açıkça yaz. "OWASP API Top 10'a göre" ifadesi 2026'da hâlâ 2019 listesiyle çalışan bir firmaya bütün yeni kategorileri atlama izni verebilir. Kapsama 2023'ün üç yeni maddesini açıkça ekle ve raporun her kategori için "test edildi, bulgu yok" ile "test edilmedi" ayrımını yapmasını iste.
Eski dokümanlarda, wiki sayfalarında ve kod yorumlarındaki numaralı referansları tarayıp güncelle. Bir kod yorumunda "API6 için eklendi" yazıyorsa, 2023 okuyucusu bunun hassas akışlarla ilgili olduğunu sanar; oysa yorum mass assignment için yazılmıştı.
Sık Düşülen Tuzaklar
Birinci tuzak, numaraya göre eşlemek. API6 ve API8 iki sürümde tamamen farklı riskler; numarayı sürümsüz yazmak en sık karşılaştığım hata.
İkincisi, listeden çıkan maddeleri "artık önemli değil" diye kapatmak. Injection ve logging, API listesinden çıktı ama genel Top 10'da duruyor.
Üçüncüsü, eski raporda bulgu olmayan kategoriyi temiz saymak. Yeni kategoriler eski raporda hiç değerlendirilmedi; bu bir sonuç değil, bir kapsam boşluğu.
Dördüncüsü, listeyi bir kontrol listesi gibi kullanmak. Top 10 bir farkındalık belgesi; doğrulama standardı arıyorsan OWASP'ın ASVS'i (Application Security Verification Standard) daha uygun. İkisinin farkını Web Güvenliği serisinin son yazısında konuşmuştuk.
Küçük Bir Deneme
★☆☆ Kendi projenden ya da halka açık bir rapordan beş bulgu seç ve 2019'dan 2023'e elle eşle. Sonra aracı çalıştırıp sonuçları karşılaştır.
★★☆ Araca kapsam bilgisini ekle: raporun "test edilen kategoriler" listesini ayrı bir dosyadan okusun ve "test edildi, bulgu yok" ile "test edilmedi" ayrımını yapsın.
★★★ Aracın çıktısını onuncu yazıdaki kontrol listesiyle birleştir: her 2023 kategorisi için lab'da hangi testlerin o kategoriyi koruduğunu listeleyen bir rapor üret. Hangi kategoride test sayısı en az?
Bu yazıyla API Güvenliği serisi bitiyor. Lab'da on kategorinin her biri için bir savunma ve onu koruyan testler var. Kimlik doğrulama ve token tarafını ilerideki Authentication ve Authorization serisinde, rate limiting ve security header'ların iç yapısını ASP.NET Core Güvenliği serisinde, gateway katmanını API Gateway ve YARP (Yet Another Reverse Proxy) serisinde, GraphQL ve gRPC'ye özgü riskleri gRPC ve GraphQL serisinde, LLM API'lerini ise LLM Uygulama Güvenliği serisinde ele alacağız.