Merhaba! Bir önceki dersimizde, bir Java uygulamasının güvenlik duruşunu temelden şekillendiren Spring Security filtre zincirlerindeki yanlış yapılandırmaları analiz ettik. Kuralların sıralamasının, "ilk eşleşen kazanır" mantığının ve "hepsini yakala" kuralının eksikliğinin, en sağlam güvenlik mekanizmalarını bile nasıl etkisiz kılabileceğini gördük.
Bu dersimizde, aynı temel prensibi, yani "yürütme sırasının güvenliğe etkisi" temasını, modern web geliştirmenin iki popüler ekosistemine taşıyacağız: Python (Django) ve JavaScript (Express.js). Bu dersin amacı, Express.js middleware sıralamasını ve Django/Flask güvenlik middleware'lerini değerlendirerek güvenlik boşluklarını tespit etmektir. Bu framework'lerde "middleware" olarak adlandırılan yapılar, Spring'deki filtrelerin birebir karşılığıdır. Gelen her isteği işlenmeden önce karşılayan, değiştiren veya reddeden bu ara katman yazılımlarının sıralaması ve mantığı, uygulamanın saldırı yüzeyini doğrudan belirler.
Middleware Felsefesi: Soğan Mimarisi
Herhangi bir modern web framework'ünü analiz ederken, istek/yanıt yaşam döngüsünü katmanlı bir yapı olarak düşünmek faydalıdır. Gelen istek, asıl iş mantığına (Django'da view, Express'te route handler) ulaşmadan önce bir dizi "soğan zarı" gibi katmandan geçer. Her katman, isteği bir sonraki katmana devretmeden önce belirli bir görevi yerine getirir: kimlik doğrulama, oturum yönetimi, veri ayrıştırma, hız limiti kontrolü vb. Yanıt oluşturulduğunda ise bu süreç tersten işler.

Bir web uygulamasında isteğin (Request) ve yanıtın (Response) geçtiği çeşitli middleware katmanlarını gösteren şematik diyagram. İstek, iş mantığının (View) merkezine ulaşmak için Güvenlik, Oturumlar, Kimlik Doğrulama gibi dış katmanlardan geçer; yanıt ise ters yönde hareket eder.
Bu mimari, modüler ve yeniden kullanılabilir güvenlik kontrolleri oluşturmak için son derece güçlüdür. Ancak bu güç, doğru sıralama ve yapılandırma sorumluluğunu da beraberinde getirir. Şimdi bu sorumluluğun Django'da nasıl yönetildiğini inceleyelim.
Django'da Middleware Zinciri
Django, projenin settings.py dosyasında tanımlanan MIDDLEWARE listesi aracılığıyla tüm ara katman yazılımlarını merkezi olarak yönetir. Bu liste, bir isteğin hangi sıralama ile işleneceğinin kesin ve tek kaynağıdır.
Django'nun middleware mimarisinin temellerini, çalışma mantığını ve önemini anlamak için aşağıdaki kaynağı incelemenizi rica ediyorum.
Django Middlewares | Egehan Gündoğdu
Bu blog yazısı, Django'daki middleware kavramını, çalışma mantığını ve kullanım amaçlarını pratik örneklerle açıklıyor. Özellikle sıralamanın önemine yaptığı vurgu, bu derste arayacağımız güvenlik boşluklarını anlamak için kritik bir temel oluşturacaktır.
Lütfen yazıyı baştan sona okuyun. Özellikle şu bölümlere odaklanın: "Middleware nedir?" bölümü, kavramın temel tanımını ve AuthenticationMiddleware gibi yerleşik örnekleri açıklar. "Çalışma sırası ve mantığı" başlığı altındaki diyagram, isteğin yukarıdan aşağıya, yanıtın ise aşağıdan yukarıya doğru katmanlardan geçtiği "soğan" modelini görselleştirir. Bu, analiz için en önemli zihinsel modeldir. "Kullanım amaçları" bölümü, middleware'lerin filtreleme, veri ekleme gibi farklı işlevlerini özetler. Yazının sonundaki "Kayıt edelim" bölümündeki uyarıya dikkat edin: "middleware sıralaması önemlidir" ve "Genellikle güvenlik middlewareleri ilk sıralara yerleştirilir".
Okuduğunuz kaynaktan da anlaşılacağı üzere, settings.py dosyasındaki MIDDLEWARE listesinin sırası kritik öneme sahiptir. Bir kod denetçisi olarak bu listeyi analiz ederken aramanız gereken yaygın güvenlik açıkları şunlardır:
-
Bağımlılıkların Yanlış Sıralanması: Bazı middleware'ler, kendilerinden önce çalışan diğerlerinin çıktısına bağımlıdır. Yanlış sıralama, bu bağımlılığı kırarak hem hataya hem de güvenlik zafiyetine yol açabilir.
- Klasik Örnek:
django.contrib.auth.middleware.AuthenticationMiddleware, isteğerequest.usernesnesini ekler. Ancak bunu yapabilmek için oturum verisine ihtiyaç duyar. Bu veri isedjango.contrib.sessions.middleware.SessionMiddlewaretarafından sağlanır. EğerAuthenticationMiddleware,SessionMiddleware'den önce gelirse, kullanıcı kimliği doğrulanamaz ve yetkilendirme kontrolleri düzgün çalışmaz.
# settings.py - YANLIŞ SIRALAMA MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', # HATA: SessionMiddleware'den önce 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', # ... ] - Klasik Örnek:
-
Güvenlik Middleware'lerinin Etkisizleştirilmesi: Bir geliştirici, test veya hata ayıklama sırasında geçici olarak bir güvenlik middleware'ini (
#ile yorum satırına alarak) devre dışı bırakabilir ve bu değişikliği kod tabanına gönderebilir.django.middleware.csrf.CsrfViewMiddleware'in devre dışı bırakılması, tüm POST isteklerini CSRF saldırılarına karşı savunmasız bırakır.django.middleware.security.SecurityMiddleware'in kaldırılması, HSTS, secure cookies, clickjacking koruması gibi birçok önemli güvenlik başlığını ortadan kaldırır.
-
Mantıksal Hatalar İçeren Özel Middleware'ler: Geliştiricilerin yazdığı özel middleware'ler, genellikle en zengin zafiyet kaynaklarıdır. Örneğin, IP bazlı kısıtlama yapan bir middleware,
X-Forwarded-Forgibi bir HTTP başlığını güvenli bir şekilde ayrıştırmazsa, bir saldırgan bu başlığı manipüle ederek kısıtlamayı atlatabilir. Ya da bir yetkilendirme middleware'i, sadece belirli rolleri kontrol edip beklenmedik bir durumda (örneğin anonim kullanıcı) istisna fırlatırsa ve bu istisna başka bir katman tarafından yanlış yorumlanırsa, yetki atlatmaya yol açabilir.
Express.js'te Middleware Sıralaması
JavaScript ve Node.js dünyasının en popüler framework'ü olan Express.js de tamamen middleware felsefesi üzerine kuruludur. Ancak Django'dan farklı olarak, merkezi bir liste yerine, kodun kendisi yürütme sırasını tanımlar. Middleware'ler, app.use() veya app.get(), app.post() gibi metotlara verilen fonksiyonlardır. Sıralama, bu fonksiyonların koddaki çağrılma sırasıdır.
İşte tipik bir Express.js uygulama yapısı ve kod denetiminde dikkat edilmesi gerekenler:
const express = require('express');
const helmet = require('helmet');
const session = require('express-session');
const passport = require('passport');
const app = express();
// 1. Genel Güvenlik Middleware'leri
app.use(helmet()); // Güvenlik başlıklarını ekler. Genellikle en başta olmalı.
// 2. Body Parser'lar
// Bu middleware'ler req.body nesnesini doldurur. Bunlardan önce çağrılan bir middleware, req.body'ye erişemez.
app.use(express.json()); // JSON body'lerini parse eder.
app.use(express.urlencoded({ extended: true }));
// 3. Oturum Yönetimi
app.use(session({ secret: '...', resave: false, saveUninitialized: true }));
// 4. Kimlik Doğrulama Middleware'leri
app.use(passport.initialize());
app.use(passport.session());
// 5. Özel veya Genel Amaçlı Middleware'ler
// Örnek: Tüm istekleri loglayan bir middleware
app.use((req, res, next) => {
console.log(`Request received for: ${req.path}`);
next(); // Bir sonraki middleware'e geçişi sağlar. next() unutulursa istek takılı kalır!
});
// 6. Rotalar (Routes)
const adminRoutes = require('./routes/admin');
const publicRoutes = require('./routes/public');
// Yanlış Sıralama Tehlikesi!
app.use('/api', adminRoutes); // Eğer bu rotanın korunması gerekiyorsa, auth middleware'i önce gelmeli.
app.use('/public', publicRoutes);
// 7. Hata Yönetimi Middleware'i
// Dört argümanlı yapısıyla diğerlerinden ayrılır ve her zaman en sonda olmalıdır.
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
});
Express.js kodunu denetlerken aranacak güvenlik boşlukları:
-
next()Fonksiyonunun Unutulması veya Koşullu Çağrılması: Bir middleware, işini bitirdiğinde ya bir yanıt göndermeli (res.send(),res.json(), vb.) ya danext()fonksiyonunu çağırarak kontrolü bir sonraki middleware'e devretmelidir. Eğer bir kod yolunext()'i çağırmayı unutursa, istek o noktada takılı kalır ve bu bir hizmet reddi (Denial of Service) zafiyetine yol açabilir. -
Hata Yönetiminin Yanlış Konumlandırılması: Express'te hata yönetimi middleware'i,
(err, req, res, next)şeklinde dört argüman alan özel bir fonksiyondur. Zincirdeki herhangi bir önceki middlewarenext(error)şeklinde bir hata ilenext()'i çağırırsa, Express diğer tüm normal middleware'leri atlayıp doğrudan bu hata yöneticisine gelir. Bu nedenle, hata yöneticisi her zaman diğer tümapp.use()ve rota çağrılarından sonra, en sonda tanımlanmalıdır. Aksi takdirde, kendisinden sonra tanımlanan rotalardaki hataları yakalayamaz. -
Rota ve Yetkilendirme Sıralaması: Spring'deki
requestMatchersgibi, Express'te de middleware'ler belirli yollara (path) uygulanabilir.- Tehlikeli Örnek: Bir kimlik doğrulama middleware'i tüm yollara uygulanırsa ve genel kullanıma açık olması gereken bir rota (
/public,/login) ondan sonra tanımlanırsa, bu genel rota yanlışlıkla korunmuş olur. Daha tehlikelisi, yetkilendirme middleware'i belirli bir yola (app.use('/admin', authMiddleware)) uygulandıktan sonra, daha genel bir rota (app.use('/', openRoutes)) tanımlanırsa, saldırgan/adminyolunu atlatmak için URL normalizasyon trick'leri (e.g.,/./admin) deneyebilir. En katı kurallar (en spesifik yollar) her zaman önce gelmelidir.
- Tehlikeli Örnek: Bir kimlik doğrulama middleware'i tüm yollara uygulanırsa ve genel kullanıma açık olması gereken bir rota (
-
Üçüncü Parti Middleware Güvenliği:
helmet,cors,csurfgibi popüler üçüncü parti middleware'lerin eski veya yanlış yapılandırılmış sürümleri, uygulamanızı zafiyetlere açık bırakabilir. Bir kod denetçisi olarakpackage.jsondosyasını incelemek ve bilinen zafiyetleri olan paketlerin kullanılıp kullanılmadığını kontrol etmek (SCA), bu bağlamda hayati önem taşır.
Flask'a Kısa Bir Bakış
Flask, Django ve Express gibi tam bir middleware "zinciri" sunmaz. Bunun yerine, before_request ve after_request gibi dekoratörler (decorators) kullanarak istek öncesi ve sonrası işlemler için fonksiyonlar kaydeder.
from flask import Flask, request
app = Flask(__name__)
@app.before_request
def check_user_auth():
# Bu fonksiyon her istekten önce çalışır.
if 'admin' not in request.path and not getattr(request, 'user', None):
# Yetkilendirme mantığı...
pass
@app.before_request
def log_request():
# Bu da her istekten önce çalışır.
# Flask, aynı türdeki birden fazla fonksiyonun çalışma sırasını garanti etmez.
app.logger.info(f"Request to {request.path}")
Flask'te aynı türdeki (örn. iki farklı @app.before_request) fonksiyonların çalışma sırası garanti edilmediği için, sıralamaya dayalı karmaşık güvenlik mantıkları kurmak daha zordur ve hataya açıktır. Bu nedenle Flask'ta güvenlik kontrolleri genellikle her bir view fonksiyonuna özel dekoratörler olarak uygulanır. Bu, merkezi kontrolü azaltır ancak her endpoint'in kendi güvenliğinden sorumlu olmasını sağlar.
Özet ve Sonraki Adımlar
Bu derste, farklı felsefelere sahip üç büyük web framework'ünün (Django, Express, Flask) istek işleme modellerini güvenlik perspektifinden karşılaştırdık. Temel prensibin değişmediğini gördük: Bir isteğin yaşam döngüsünü kontrol eden ara katmanların sırası, kapsamı ve mantığı, uygulamanın genel güvenlik duruşunu belirler.
Anahtar Çıkarımlar:
- Django: Güvenlik,
settings.pydosyasındakiMIDDLEWARElistesinin doğru sıralanmasına bağlıdır. Bağımlılıklar ve güvenlik middleware'lerinin konumu kritiktir. - Express.js: Sıralama,
app.use()çağrılarının koddaki sırasıyla belirlenir. Hata yönetimi middleware'inin en sonda olması venext()fonksiyonunun doğru kullanılması hayati önem taşır. - Genel Prensip: İster bir listede, ister kod akışında tanımlansın, middleware zincirini analiz etmek, en kısıtlayıcı kuralların en önce uygulandığından ve hiçbir güvenlik kontrolünün yanlışlıkla atlanmadığından emin olmayı gerektirir.
- Flask: Merkezi zincir yerine dekoratörler kullanır; bu da kontrollerin daha dağıtık olmasına yol açar.
Bu dersle birlikte, farklı framework'lerdeki konfigürasyon ve sıralama kaynaklı zafiyetleri tespit etme yeteneğinizi genişlettik. Bir sonraki dersimizde, API'lere özgü güvenlik konularına daha derin bir dalış yapacağız. GraphQL API'lerinde introspection kontrolü, derinlik/maliyet limiti eksiklikleri ve aşırı veri ifşası zafiyetlerini analiz ederek bu modern API teknolojisinin getirdiği yeni saldırı yüzeylerini ve kod denetimi stratejilerini keşfedeceğiz.