Yapay Zeka Mühendisleri İçin FastAPI ve JWT Kimlik Doğrulama: Güvenli API Geliştirme Rehberi
Yapay zeka modellerini servis etmek, günümüzün en heyecan verici ve bir o kadar da kritik görevlerinden biridir. Ancak bu modellerin sunduğu API’lar, genellikle hassas verilerle etkileşime girer veya kritik işlevleri yerine getirir. Peki, bu değerli API’ları yetkisiz erişimden nasıl koruyabiliriz? İşte bu noktada, FastAPI’nin sunduğu kolaylıklar ve JSON Web Token (JWT) tabanlı kimlik doğrulama mekanizması devreye giriyor. Bu makalede, yapay zeka mühendislerinin FastAPI uygulamalarında JWT kullanarak güvenli ve ölçeklenebilir kimlik doğrulama sistemlerini nasıl kuracaklarını adım adım inceleyeceğiz.
API Güvenliği Neden Bu Kadar Önemli ve JWT Nedir?
Günümüzde yapay zeka (YZ) destekli uygulamalar, finans, sağlık, eğitim ve otonom sistemler gibi pek çok alanda kritik roller üstlenmektedir. Bu uygulamaların temelini oluşturan YZ API’ları, genellikle kişisel veriler, finansal bilgiler veya fikri mülkiyet değeri yüksek algoritmalar gibi hassas bilgilere erişim sağlar veya bu bilgileri işler. Dolayısıyla, bu API’ların güvenliği, sadece teknik bir gereklilik olmaktan öte, yasal uyumluluk (KVKK, GDPR gibi), itibar koruma ve iş sürekliliği açısından hayati bir önem taşır. Yetkisiz erişim, veri ihlallerine, hizmet kesintilerine, dolandırıcılığa ve ciddi mali kayıplara yol açabilir. Örneğin, bir sağlık uygulamasındaki YZ teşhis API’sına yetkisiz erişim, hastaların özel bilgilerinin sızdırılmasına veya yanlış teşhislerin manipüle edilmesine neden olabilir. Benzer şekilde, bir finansal dolandırıcılık tespit modelinin API’sına yapılan yetkisiz müdahale, sistemin zafiyetlerini ortaya çıkararak büyük ölçekli finansal kayıplara yol açabilir. Bu senaryolar, API güvenliğinin sadece “olması gereken” bir özellik değil, aynı zamanda “olmak zorunda olan” temel bir bileşen olduğunu açıkça göstermektedir. Bu bağlamda, kimlik doğrulama (authentication) ve yetkilendirme (authorization) mekanizmaları, API güvenliğinin temel taşlarını oluşturur.
Peki, bu kimlik doğrulama sürecini nasıl daha etkili ve güvenli hale getirebiliriz? İşte burada JSON Web Token (JWT) devreye giriyor. JWT, istemciler ve sunucular arasında güvenli bir şekilde bilgi alışverişi yapmak için kullanılan kompakt, URL güvenli bir yöntemdir. Temelde bir JSON nesnesi olan JWT, dijital olarak imzalanmış olduğu için içeriğinin değiştirilmediği ve gönderenin kimliğinin doğrulandığı garanti edilir. JWT’ler, özellikle mikroservis mimarilerinde ve durum bilgisi olmayan (stateless) API’larda popüler bir seçimdir çünkü sunucunun her istekte kullanıcı oturum bilgilerini saklamasına gerek kalmaz. Bu da sunucu tarafındaki yükü azaltır ve yatay ölçeklenebilirliği artırır.
Bir JWT, üç ana bölümden oluşur: Header (Başlık), Payload (Yük) ve Signature (İmza). Her bir bölüm, base64url formatında kodlanır ve noktalarla ayrılır.
- Header (Başlık): Token’ın türünü (örneğin, JWT) ve kullanılan imzalama algoritmasını (örneğin, HS256 veya RS256) belirtir.
- Payload (Yük): Token’ın içerdiği iddiaları (claims) barındırır. Bu iddialar, kullanıcı kimliği, rolleri, token’ın süresi (exp), yayıncı (iss) gibi bilgileri içerebilir. Payload kısmı, hassas veriler için şifrelenmemiştir, bu yüzden buraya çok özel ve gizli bilgiler koymaktan kaçınılmalıdır.
- Signature (İmza): Header, Payload ve sunucu tarafında saklanan gizli bir anahtar (secret key) kullanılarak oluşturulur. Bu imza, token’ın bütünlüğünü ve orijinalliğini doğrulamak için kullanılır. Eğer token’ın içeriği (Header veya Payload) değiştirilirse, imza geçersiz hale gelir ve sunucu token’ı reddeder.
JWT’nin çalışma prensibi oldukça basittir: Kullanıcı kimlik bilgileriyle (kullanıcı adı, parola) giriş yaptığında, sunucu bu bilgileri doğrular ve başarılı olursa bir JWT oluşturur. Bu token, istemciye gönderilir ve istemci, sonraki her istekte bu token’ı yetkilendirme başlığında (Authorization: Bearer <token>) sunucuya geri gönderir. Sunucu, her istekte token’ın imzasını doğrular ve geçerliyse, token’daki payload bilgilerini kullanarak kullanıcının yetkisini kontrol eder. Bu sayede, sunucu her istek için veritabanına sorgu yapmaktan veya oturum yönetimi için ek kaynaklar ayırmaktan kurtulur, bu da özellikle yüksek trafikli YZ API’ları için büyük bir performans avantajı sağlar.
FastAPI ile JWT Kimlik Doğrulama Ortamının Kurulumu ve Temel Yapılandırma
FastAPI, modern, hızlı (yüksek performanslı), web API’ları oluşturmak için Python tabanlı bir framework (yazılım çerçevesidir). Asenkron programlama desteği, otomatik dokümantasyon (OpenAPI/Swagger UI) ve bağımlılık enjeksiyonu (Dependency Injection) gibi özellikleriyle yapay zeka mühendisleri için API geliştirmeyi oldukça kolaylaştırır. JWT tabanlı kimlik doğrulama sistemini FastAPI ile entegre etmek için öncelikle gerekli kütüphaneleri kurmamız ve temel bir yapılandırma oluşturmamız gerekmektedir. Bu bölümde, projemizi sıfırdan oluşturacak, gerekli paketleri kuracak ve kimlik doğrulama sürecinin temelini atacak ilk adımları atacağız.
Gerekli Paketlerin Kurulumu
FastAPI projemizi başlatmak için bir sanal ortam oluşturmak iyi bir pratiktir. Bu, projemizin bağımlılıklarını sistem genelindeki Python paketlerinden izole etmemizi sağlar. Ardından, FastAPI’nin kendisi, bir ASGI sunucusu olan Uvicorn, JWT oluşturma ve doğrulama için python-jose kütüphanesi ve parolaları güvenli bir şekilde hash’lemek için passlib paketini kurmamız gerekecek. bcrypt, passlib‘in güçlü bir hash algoritmasıdır ve parola güvenliği için şiddetle tavsiye edilir.
# Sanal ortam oluşturma (isteğe bağlı ama önerilir)
python -m venv venv
source venv/bin/activate # Linux/macOS
# venv\Scripts\activate # Windows
# Gerekli paketleri kurma
pip install fastapi uvicorn python-jose passlib[bcrypt]
Bu komutları çalıştırdıktan sonra, projemizin temel bağımlılıkları yüklenmiş olacaktır. Şimdi, bu paketleri kullanarak bir FastAPI uygulaması oluşturmaya başlayabiliriz.
Temel FastAPI Uygulama Yapısı ve Ayarlar
Projemiz için bir main.py dosyası oluşturalım. Bu dosya, FastAPI uygulamasının ana giriş noktası olacaktır. Ayrıca, güvenlik anahtarları ve token süreleri gibi yapılandırma ayarlarını yönetmek için ayrı bir config.py dosyası oluşturmak, kodumuzu daha düzenli ve güvenli hale getirecektir.
Öncelikle config.py dosyasını oluşturalım:
# config.py
import os
# Güvenlik anahtarı: JWT'leri imzalamak ve doğrulamak için kullanılır.
# Gerçek uygulamalarda bu değeri bir ortam değişkeninden almalısınız.
SECRET_KEY = os.getenv("SECRET_KEY", "gizli_anahtar_buraya_gelmeli_ve_cok_uzun_olmalı")
ALGORITHM = "HS256" # JWT imzalama algoritması
ACCESS_TOKEN_EXPIRE_MINUTES = 30 # Erişim token'ının geçerlilik süresi (dakika)
REFRESH_TOKEN_EXPIRE_DAYS = 7 # Yenileme token'ının geçerlilik süresi (gün)
SECRET_KEY değeri, kesinlikle tahmin edilemez ve uzun olmalıdır. Üretim ortamında bu değeri doğrudan koda gömmek yerine, ortam değişkenlerinden (environment variables) alınması kritik bir güvenlik uygulamasıdır. Örneğin, Docker kullanıyorsanız docker-compose.yml dosyasında veya Kubernetes kullanıyorsanız Secret objeleri aracılığıyla bu anahtarı yönetebilirsiniz.
Kullanıcı Modelleri ve Şifreleme
Kimlik doğrulama sistemi için kullanıcı verilerini temsil edecek Pydantic modellerine ihtiyacımız var. Ayrıca, kullanıcı parolalarını düz metin olarak saklamak yerine, güvenli bir şekilde hash’lememiz (kriptografik özetleme) gerekir. passlib kütüphanesi bu konuda bize yardımcı olacaktır.
# main.py veya ayrı bir models.py dosyası
from pydantic import BaseModel
from typing import Optional
from passlib.context import CryptContext
# Şifre bağlamı oluşturma
# bcrypt, güçlü bir parola hash algoritmasıdır.
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
class User(BaseModel):
username: str
email: Optional[str] = None
full_name: Optional[str] = None
disabled: Optional[bool] = False
hashed_password: str # Hashlenmiş parola
class UserInDB(User):
pass # Veritabanındaki kullanıcı modelimiz, User ile aynı olabilir
class Token(BaseModel):
access_token: str
token_type: str
class TokenData(BaseModel):
username: Optional[str] = None
# Parola hashleme ve doğrulama fonksiyonları
def get_password_hash(password: str) -> str:
return pwd_context.hash(password)
def verify_password(plain_password: str, hashed_password: str) -> bool:
return pwd_context.verify(plain_password, hashed_password)
# Geçici kullanıcı veritabanı (gerçek uygulamada bir veritabanı kullanılmalı)
# Parolalar hashlenmiş olarak saklanmalı
fake_users_db = {
"testuser": {
"username": "testuser",
"email": "test@example.com",
"full_name": "Test User",
"disabled": False,
"hashed_password": get_password_hash("supersecret"),
}
}
def get_user(username: str):
if username in fake_users_db:
user_dict = fake_users_db[username]
return UserInDB(**user_dict)
return None
Yukarıdaki kod bloğunda, User ve UserInDB modelleri, kullanıcı bilgilerini temsil ederken, Token ve TokenData modelleri JWT ile ilgili verileri yapılandırmak için kullanılır. pwd_context objesi, parolaları hash’lemek ve doğrulamak için gerekli fonksiyonları sağlar. fake_users_db ise, gerçek bir veritabanı entegrasyonuna geçmeden önce test amaçlı kullanabileceğimiz basit bir bellek içi (in-memory) veri deposudur. Gerçek bir projede PostgreSQL, MongoDB gibi kalıcı bir veritabanı kullanmanız gerekecektir. Her bölüm minimum 300 kelime hedefine ulaşmak için bu temel yapılandırma adımlarını detaylandırmak, her bir bileşenin neden önemli olduğunu açıklamak ve olası güvenlik risklerine değinmek kritik öneme sahiptir. Örneğin, SECRET_KEY‘in güvenliği, bcrypt‘in seçilme nedenleri ve fake_users_db yerine gerçek bir veritabanının gerekliliği gibi noktalar üzerinde durulabilir. Bu başlangıç adımları, FastAPI ile güvenli bir API geliştirme yolculuğunun sağlam temellerini oluşturur.
Kullanıcı Kimlik Doğrulama ve JWT Oluşturma Süreçleri
FastAPI ile JWT tabanlı kimlik doğrulamanın temelini attıktan sonra, şimdi kullanıcıların sisteme giriş yapmasını ve başarılı bir kimlik doğrulamasının ardından JWT token’ları almasını sağlayacak mekanizmaları inşa edelim. Bu süreç, kullanıcının kimlik bilgilerini doğrulama, JWT oluşturma ve bu token’ı istemciye güvenli bir şekilde iletme adımlarını içerir. FastAPI’nin bağımlılık enjeksiyonu (Dependency Injection) sistemi, bu süreci oldukça modüler ve yönetilebilir hale getirecektir.
Token Oluşturma Fonksiyonu
İlk olarak, JWT oluşturmak için bir yardımcı fonksiyona ihtiyacımız var. Bu fonksiyon, kullanıcının kimliğini ve token’ın geçerlilik süresini içeren bir payload alacak ve bu payload’ı config.SECRET_KEY ile imzalayarak bir JWT döndürecektir.
# main.py (veya ayrı bir auth.py dosyası)
from datetime import datetime, timedelta
from typing import Optional
from jose import JWTError, jwt
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm
from config import SECRET_KEY, ALGORITHM, ACCESS_TOKEN_EXPIRE_MINUTES # config.py'dan import ettik
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token") # "token" endpoint'i login için kullanılacak
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
Bu fonksiyon, token’ın payload’ına exp (expiration time – son kullanma zamanı) alanını ekler. Bu alan, token’ın ne zaman geçersiz olacağını belirtir ve güvenlik açısından önemlidir. Token’ın kısa ömürlü olması, çalınması durumunda potansiyel zararı minimize eder. Bu yüzden ACCESS_TOKEN_EXPIRE_MINUTES değerini makul bir sürede tutmak önemlidir.
Kullanıcı Giriş (Login) Endpoint’i
Şimdi, kullanıcıların kimlik bilgilerini (kullanıcı adı ve parola) göndererek bir access token alabilecekleri bir giriş endpoint’i oluşturalım. FastAPI, OAuth2PasswordRequestForm bağımlılığını kullanarak form verilerini kolayca almanızı sağlar.
# main.py
from fastapi import FastAPI
from fastapi.responses import JSONResponse
app = FastAPI()
# ... (Yukarıdaki User, UserInDB, Token, TokenData, get_password_hash, verify_password, get_user, create_access_token tanımlamaları buraya gelecek) ...
@app.post("/token", response_model=Token)
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
user = get_user(form_data.username)
if not user or not verify_password(form_data.password, user.hashed_password):
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Kullanıcı adı veya parola hatalı",
headers={"WWW-Authenticate": "Bearer"},
)
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user.username}, expires_delta=access_token_expires
)
return {"access_token": access_token, "token_type": "bearer"}
Bu /token endpoint’i, kullanıcıdan kullanıcı adı ve parola alır. get_user fonksiyonu ile kullanıcıyı bellek içi veritabanımızdan bulur ve verify_password ile parolasını doğrular. Eğer kimlik bilgileri yanlışsa, HTTPException yükseltilerek 401 Yetkisiz (Unauthorized) hatası döndürülür. Başarılı olursa, create_access_token fonksiyonu ile bir JWT oluşturulur ve bu token istemciye JSON yanıtı olarak gönderilir. "sub" (subject) claim’i genellikle kullanıcının benzersiz kimliğini (örneğin, kullanıcı adı) tutar.
JWT Token Doğrulama Bağımlılığı
Artık kullanıcılar token alabiliyor, ancak bu token’ları korumalı rotalarda nasıl kullanacaklar? FastAPI’nin bağımlılık enjeksiyonu sistemi, gelen isteğin yetkilendirme başlığındaki JWT’yi alıp doğrulayacak bir fonksiyon oluşturmamıza olanak tanır. Bu fonksiyon, her korumalı endpoint’e kolayca eklenebilir.
# main.py
# ... (Diğer import'lar ve fonksiyonlar) ...
async def get_current_user(token: str = Depends(oauth2_scheme)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Kimlik bilgileri doğrulanamadı",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
token_data = TokenData(username=username)
except JWTError:
raise credentials_exception
user = get_user(token_data.username)
if user is None:
raise credentials_exception
return user
async def get_current_active_user(current_user: User = Depends(get_current_user)):
if current_user.disabled:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="Devre dışı bırakılmış kullanıcı")
return current_user
get_current_user fonksiyonu, oauth2_scheme bağımlılığını kullanarak isteğin başlığından JWT’yi alır. Ardından, jwt.decode ile token’ı çözümler ve imzasını doğrular. Eğer token geçerliyse, payload’dan kullanıcı adını (sub claim) çıkarır. Herhangi bir hata durumunda (geçersiz token, süresi dolmuş token, imza hatası vb.), HTTPException yükseltilir. get_current_active_user ise, kullanıcının aktif olup olmadığını kontrol eden ek bir katmandır. Bu bağımlılıklar sayesinde, herhangi bir API endpoint’ine basitçe current_user: User = Depends(get_current_active_user) ekleyerek o endpoint’i koruma altına alabiliriz. Bu modüler yaklaşım, kod tekrarını önler ve güvenlik mantığını merkezi bir yerde tutar. Bu detaylı adımlar, bir YZ mühendisinin FastAPI uygulamalarında kullanıcı kimlik doğrulamasını ve JWT oluşturma/doğrulama süreçlerini eksiksiz bir şekilde anlamasını ve uygulamasını sağlar. Özellikle, token yaşam döngüsü yönetimi ve hata işleme mekanizmaları, sağlam bir güvenlik altyapısı için vazgeçilmezdir.
Korumalı Rotalar ve Yapay Zeka Servislerinin Güvenliği
FastAPI’de JWT kimlik doğrulama mekanizmasını başarıyla kurduktan sonra, sıra geldi yapay zeka servislerimizi ve diğer hassas rotalarımızı bu mekanizma ile koruma altına almaya. Bu, sadece yetkili kullanıcıların veya sistemlerin YZ modellerimize erişebilmesini ve onlarla etkileşim kurabilmesini sağlayarak veri güvenliğini ve model bütünlüğünü garanti altına alır. FastAPI’nin bağımlılık enjeksiyonu sistemi sayesinde, bu koruma katmanını uygulamak oldukça basittir ve kod tekrarını minimize eder.
Basit Bir Korumalı Rota Oluşturma
Daha önce tanımladığımız get_current_active_user bağımlılığını kullanarak, herhangi bir FastAPI rotasını kolayca koruma altına alabiliriz. Bu bağımlılık, isteğin yetkilendirme başlığında geçerli bir JWT olup olmadığını kontrol eder ve eğer varsa, kimliği doğrulanmış aktif kullanıcı nesnesini rotaya enjekte eder. Eğer token yoksa, geçersizse veya kullanıcı devre dışı bırakılmışsa, FastAPI otomatik olarak 401 veya 400 durum kodlarıyla uygun bir hata yanıtı döndürür.
# main.py
# ... (Yukarıdaki tüm tanımlamalar ve app = FastAPI() satırı) ...
@app.get("/users/me/", response_model=User)
async def read_users_me(current_user: User = Depends(get_current_active_user)):
"""
Mevcut oturum açmış kullanıcının bilgilerini döndürür.
Bu rota JWT ile korunmaktadır.
"""
return current_user
# Yapay Zeka Modeli Tahmin Servisi Örneği
class PredictionRequest(BaseModel):
text: str
class PredictionResponse(BaseModel):
prediction: str
confidence: float
@app.post("/ai/predict/", response_model=PredictionResponse)
async def ai_predict(request: PredictionRequest, current_user: User = Depends(get_current_active_user)):
"""
Bir metin girdisi alır ve yapay zeka modelimizden bir tahmin döndürür.
Sadece yetkili kullanıcılar bu servisi kullanabilir.
"""
print(f"Kullanıcı '{current_user.username}' tarafından AI tahmin isteği alındı: {request.text}")
# Burada gerçek yapay zeka modeli çağrısı yapılır.
# Örnek olarak basit bir metin analizi yapalım:
if "kötü" in request.text.lower() or "negatif" in request.text.lower():
prediction = "Negatif Duygu"
confidence = 0.95
elif "iyi" in request.text.lower() or "pozitif" in request.text.lower():
prediction = "Pozitif Duygu"
confidence = 0.88
else:
prediction = "Nötr Duygu"
confidence = 0.70
return {"prediction": prediction, "confidence": confidence}
# Uygulamayı çalıştırma (terminalde: uvicorn main:app --reload)
/users/me/ rotası, kimliği doğrulanmış kullanıcının kendi bilgilerini almasını sağlar. Bu, kullanıcı profil sayfaları veya yetkilendirme kontrolleri için sıkça kullanılan bir desendir. Daha da önemlisi, /ai/predict/ rotası, bir yapay zeka modelinin tahmin servisini temsil eder. Bu servise yapılan her istek, get_current_active_user bağımlılığı sayesinde önce kimlik doğrulama kontrolünden geçer. Bu sayede, yalnızca geçerli bir JWT’ye sahip, aktif kullanıcılar bu tahmin servisini kullanabilir. İçerideki print ifadesi, hangi kullanıcının isteği yaptığını loglamanıza olanak tanır, bu da denetim (auditing) ve güvenlik izleme açısından değerlidir.
Gerçek Dünya Senaryosu: Finansal Dolandırıcılık Tespit API’sı
Bir bankanın veya finansal kurumun, gerçek zamanlı işlem verilerini analiz ederek dolandırıcılık faaliyetlerini tespit eden bir YZ modeli geliştirdiğini varsayalım. Bu modelin API’sı, saniyede binlerce finansal işlemi işleyebilir. Bu API’ya yetkisiz erişim, hem müşteri verilerinin ifşa olmasına hem de bankanın büyük finansal kayıplar yaşamasına neden olabilir. İşte bu senaryoda JWT kimlik doğrulama hayati bir rol oynar:
- Sadece Yetkili Sistemler: API, sadece bankanın kendi iç sistemleri (örneğin, işlem işleme motorları veya risk yönetim panelleri) tarafından erişilebilir olmalıdır. Bu sistemler, bir kez giriş yaptıktan sonra aldıkları JWT’yi her API isteğiyle birlikte gönderirler.
- Veri Bütünlüğü: JWT’nin imzası sayesinde, API’ya gönderilen isteklerin yetkili bir kaynaktan geldiği ve yolda manipüle edilmediği garanti edilir. Bu, dolandırıcılık tespit modelinin doğru ve güvenilir verilere dayanarak çalıştığı anlamına gelir.
- Performans ve Ölçeklenebilirlik: JWT’ler durum bilgisi olmayan (stateless) olduğu için, her API isteğinde veritabanına sorgu yapmaya gerek kalmaz. Bu, yüksek işlem hacmine sahip finansal sistemler için kritik öneme sahip olan performansı artırır ve API’nın kolayca ölçeklenmesini sağlar.
- Rol Tabanlı Erişim Kontrolü (RBAC): İleri düzeyde, JWT payload’ına “admin”, “analyst”, “system” gibi roller eklenebilir. Böylece, bazı YZ modellerine sadece “admin” rolüne sahip kullanıcıların erişmesine izin verilebilirken, “analyst” rolüne sahip kullanıcılar sadece belirli raporlama API’larına erişebilir. Örneğin, bir “model_update” API’sına sadece “admin” yetkisine sahip kullanıcıların erişmesi sağlanabilirken, “predict” API’sına tüm yetkili sistemlerin erişimi olabilir.
Bu tür bir senaryoda, FastAPI’nin sunduğu basit ve güçlü bağımlılık enjeksiyonu mekanizması, hem geliştirme hızını artırır hem de sağlam bir güvenlik katmanı sağlar. YZ mühendisleri, model geliştirme ve optimizasyonuna odaklanırken, API’larının arkasındaki güvenlik altyapısının FastAPI ve JWT ile güvenilir bir şekilde sağlandığından emin olabilirler. Bu, YZ ürünlerinin pazara daha hızlı ve daha güvenli bir şekilde sunulmasına olanak tanır.
İleri Düzey JWT Kullanımı ve Güvenlik İpuçları
FastAPI ve JWT ile temel kimlik doğrulama sistemini kurduk. Ancak gerçek dünya uygulamalarında, özellikle yüksek güvenlik gerektiren veya büyük ölçekli yapay zeka servislerinde, daha gelişmiş senaryolar ve ek güvenlik önlemleri gerekebilir. Bu bölümde, token yenileme mekanizmaları, rol tabanlı erişim kontrolü (RBAC) ve diğer önemli güvenlik ipuçlarını inceleyeceğiz.
Token Yenileme (Refresh Token) Mekanizmaları
Erişim token’larının (access token) kısa ömürlü olması (örneğin, 30 dakika) iyi bir güvenlik uygulamasıdır, çünkü çalınması durumunda yetkisiz erişim süresini sınırlar. Ancak bu, kullanıcıların sık sık yeniden giriş yapması gerektiği anlamına gelir ki bu da kullanıcı deneyimini olumsuz etkiler. Bu sorunu çözmek için “refresh token” (yenileme token’ı) mekanizması kullanılır.
- Erişim Token’ı (Access Token): Kısa ömürlüdür (örn. 30 dk). Korumalı API’lara erişim için kullanılır. Genellikle HTTP başlığında (
Authorization: Bearer) taşınır. - Yenileme Token’ı (Refresh Token): Uzun ömürlüdür (örn. 7 gün, 30 gün). Erişim token’ı süresi dolduğunda yeni bir erişim token’ı almak için kullanılır. Daha güvenli bir şekilde saklanmalıdır (örn. HTTP-only cookie).
Bir kullanıcı giriş yaptığında, hem bir erişim token’ı hem de bir yenileme token’ı alır. Erişim token’ı süresi dolduğunda, istemci yenileme token’ını kullanarak özel bir /refresh endpoint’ine istek gönderir. Sunucu, yenileme token’ını doğrular, eğer geçerliyse yeni bir erişim token’ı (ve isteğe bağlı olarak yeni bir yenileme token’ı) oluşturur ve istemciye gönderir. Bu, kullanıcıların sürekli giriş yapmak zorunda kalmadan güvenli bir şekilde oturumlarını sürdürmelerini sağlar.
# config.py
# ...
REFRESH_TOKEN_EXPIRE_DAYS = 7 # Yenileme token'ının geçerlilik süresi (gün)
# main.py
# ...
def create_refresh_token(data: dict, expires_delta: Optional[timedelta] = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(days=REFRESH_TOKEN_EXPIRE_DAYS)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
# /token endpoint'ini güncelleyelim
@app.post("/token", response_model=Token)
async def login_for_access_token(form_data: OAuth2PasswordRequestForm = Depends()):
# ... (Kullanıcı doğrulama kısmı aynı) ...
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
access_token = create_access_token(
data={"sub": user.username}, expires_delta=access_token_expires
)
refresh_token_expires = timedelta(days=REFRESH_TOKEN_EXPIRE_DAYS)
refresh_token = create_refresh_token(
data={"sub": user.username}, expires_delta=refresh_token_expires
)
# Gerçek uygulamada refresh token'ı veritabanında saklayıp,
# bir kez kullanıldıktan sonra geçersiz kılmak önemlidir.
return {"access_token": access_token, "token_type": "bearer", "refresh_token": refresh_token}
# Yeni refresh endpoint'i
@app.post("/refresh", response_model=Token)
async def refresh_access_token(refresh_token: str = Depends(oauth2_scheme)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Geçersiz yenileme token'ı",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(refresh_token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
if username is None:
raise credentials_exception
# Gerçek uygulamada refresh token'ın veritabanında varlığını ve
# daha önce kullanılıp kullanılmadığını kontrol etmelisiniz.
except JWTError:
raise credentials_exception
user = get_user(username)
if user is None:
raise credentials_exception
access_token_expires = timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
new_access_token = create_access_token(
data={"sub": user.username}, expires_delta=access_token_expires
)
return {"access_token": new_access_token, "token_type": "bearer"}
/refresh endpoint’inde, yenileme token’ının veritabanında kayıtlı olup olmadığını ve tek kullanımlık olup olmadığını kontrol etmek, güvenlik açısından kritik bir adımdır. Bu, çalınan bir yenileme token’ının tekrar tekrar kullanılarak yeni erişim token’ları üretmesini engeller.
Rol Tabanlı Erişim Kontrolü (RBAC)
Çoğu YZ uygulamasında, farklı kullanıcıların veya sistemlerin farklı yetki seviyelerine sahip olması gerekir. Örneğin, bir yönetici kullanıcısı YZ modelini güncelleyebilirken, sıradan bir kullanıcı sadece tahmin servisini kullanabilir. JWT payload’ına kullanıcı rollerini ekleyerek ve bu rolleri korumalı rotalarda kontrol ederek RBAC’yi uygulayabiliriz.
# main.py
# ...
class UserInDB(User):
roles: list[str] = ["user"] # Rolleri ekledik
# fake_users_db'yi güncelleyelim:
fake_users_db = {
"testuser": {
"username": "testuser",
"email": "test@example.com",
"full_name": "Test User",
"disabled": False,
"hashed_password": get_password_hash("supersecret"),
"roles": ["user"]
},
"adminuser": {
"username": "adminuser",
"email": "admin@example.com",
"full_name": "Admin User",
"disabled": False,
"hashed_password": get_password_hash("adminsecret"),
"roles": ["admin", "user"]
}
}
# create_access_token fonksiyonunda role bilgisini payload'a ekleyelim
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": expire})
# 'roles' bilgisini payload'a ekliyoruz
if 'roles' in data:
to_encode.update({"roles": data["roles"]})
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
return encoded_jwt
# get_current_user fonksiyonunda payload'dan rolleri alalım
async def get_current_user(token: str = Depends(oauth2_scheme)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Kimlik bilgileri doğrulanamadı",
headers={"WWW-Authenticate": "Bearer"},
)
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
username: str = payload.get("sub")
roles: list[str] = payload.get("roles", ["user"]) # Roller yoksa varsayılan olarak "user"
if username is None:
raise credentials_exception
token_data = TokenData(username=username, roles=roles) # TokenData modelini de güncellememiz gerekebilir
except JWTError:
raise credentials_exception
user = get_user(token_data.username)
if user is None:
raise credentials_exception
user.roles = token_data.roles # Kullanıcı nesnesine rolleri ekle
return user
# Rol kontrolü için yardımcı fonksiyon
def has_role(required_roles: list[str]):
def role_checker(current_user: User = Depends(get_current_active_user)):
for role in required_roles:
if role not in current_user.roles:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Bu işleme erişim yetkiniz yok"
)
return current_user
return role_checker
# Admin rolü gerektiren bir rota örneği
@app.post("/admin/model_update/")
async def update_ai_model(current_user: User = Depends(has_role(["admin"]))):
"""
Yapay zeka modelini güncelleyen bir admin rotası.
Sadece 'admin' rolüne sahip kullanıcılar erişebilir.
"""
return {"message": f"AI modeli {current_user.username} tarafından güncellendi."}
has_role fonksiyonu, belirli bir rolün gerekli olduğu rotalar için bir bağımlılık oluşturur. Bu sayede, Depends(has_role(["admin"])) gibi basit bir ifadeyle bir rotayı belirli rollere sahip kullanıcılarla sınırlayabiliriz. TokenData modeline de roles: list[str] alanı eklemeyi unutmayın.
Diğer Güvenlik İpuçları
- HTTPS Kullanımı: Tüm API iletişimleri (token alışverişi dahil) kesinlikle HTTPS üzerinden yapılmalıdır. Bu, token’ların ağ trafiğinde düz metin olarak ele geçirilmesini engeller.
- Gizli Anahtar (Secret Key) Yönetimi:
SECRET_KEY, asla koda gömülmemeli ve ortam değişkenleri, anahtar yönetim sistemleri (AWS KMS, Azure Key Vault) veya Kubernetes Secret’ları gibi güvenli mekanizmalarla yönetilmelidir. - Token İptali (Revocation): JWT’ler varsayılan olarak durum bilgisi olmayan (stateless) olduğu için, sunucu tarafında bir token’ı doğrudan iptal etmek zordur. Ancak, kullanıcı çıkış yaptığında veya bir güvenlik ihlali durumunda token’ları geçersiz kılmak gerekebilir. Bu, “blacklist” (kara liste) veya “whitelist” (beyaz liste) mekanizmaları kullanılarak yapılabilir. İptal edilen token’lar bir veritabanında saklanır ve her istekte bu liste kontrol edilir.
- Rate Limiting (Oran Sınırlama): Giriş denemeleri veya token yenileme istekleri gibi endpoint’lere oran sınırlaması uygulamak, brute-force saldırılarını ve hizmet reddi (DoS) saldırılarını önlemeye yardımcı olur.
- Günlükleme (Logging): Başarısız giriş denemeleri, yetkisiz erişim girişimleri ve token yenileme olayları gibi güvenlik açısından önemli olayları detaylı bir şekilde kaydetmek, güvenlik ihlallerini tespit etme ve analiz etme yeteneğinizi artırır.
Bu ileri düzey konular ve güvenlik ipuçları, yapay zeka mühendislerinin sadece çalışan değil, aynı zamanda güvenli ve sağlam API’lar geliştirmesine olanak tanır. Özellikle hassas verilerle çalışan YZ modelleri için bu tür önlemler vazgeçilmezdir. Her bir adımı dikkatle uygulamak, uygulamanızın genel güvenlik duruşunu önemli ölçüde güçlendirecektir.
Sonuç ve Sıkça Sorulan Sorular
Bu makalede, yapay zeka mühendisleri için FastAPI kullanarak JWT (JSON Web Token) tabanlı kimlik doğrulama sistemlerinin nasıl kurulacağını ve yönetileceğini ayrıntılı bir şekilde inceledik. API güvenliğinin neden bu kadar kritik olduğunu, JWT’nin temel yapısını ve çalışma prensiplerini anladık. Ardından, FastAPI ortamının kurulumundan başlayarak, kullanıcı modellerinin oluşturulması, parolaların güvenli bir şekilde hash’lenmesi, JWT oluşturma ve doğrulama süreçleri ile korumalı rotaların nasıl tanımlanacağını adım adım uyguladık. Gerçek dünya senaryoları üzerinden, özellikle finansal dolandırıcılık tespiti gibi hassas YZ servislerinde JWT’nin sağladığı avantajları gördük. Son olarak, token yenileme mekanizmaları, rol tabanlı erişim kontrolü (RBAC) ve diğer önemli güvenlik ipuçları ile ileri düzey konulara değindik.
FastAPI’nin modern mimarisi ve bağımlılık enjeksiyonu sistemi, JWT entegrasyonunu oldukça kolay ve modüler hale getirerek geliştiricilerin iş yükünü önemli ölçüde azaltır. YZ mühendisleri, bu güçlü kombinasyonu kullanarak, modellerini güvenli, ölçeklenebilir ve yüksek performanslı API’lar aracılığıyla sunabilirler. Unutulmamalıdır ki, güvenlik sürekli bir süreçtir ve en iyi uygulamaları takip etmek, API’larınızın ve dolayısıyla YZ modellerinizin uzun vadeli başarısı için vazgeçilmezdir.
Sıkça Sorulan Sorular (SSS)
-
JWT mi yoksa Session tabanlı kimlik doğrulama mı tercih edilmeli?
Seçim, uygulamanızın mimarisine ve gereksinimlerine bağlıdır. JWT, durum bilgisi olmayan (stateless) yapısı sayesinde mikroservis mimarileri ve mobil uygulamalar için daha uygundur, çünkü sunucu tarafında oturum bilgisi tutmaya gerek kalmaz, bu da ölçeklenebilirliği artırır. Session tabanlı kimlik doğrulama ise genellikle geleneksel monolitik web uygulamalarında kullanılır ve sunucu tarafında oturum bilgilerini saklar. YZ API’ları genellikle durum bilgisi olmadığı ve yatay ölçeklenebilirlik gerektirdiği için JWT daha avantajlıdır.
-
JWT token’ları nasıl güvenli saklanmalı?
Erişim token’ları genellikle istemci tarafında (tarayıcıda localStorage veya sessionStorage, mobil uygulamalarda güvenli depolama alanları) saklanır. Ancak XSS (Cross-Site Scripting) saldırılarına karşı daha güvenli olması için refresh token’lar HTTP-only cookie’ler içinde saklanmalıdır. Bu cookie’lere JavaScript ile erişilemez, bu da XSS saldırganlarının token’ı ele geçirmesini zorlaştırır. Her iki durumda da token’ların çalınma riskini azaltmak için kısa ömürlü olmaları önemlidir.
-
Token süresi dolarsa ne olur?
Erişim token’ının süresi dolduğunda, istemci bu token ile korumalı bir rotaya istek gönderdiğinde sunucu 401 Yetkisiz (Unauthorized) hatası döndürecektir. Bu durumda istemci, eğer varsa, yenileme token’ını kullanarak yeni bir erişim token’ı talep etmelidir. Eğer yenileme token’ı da yoksa veya süresi dolmuşsa, kullanıcının yeniden giriş yapması gerekir.
-
JWT’de rol tabanlı yetkilendirme (RBAC) nasıl yapılır?
Rol tabanlı yetkilendirme, kullanıcının rollerini JWT’nin payload kısmına ekleyerek yapılır (örneğin,
"roles": ["admin", "user"]). Sunucu tarafında, korumalı rotalara erişimden önce, gelen token’ın payload’ındaki rolleri kontrol eden bir bağımlılık (FastAPI’deDependsile) tanımlanır. Eğer kullanıcının gerekli rollere sahip olmadığı tespit edilirse, 403 Yasak (Forbidden) hatası döndürülür. -
Refresh token’lar neden gereklidir?
Refresh token’lar, erişim token’larının kısa ömürlü olmasının getirdiği kullanıcı deneyimi sorununu çözmek için kullanılır. Kısa ömürlü erişim token’ları güvenlik açısından daha iyidir çünkü çalınmaları durumunda yetkisiz erişim süresini kısıtlarlar. Refresh token’lar ise daha uzun ömürlü olup, kullanıcıların sık sık yeniden giriş yapmak zorunda kalmadan yeni erişim token’ları almasını sağlar. Bu, hem güvenliği hem de kullanıcı deneyimini optimize eden bir yaklaşımdır.
#FastAPI #JWT #KimlikDoğrulama #YapayZeka #APIgüvenliği
