Mert Özen Her Satırda Daha İleri
Güvenlik Web Güvenliği (OWASP Top 10) — #18

OWASP Top 10:2017: XXE, Insecure Deserialization ve Bugüne Kalan Dersler

Mert Özen 6 Kas 2026 8 dk 7 görüntülenme
OWASP Top 10:2017: XXE, Insecure Deserialization ve Bugüne Kalan Dersler

OWASP Top 10:2017 listesini, yazıldığı dönemin web'ini ve 2013'ten 2017'ye değişenleri inceliyoruz. XXE'nin .NET 10'da varsayılan olarak nasıl reddedildiğini gerçek bir istekle, BinaryFormatter'ın neden artık derlenmediğini ve çalışmadığını kodla görüyor, XSS'in ayrı kategori olduğu yılları bugünkü listeyle karşılaştırıyoruz.

Sürüm notu: Bu yazı OWASP Top 10:2017'yi anlatır; bugünkü karşılıklar OWASP Top 10:2025'e göre verilmiş, kodlar ASP.NET Core 10 (.NET 10) ile denenmiştir.

Önce Tahmin Et

2017 listesinde XML External Entities (XXE) kendi başına dördüncü sıradaydı. 2025 listesinde bu adla bir kategori yok. Bookstore API'ye DOCTYPE içeren bir XML gönderirsen ve kodda XML ayarlarına hiç dokunmadıysan .NET 10 ne yapar: belgeyi işler mi, reddeder mi?

Cevabı birazdan gerçek bir istekle göreceğiz. Bu yazıyla serinin ikinci bloğuna, listenin sürümler arasındaki evrimine geçiyoruz. İlk durak OWASP Top 10:2017: liste neydi, hangi web için yazılmıştı ve o yılın derslerinden hangileri bugün hâlâ geçerli? Kodlar her zamanki gibi yalnız kendi makinende; bu teknikleri yalnız sahibi olduğun ya da yazılı izin aldığın sistemlerde dene.

2017 Listesi

OWASP Top 10:2017 Kasım 2017'de yayımlandı; ilk release candidate Nisan 2017'de çıkmıştı. Bugün proje sayfasında resmî PDF'i bulunan tek sürüm de bu. Numaralar o zaman tek haneliydi: A01 değil A1.

A1 Injection, listenin tepesindeydi: SQL, OS komutu, LDAP gibi bir yorumlayıcıya güvenilmeyen verinin komutun parçası olarak gitmesi. A2 Broken Authentication, oturum ve kimlik doğrulama hatalarını topluyordu. A3 Sensitive Data Exposure, hassas verinin korunmadan saklanması ya da taşınmasıydı. A4 XML External Entities (XXE), XML işleyicilerinin dış varlıkları çözmesinden doğan açıklardı. A5 Broken Access Control, kullanıcının yetkisi olmayan veriye ya da işleme ulaşmasıydı.

A6 Security Misconfiguration, varsayılan ayarlar, açık bırakılmış hata sayfaları ve eksik sertleştirme demekti. A7 Cross-Site Scripting (XSS) ayrı bir kategoriydi. A8 Insecure Deserialization, güvenilmeyen veriden nesne üretmenin riskleriydi. A9 Using Components with Known Vulnerabilities, bilinen açığı olan bağımlılıkları kullanmaktı. A10 Insufficient Logging & Monitoring ise saldırıyı fark etmeyi ve müdahale etmeyi zorlaştıran eksik kayıt ve izlemeydi.

Listeye bakınca dikkat çeken şey, kategorilerin çoğunun bir semptomu ya da bir teknolojiyi adlandırması. "Sensitive Data Exposure" bir sonucu, "XML External Entities" belirli bir formatı anlatıyor. Bir sonraki yazıda 2021'in bu bakışı nasıl kök nedene çevirdiğini göreceğiz.

O Yılların Web'i

2017, tek sayfa uygulamaların ve JSON API'lerin hızla yaygınlaştığı ama kurumsal entegrasyonların hâlâ büyük ölçüde XML ve SOAP üzerinden konuştuğu bir dönemdi. .NET dünyasında çoğu proje .NET Framework'te koşuyordu; ASP.NET Core yeniydi. Sunucu tarafında HTML üreten uygulamalar çoğunluktaydı, bu yüzden XSS'in ayrı bir kategori olması doğaldı. Java tarafında nesne serileştirme ve XML yapılandırması her yerdeydi; XXE ve insecure deserialization'ın listeye girmesi bu tabloyla uyumluydu.

A9'un neden önemli olduğunu aynı yıl gösterdi: Equifax olayında Apache Struts 2'deki CVE-2017-5638 kullanıldı. OWASP bugün bu örneği A03:2025 Software Supply Chain Failures sayfasında hâlâ anıyor. Yani 2017'deki "bilinen açığı olan bileşen" fikri, 2025'te bütün tedarik zincirini kapsayan bir kategoriye büyüdü.

2013'ten 2017'ye

2017 listesi, 2013 sürümünün üzerine kuruldu. Burada yalnız iki belgeyi yan yana koyunca görülen değişiklikleri söyleyeceğim. 2013'te ayrı duran Insecure Direct Object References ve Missing Function Level Access Control, 2017'de A5 Broken Access Control altında birleşti. Bugün IDOR dediğimiz şey bu çatının altında kaldı ve 2021'den beri listenin bir numarası. Cross-Site Request Forgery (CSRF) ile Unvalidated Redirects and Forwards ise 2017'de listeden çıktı. XXE, Insecure Deserialization ve Insufficient Logging & Monitoring listeye yeni girdi.

CSRF'in listeden çıkması "sorun bitti" demek değildi. Bugün CWE-352 (CSRF), A01:2025 Broken Access Control'ün eşlendiği CWE'ler arasında duruyor. Framework'lerin antiforgery desteğini varsayılan hâle getirmesi ve tarayıcıların SameSite cookie'leri bu riski azalttı, ama cookie ile kimlik doğrulama yapan her uygulama hâlâ bu soruyu cevaplamak zorunda.

XXE: .NET 10'un Kapalı Kapısı

Bookstore API'ye XML ile kitap içe aktaran bir endpoint ekleyelim. XML ayarlarında güvenlikle ilgili hiçbir şey yazmıyoruz:

using System.Xml;

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
builder.Services.AddProblemDetails();
WebApplication app = builder.Build();

app.UseExceptionHandler();

app.MapPost("/books/import", async (HttpRequest request, ILogger<Program> logger) =>
{
    List<string> titles = [];
    XmlReaderSettings settings = new() { Async = true };
    try
    {
        using XmlReader reader = XmlReader.Create(request.Body, settings);
        while (await reader.ReadAsync())
        {
            if (reader.NodeType == XmlNodeType.Element && reader.Name == "title")
            {
                titles.Add(await reader.ReadElementContentAsStringAsync());
            }
        }
    }
    catch (XmlException ex)
    {
        // ayrıntı log'a, istemciye yalnız genel bir mesaj
        logger.LogWarning("Rejected XML import: {Reason}", ex.Message);
        return Results.Problem(statusCode: StatusCodes.Status400BadRequest, title: "Invalid XML.");
    }
    return Results.Ok(new { Imported = titles });
});

app.Run();

Önce normal bir belge, sonra içinde zararsız bir iç entity tanımı olan, DOCTYPE'lı bir belge gönderiyoruz:

$ cat books.xml
<books><book><title>Clean Code</title></book></books>
$ curl -i -X POST http://localhost:5181/books/import -H "Content-Type: application/xml" --data-binary @books.xml
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
...
{"imported":["Clean Code"]}

$ cat books-dtd.xml
<?xml version="1.0"?>
<!DOCTYPE books [<!ENTITY publisher "Lab Press">]>
<books><book><title>&publisher;</title></book></books>
$ curl -i -X POST http://localhost:5181/books/import -H "Content-Type: application/xml" --data-binary @books-dtd.xml
HTTP/1.1 400 Bad Request
Content-Type: application/problem+json
...
{"type":"https://tools.ietf.org/html/rfc9110#section-15.5.1","title":"Invalid XML.","status":400,"traceId":"00-c2835a5c...-00"}

Sunucu log'unda sebep açıkça yazıyor:

warn: Program[0]
      Rejected XML import: For security reasons DTD is prohibited in this XML document. To enable DTD processing set the DtdProcessing property on XmlReaderSettings to Parse and pass the settings into XmlReader.Create method.

Açılıştaki sorunun cevabı: reddeder. XmlReaderSettings'in varsayılan DtdProcessing değeri Prohibit. Aynı makinede varsayılanları yazdırınca MaxCharactersFromEntities için 10.000.000 değerini gördüm; entity açılımıyla belleği şişiren "billion laughs" türü saldırılara karşı bir tavan bu. Denemede DtdProcessing'i Parse'a çeksem bile dış kaynaklar varsayılan olarak çözülmedi; bunun için ayrıca bir XmlResolver atamak gerekiyor. Yani 2017'nin dersi bugün framework'ün varsayılanlarına gömülü. Senin işin bu iki ayarı bir gerekçe olmadan değiştirmemek ve DTD'ye gerçekten ihtiyacın yoksa (çoğu API'de yoktur) belgeyi bu hâliyle reddetmek.

2021'de XXE, A05:2021 Security Misconfiguration'a katıldı; 2025'te CWE-611 hâlâ A02:2025 Security Misconfiguration'ın altında. Kategori adı kayboldu, çünkü sorun XML'in kendisi değil, tehlikeli bir özelliğin açık bırakılması.

Insecure Deserialization: Kaldırılan BinaryFormatter

A8:2017'nin .NET'teki en bilinen örneği BinaryFormatter'dı: güvenilmeyen bir byte dizisinden, içinde yazan tipte nesne üretiyordu. .NET 10'da ne olduğuna bakalım:

using System.Runtime.Serialization.Formatters.Binary;

BinaryFormatter formatter = new();
formatter.Serialize(new MemoryStream(), "Clean Code");
$ dotnet build
Program.cs(3,1): error SYSLIB0011: 'BinaryFormatter' is obsolete: 'BinaryFormatter serialization is obsolete and should not be used. See https://aka.ms/binaryformatter for more information.' (https://aka.ms/dotnet-warnings/SYSLIB0011)

Kod derlenmiyor bile. Uyarıyı bastırıp çalıştırınca da sonuç değişmiyor:

using System.Runtime.Serialization.Formatters.Binary;

#pragma warning disable SYSLIB0011
BinaryFormatter formatter = new();
try
{
    formatter.Serialize(new MemoryStream(), "Clean Code");
}
catch (PlatformNotSupportedException ex)
{
    Console.WriteLine($"{ex.GetType().Name}: {ex.Message}");
}
$ dotnet run
PlatformNotSupportedException: BinaryFormatter serialization and deserialization have been removed. See https://aka.ms/binaryformatter for more information.

.NET 9'dan beri runtime'da BinaryFormatter'ın bir implementasyonu yok; API'ler duruyor ama her çağrı bu exception'ı fırlatıyor. Geri getirmenin tek yolu desteklenmeyen ayrı bir NuGet paketi. Ders şu: veriyle birlikte tip bilgisini de kabul eden her serializer aynı riski taşır. System.Text.Json tip adına göre polimorfizm yapmaz, yalnız [JsonDerivedType] ile izin verdiğin tipleri kabul eder. Newtonsoft.Json'da TypeNameHandling'i None dışında bir değere çekmek ise 2017'nin hatasını bugün yeniden yazmaktır. Bu kategori 2021'de A08 Software and Data Integrity Failures'a katıldı ve CWE-502 bugün A08:2025'in altında.

XSS'in Ayrı Kategori Olduğu Yıllar

2017'de XSS'in kendi kategorisi vardı ve bu, o yılların uygulama yapısına uyuyordu: HTML sunucuda üretiliyordu, encoding'i unutmak en sık hataydı. Razor'ın @value ile otomatik HTML encoding yapması, bu hatayı .NET tarafında büyük ölçüde varsayılan olarak kapattı. Angular da interpolation'ı her zaman escape ediyor.

2021'de XSS, A03:2021 Injection'a katıldı; 2025'te CWE-79 A05:2025 Injection'ın altında ve OWASP'ın verisine göre 30 binden fazla CVE ile bu kategorinin en kalabalık CWE'si. Yani XSS ayrı bir başlık olmaktan çıktı ama azalmadı. Değişen bakış: SQL injection da XSS de aynı kök nedenden doğuyor, verinin bir yorumlayıcıda kod olarak ele alınması. Bunu bir sonraki yazıda ayrıntılı konuşacağız.

Sık Düşülen Tuzaklar

Birincisi, eski bir güvenlik dokümanındaki "A4" ifadesini bugünkü listeyle okumak. 2017'nin A4'ü XXE, 2025'in A04'ü Cryptographic Failures. Numarayı her zaman yılıyla birlikte oku ve yaz.

İkincisi, "listeden çıktıysa önemsizdir" sanmak. CSRF 2017'de çıktı, XXE ve XSS 2021'de başka kategorilere katıldı; üçü de bugünkü listede CWE olarak duruyor. Liste daraldıkça kategoriler genişledi.

Üçüncüsü, framework'ün güvenli varsayılanını "bir yerde çalışmıyordu" diye kapatmak. DtdProcessing = DtdProcessing.Parse ya da #pragma warning disable SYSLIB0011 satırını Stack Overflow'dan kopyalamak, 2017'nin iki kategorisini tek satırda geri getirir. Bu satırları kod incelemesinde ayrıca işaretle.

Dördüncüsü, .NET Framework'ten taşınan eski kodu yeni varsayılanlarla aynı sanmak. Eski projelerdeki XmlDocument, XmlTextReader ve serializer kullanımlarını taşırken ayarlarıyla birlikte gözden geçir.

Kontrol Listesi

XML kabul eden her endpoint'te XmlReader'ı varsayılan ayarlarla ya da açıkça DtdProcessing.Prohibit ile kullan; XmlResolver atama. Gövde boyutuna bir sınır koy. Hata ayrıntısını log'a yaz, istemciye genel bir mesaj dön. BinaryFormatter ve onun uyumluluk paketini kullanma; JSON'da tip adına göre polimorfizmi kapalı tut, Newtonsoft kullanıyorsan TypeNameHandling.None'da bırak. Razor'da @Html.Raw, Angular'da bypassSecurityTrust* kullanımlarını gerekçelendir. Eski dokümanlardaki kategori numaralarını yılıyla birlikte güncelle.

Küçük Bir Deneme

★☆☆ Bu yazıdaki /books/import endpoint'ini kendi lab'ında çalıştır ve DOCTYPE'sız belgenin kabul edildiğini, DOCTYPE'lı belgenin 400 ile reddedildiğini gör. Sonra log satırını bulup istemciye giden yanıtla karşılaştır.

★★☆ Endpoint'e MaxCharactersInDocument ile bir tavan ekle ve RequestSizeLimit ile gövde boyutunu sınırla. Büyük bir belgeyle hangi katmanın önce devreye girdiğini gözle.

★★★ 2017 listesinin on maddesini 2025 listesindeki karşılıklarıyla eşleştiren bir tablo çıkar (bu yazıda ve araştırmanda bulduklarınla). Hangi maddenin adı değişti, hangisi başka bir kategoriye katıldı, hangisinin numarası kaydı?

Bir sonraki yazıda 2017'den 2021'e geçiyoruz: semptomdan kök nedene dönüş, Broken Access Control'ün bir numaraya çıkışı, Insecure Design ve SSRF'in gelişi ve metodolojinin nasıl değiştiği.