Application Security

Mikroservisleri Güçlendirme: OAuth2 Kapsam Zorlaması ve Granüler Hız Sınırlamayı Ustalıkla Yönetmek

Dağıtık sistemler çağında güvenlik artık bir sonradan düşünce değil, temel bir direk haline gelmiştir. Kurumlar monolitik mimarilerden mikroservislere geçiş yaptıkça, saldırı yüzeyi katlanarak artar. Tek bir ihlal edilmiş servis, tüm ekosistemde hassas verilerin ifşa olmasına neden olabilir. Bu riskleri azaltmak için geliştiriciler temel kimlik doğrulamadan öteye geçip, sağlam yetkilendirme stratejileri uygulamak zorundadır. Bunun için en kritik kontrollerden ikisi sıkı OAuth2 kapsam zorlaması ve granüler hız sınırlamadır. Bu yazı, API altyapınızı güvence altına almak için bu mekanizmaları nasıl etkili bir şekilde uygulayabileceğinizi incelemektedir.

Temel: OAuth2 Kapsam Zorlamasını Anlamak

OAuth2, yetkilendirme için endüstri standardıdır; ancak birçok uygulama sadece geçerli bir erişim tokenının doğrulanmasıyla sınırlı kalır. Bir mikroservis ortamında, bir token kullanıcının birden fazla servise erişmesini sağlayabilir, ancak tüm uç noktalar aynı düzeyde ayrıcalık gerektirmez. İşte burada kapsamlar (scopes) hayati önem kazanır. Kapsamlar, bir erişim tokenının hangi düzeyde erişim sağladığını tanımlar; örneğin read:profile veya write:orders. Açık bir zorlama olmadan, geniş izinlere sahip bir token, kullanıcının niyet ettiği bağlamın dışında eylemler gerçekleştirmek için kötüye kullanılabilir.

Kapsam zorlaması, gelen her isteği araştıran bir ara katman (middleware) katmanı gerektirir. Bu ara katman, tokenı (genellikle bir JWT) ayrıştırmalı, imzasını doğrulamalı ve ardından scope iddiasını çıkarmalıdır. Ardından, uç nokta için gerekli kapsamları, token içindeki kapsamlarla karşılaştırır.

FastAPI kullanarak kapsamları zorlamak için pratik bir Python ara katman işlevi örneği:

from fastapi import Security, Depends, HTTPException, status
from fastapi.security import OAuth2AuthorizationCodeBearer
from jose import jwt, JWTError
import os

# Belirli rotalar için gerekli kapsamları tanımlayın
REQUIRED_SCOPES = {
    "GET /users": ["read:users"],
    "POST /orders": ["write:orders", "read:users"]
}

oauth2_scheme = OAuth2AuthorizationCodeBearer(
    authorizationUrl="https://auth.example.com/oauth/token",
    tokenUrl="https://auth.example.com/oauth/token",
    scopes={"read:users": "Kullanıcı verilerini oku", "write:orders": "Sipariş oluştur"}
)

async def verify_scope(
    token: str = Security(oauth2_scheme),
    required_scope: str = None
):
    # Üretim ortamında, gerçek JWT gizli anahtarınızla ayrıştırın
    try:
        payload = jwt.decode(token, os.getenv("SECRET_KEY"), algorithms=["HS256"])
        token_scopes = payload.get("scope", "").split()
        
        if required_scope and required_scope not in token_scopes:
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Bu kaynağa erişmek için yeterli yetkiniz yok."
            )
        return payload
    except JWTError:
        raise HTTPException(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail="Geçersiz kimlik doğrulama bilgileri."
        )

# Rota içinde kullanım örneği
@app.get("/users", dependencies=[Depends(verify_scope)])
async def get_users():
    return {"users": ["Alice", "Bob"]}

Kaynakları Koruma: Granüler Hız Sınırlama Stratejileri

Mükemmel bir yetkilendirme olsa bile, API'ler Hizmet Reddi (DoS) saldırılarına ve meşru ancak kötü davranan istemcilerden kaynaklanan istismarlara karşı savunmasız kalır. Yaygın bir hata, hız sınırlamayı yalnızca ağ geçidi (gateway) seviyesinde uygulamaktır. Bu bir temel sağlar, ancak daha etkili bir strateji, mikroservis seviyesinde granüler hız sınırlama içermektir. Bu sayede kullanıcı rollerine, belirli API uç noktalarına veya kaynak hassasiyetine göre farklı limitler tanımlayabilirsiniz.

Örneğin, bir "toplu dışa aktarma" (bulk export) uç noktası, basit bir "profil al" çağrısına kıyasla çok daha sıkı bir limite sahip olmalıdır. Ayrıca, bir premium kullanıcı, ücretsiz katman bir kullanıcıya göre daha yüksek bir kota sahibi olabilir. Hız sınırlama mantığını ağ geçidinden ayırarak, belirli mikroservisleri bağımsız olarak koruma esnekliğine sahip olursunuz.

Bunu uygularken, istek sayılarını saklamak için Redis gibi dağıtık bir önbellek kullanmayı düşünün. Bu, mikroservis örneklerinden oluşan bir küme boyunca tutarlılığı sağlar.

import time
import redis
from functools import wraps

redis_client = redis.Redis(host='localhost', port=6379, db=0)

def rate_limit(limit_per_minute, unique_key_header="X-Client-ID"):
    def decorator(func):
        @wraps(func)
        async def wrapper(request, *args, **kwargs):
            client_id = request.headers.get(unique_key_header, "anonymous")
            now = int(time.time())
            window = now // 60
            
            # İstemci ve uç nokta için benzersiz bir anahtar oluşturun
            rate_key = f"ratelimit:{request.url.path}:{client_id}:{window}"
            
            # Sayacı artırın
            count = redis_client.incr(rate_key)
            
            if count == 1:
                # Anahtarın süresini, dakika penceresinin sonuna ayarlayın
                redis_client.expire(rate_key, 60)
            
            if count > limit_per_minute:
                raise HTTPException(
                    status_code=status.HTTP_429_TOO_MANY_REQUESTS,
                    detail="Hız sınırlaması aşıldı. Lütfen daha sonra tekrar deneyin."
                )
            
            return await func(request, *args, **kwargs)
        return wrapper
    return decorator

# Kullanım
@app.post("/export/bulk", dependencies=[Depends(rate_limit(10))])
async def bulk_export():
    # Pahalı bir işlem
    pass

Güvenlikte Sinerji

API güvenliğinin gerçek gücü, bu iki kontrolün birleşiminde yatar. OAuth2 kapsamları, yalnızca doğru varlıkların belirli kaynaklara erişmesini sağlarken, granüler hız sınırlama, bu varlıkların sistemi aşamamasını garanti eder. Yetkisiz bir kullanıcı bir uça noktaya erişmeye çalıştığında, kapsam zorlaması tarafından hemen engellenir. Meşru bir kullanıcı bir uça noktayı istismar etmeye çalıştığında, hız sınırlayıcı, servis kullanılabilirliğini etkilemeden trafiği kısıtlar.

Yetkilendirme ve hız sınırlamanın yalnızca merkezi bir ağ geçidine güvenmek yerine mikroservis seviyesinde uygulandığı "derinlikte savunma" (defense in depth) yaklaşımını benimseyerek, dirençli bir güvenlik pozisyonu oluşturursunuz. Bu, olası saldırıların patlama yarıçapını azaltır ve mikroservislerinizin ağır yük veya kötü niyetli niyetler altında sağlam kalmasını sağlar.

Sonuç

Mikroservisleri güvence altına almak, tek seferlik bir yapılandırma değil, sürekli bir yolculuktur. Sıkı OAuth2 kapsam zorlaması, yetkisiz erişimi hassas verilere karşı engellerken, granüler hız sınırlama istismarı ve hizmet reddi tehditlerini azaltır. Geliştiriciler olarak, bu uygulamaları CI/CD pipeline'larımıza ve kod incelemelerimize yerleştirmeliyiz. Bunu yaparak sadece verilerimizi korumakla kalmaz, aynı zamanda kullanıcılarımızla güven inşa eder ve giderek daha düşmanca hale gelen dijital bir manzara içinde uygulamalarımızın güvenilir ve güvenli kalmasını sağlarız.

Share: