API Güvenliği

OAuth 2.0 & OIDC: Yapılandırma Hataları ve Saldırı Vektörleri

OAuth 2.0 ve OpenID Connect'te karşılaşılan zafiyetlerin büyük çoğunluğu protokolün kendisinden değil, uygulayan tarafın verdiği kararlardan doğuyor. Bu yazı, tekrar tekrar aynı yerlerde karşımıza çıkan hata sınıflarını, neden ortaya çıktıklarını ve nasıl kapatıldıklarını topluyor.

10 dakika Hata sınıfları

Kavramsal hata: yetkilendirme ile kimlik doğrulamayı karıştırmak

OAuth 2.0 bir yetkilendirme (authorization) çerçevesidir: bir istemciye, kullanıcının kaynaklarına sınırlı erişim veren bir token üretir. Kimin oturum açtığını söylemek işi değildir. OpenID Connect, bunun üzerine id_token ile bir kimlik katmanı ekler.

En pahalı hatalar bu ayrımın kaybolmasıyla başlıyor. Klasik örnek: uygulama, istemciden gelen bir access_token'ı alıp sağlayıcının profil uç noktasına soruyor, dönen kullanıcı kimliğiyle oturum açtırıyor. Token'ın kime ve hangi istemci için verildiği doğrulanmıyor. Bu durumda saldırgan, kendi kontrolündeki bir uygulama için alınmış geçerli bir token'ı kurbanın hesabına giriş yapmak için kullanabiliyor — "confused deputy"nin ders kitabı hâli.

Kural: kimlik için id_token kullan, imzasını ve aud alanını doğrula. Bir access token'ı kimlik kanıtı olarak kabul ediyorsan, en azından token introspection ile hangi istemci için verildiğini kontrol etmelisin.

Akış seçimi ve miras kalan riskler

Bugünün doğru varsayılanı sade: Authorization Code + PKCE. Tüm istemci tipleri için — gizli tutabilen sunucu tarafı istemciler dahil. Miras akışların bıraktığı sorunlar ise şöyle:

  • Implicit flow: token doğrudan yönlendirme adresinin fragment kısmında döner. Tarayıcı geçmişi, referrer başlıkları, ara katman logları ve sayfadaki üçüncü taraf betikler token'a temas edebilir. OAuth 2.1 yönünde bu akış kaldırılıyor.
  • Resource Owner Password Credentials: istemcinin kullanıcının parolasını görmesini gerektirir; çok faktörlü doğrulama ve federasyonla uyumsuzdur. Terk edilmiş kabul edilmeli.
  • Device authorization flow: yerinde bir çözüm, ama kullanıcı kodu onaylatmaya yönelik oltalama senaryolarına açık. Cihaz eşleşmesi ve kullanıcıya net bağlam gösterimi şart.

redirect_uri: en verimli hata sınıfı

Yetkilendirme sunucusu, kodu yalnızca kayıtlı bir adrese göndermelidir. Buradaki eşleştirme mantığı gevşediği anda kod, saldırganın kontrolündeki bir adrese taşınabilir hale gelir. Tekrar eden gevşeklik biçimleri:

  • Ön ek eşleşmesi: kayıtlı değer bir ön ek gibi ele alınır, sonuna eklenen her şey kabul edilir.
  • Joker alt alan adı: tüm alt alan adlarına izin verilir; tek bir zayıf alt alan adı yeterli olur.
  • Yol normalizasyonu farkı: eşleştirme ile gerçek yönlendirme farklı normalizasyon uygular.
  • Sorgu/fragment esnekliği: kayıtlı adrese ek parametre veya fragment eklenebilmesi.
  • Zincirlenen açık yönlendirme: kayıtlı ve "meşru" bir adres, kendi içinde açık yönlendirme barındırıyorsa, kod meşru adrese gider ve oradan dışarıya taşınır. Bu yüzden açık yönlendirme bulguları OAuth kullanan uygulamalarda düşük şiddetli sayılmamalıdır.
Kayıtlı: https://app.example.tld/oauth/callback

Reddedilmeli:
  https://app.example.tld/oauth/callback/../../elsewhere
  https://app.example.tld/oauth/callback?next=https://baska.tld
  https://app.example.tld.attacker.tld/oauth/callback
  https://anything.app.example.tld/oauth/callback

Kabul edilmeli:
  https://app.example.tld/oauth/callback   (birebir aynı)

Savunma tarafı kısa: tam dize eşleşmesi, önceden kayıtlı adres listesi, dinamik parça yok. İstemci başına ayrı liste, ortam (üretim / test) ayrımı ve kullanılmayan kayıtların temizliği de aynı başlığın parçası.

state ve PKCE: iki farklı işi yapan iki farklı parametre

Sık görülen bir kısayol: "PKCE var, state'e gerek yok". İkisi farklı saldırıları engelliyor.

  • state — istemci tarafında CSRF ve oturum bağlama koruması. Eksik olduğunda saldırgan, kendi hesabına ait bir yetkilendirme kodunu kurbanın tarayıcısında tamamlatabilir. Sonuç: kurbanın uygulamadaki oturumu saldırganın kimliğine bağlanır; kurbanın girdiği veriler saldırganın hesabına akar. Sosyal hesap bağlama akışlarında bunun tersi de olur ve doğrudan hesap devralmaya çıkar.
  • PKCE (code_verifier / code_challenge) — yakalanan bir yetkilendirme kodunun başkası tarafından kullanılmasını engeller. Public istemcilerde zorunlu, diğerlerinde de önerilir.
  • nonce (OIDC) — id_token tekrar kullanımına karşı. state'in yerini tutmaz.

Uygulama detayları da denetlenmeli: state tahmin edilemez ve oturuma bağlı olmalı, tek kullanımlık olmalı ve dönüşte gerçekten karşılaştırılmalı — sadece varlığına bakmak yaygın bir hata. PKCE tarafında code_challenge_method olarak plain kabul etmek, mekanizmayı büyük ölçüde anlamsızlaştırır; yalnızca S256 kabul edilmeli. Yetkilendirme kodu tek kullanımlık, kısa ömürlü olmalı ve ikinci kullanım denemesinde ilgili token'lar iptal edilmelidir.

Token doğrulama hataları

JWT tabanlı kimlik doğrulamada hata sınıfları oldukça standartlaştı; yine de düzenli olarak karşımıza çıkıyorlar.

  • İmzayı doğrulamamak. Token'ı yalnızca çözüp içindeki alanlara güvenmek. Kütüphanelerin "decode" ve "verify" fonksiyonlarının karıştırılması bunun en yaygın hâli.
  • alg alanına güvenmek. none kabulü ya da asimetrik/simetrik karışımı — sunucunun beklediği algoritmayı yapılandırmadan alması, token'dan değil.
  • Anahtar kaynağını token'ın söylemesine izin vermek. Başlıktaki anahtar konumu alanlarına güvenmek yerine, sağlayıcının bilinen JWKS adresinden alınan ve önbelleklenen anahtarlar kullanılmalı.
  • Talepleri kontrol etmemek. iss beklenen sağlayıcı mı, aud bu istemci mi, exp geçmiş mi, nonce gönderilenle aynı mı, azp beklenen istemci mi. aud kontrolünün atlanması, yukarıdaki confused deputy senaryosunun anahtarıdır.
  • Kullanıcı kimliğini yanlış alandan almak. Kimlik için kararlı olan iss + sub ikilisidir. E-posta alanına göre eşleştirme yapmak, doğrulanmamış e-posta veya sağlayıcı değişikliği durumunda hesap birleşmesine yol açar.

Mix-up ve çok sağlayıcılı ortamlar

Birden fazla kimlik sağlayıcısını destekleyen bir istemci, dönen yanıtın hangi sağlayıcıdan geldiğini kesin olarak bilmek zorundadır. Aksi hâlde saldırgan, akışı kendi sağlayıcısı üzerinden başlatıp kodu meşru sağlayıcının token uç noktasına gönderilecek şekilde yönlendirebilir; ya da tersine, istemcinin gizli anahtarını kendi kontrolündeki bir uç noktaya taşıyabilir.

Korunma yolu, yetkilendirme yanıtında sağlayıcı kimliğinin taşınması (iss parametresi) ve istemcinin bunu beklediği sağlayıcıyla karşılaştırmasıdır. Sağlayıcı başına ayrı redirect_uri kullanmak da pratik ve etkili bir ayrıştırma sağlar.

Kapsam, rıza ve refresh token

  • Kapsam yalnızca istekte kalmasın. Kaynak sunucu, gelen token'ın kapsamını her istekte yeniden doğrulamalı. "İstemci zaten bu kapsamı istemişti" bir yetkilendirme kararı değildir.
  • Rıza ekranı atlanmasın. Birinci taraf uygulamalar için otomatik onay verilmesi yaygın; ancak bu, kapsam yükseltme isteklerinde de sessizce devreye giriyorsa kullanıcı hiç görmediği bir yetkiyi vermiş olur.
  • Refresh token rotasyonu. Public istemcilerde rotasyon ve yeniden kullanım tespiti şart: aynı refresh token ikinci kez sunulduğunda tüm aile iptal edilmeli. Aksi hâlde çalınan bir refresh token süresiz erişim demektir.
  • Çıkışın anlamı. Uygulamadan çıkmak token'ı iptal etmiyorsa, "çıkış yaptım" hissi ile gerçek erişim durumu ayrışır. Token iptali ve oturum sonlandırma ayrı ayrı ele alınmalı.

Token saklama ve sızıntı yolları

Protokolü doğru kurup token'ı yanlış yerde tutmak, kazanılan güvenliği geri veriyor.

  • Tarayıcıda localStorage içinde tutulan bir token, sayfadaki herhangi bir betik tarafından okunabilir; tek bir XSS bulgusu doğrudan hesap devralmaya dönüşür. HttpOnly, Secure ve uygun SameSite ayarlı çerezler ya da token'ı hiç tarayıcıya indirmeyen bir arka uç (BFF) deseni tercih edilmeli.
  • Yetkilendirme kodu ve token'lar erişim loglarına, hata izlerine, analitik olaylarına ve Referer başlıklarına düşebilir. Sorgu parametresinde token taşımaktan kaçınmak temel önlem.
  • Mobil ve masaüstü istemcilerde özel şema geri çağrıları başka uygulamalar tarafından da kaydedilebilir. Doğrulanmış uygulama bağlantıları ve PKCE birlikte kullanılmalı.

Triyaj kontrol listesi

Kontrol Neyi engeller
redirect_uri tam dize eşleşmesi, joker yokKod sızdırma, hesap devralma
Kayıtlı callback üzerinde açık yönlendirme yokKodun zincirle dışarı taşınması
state üretiliyor, oturuma bağlı ve dönüşte karşılaştırılıyorCSRF, kod enjeksiyonu, hesap bağlama
PKCE zorunlu, yalnızca S256Yakalanan kodun kullanılması
Yetkilendirme kodu tek kullanımlık, kısa ömürlüTekrar kullanım
id_token imzası, iss, aud, exp, nonce doğrulanıyorSahte kimlik, confused deputy
Algoritma yapılandırmadan geliyor, token'dan değilalg karışıklığı
Kimlik iss + sub üzerinden eşleniyorHesap birleşmesi
Kapsam kaynak sunucuda yeniden doğrulanıyorYetki aşımı
Refresh token rotasyonu ve yeniden kullanım tespitiKalıcı erişim
Token tarayıcıda betiğe açık şekilde saklanmıyorXSS ile devralma

Özet

OAuth 2.0 ve OIDC'de zafiyet aramak, genellikle kriptografi kırmak değil varsayımları test etmek demek: sunucu redirect_uri'yi gerçekten tam mı eşleştiriyor, state gerçekten karşılaştırılıyor mu, aud gerçekten kontrol ediliyor mu, kapsam kaynakta yeniden doğrulanıyor mu. Bu soruların her biri tek bir kod satırıyla cevaplanıyor ve her biri atlandığında sonuç aynı yere çıkıyor: hesap devralma.

İyi haber, savunma listesinin kısa ve büyük ölçüde standartlaşmış olması. IETF'in güvenlik en iyi uygulamaları belgesi ve OAuth 2.1 yönü, doğru varsayılanları zaten tarif ediyor; iş, onları uygulamada gerçekten doğrulamakta.

Kaynaklar

  1. RFC 6749 — The OAuth 2.0 Authorization Framework.
  2. RFC 6819 — OAuth 2.0 Threat Model and Security Considerations.
  3. RFC 7636 — Proof Key for Code Exchange (PKCE).
  4. RFC 8252 — OAuth 2.0 for Native Apps.
  5. RFC 9207 — OAuth 2.0 Authorization Server Issuer Identification.
  6. RFC 9700 — OAuth 2.0 Security Best Current Practice.
  7. OpenID Connect Core 1.0 — OpenID Foundation.
  8. OWASP API Security Top 10: owasp.org/API-Security