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.
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_tokenkullan, imzasını veaudalanı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_tokentekrar 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.
-
algalanına güvenmek.nonekabulü 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.
issbeklenen sağlayıcı mı,audbu istemci mi,expgeçmiş mi,noncegönderilenle aynı mı,azpbeklenen istemci mi.audkontrolünün atlanması, yukarıdaki confused deputy senaryosunun anahtarıdır. -
Kullanıcı kimliğini yanlış alandan almak. Kimlik için
kararlı olan
iss+subikilisidir. 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
localStorageiçinde tutulan bir token, sayfadaki herhangi bir betik tarafından okunabilir; tek bir XSS bulgusu doğrudan hesap devralmaya dönüşür.HttpOnly,Secureve uygunSameSiteayarlı ç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
Refererbaş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 yok | Kod sızdırma, hesap devralma |
| Kayıtlı callback üzerinde açık yönlendirme yok | Kodun zincirle dışarı taşınması |
| state üretiliyor, oturuma bağlı ve dönüşte karşılaştırılıyor | CSRF, kod enjeksiyonu, hesap bağlama |
| PKCE zorunlu, yalnızca S256 | Yakalanan 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ıyor | Sahte kimlik, confused deputy |
| Algoritma yapılandırmadan geliyor, token'dan değil | alg karışıklığı |
| Kimlik iss + sub üzerinden eşleniyor | Hesap birleşmesi |
| Kapsam kaynak sunucuda yeniden doğrulanıyor | Yetki aşımı |
| Refresh token rotasyonu ve yeniden kullanım tespiti | Kalıcı erişim |
| Token tarayıcıda betiğe açık şekilde saklanmıyor | XSS 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
- RFC 6749 — The OAuth 2.0 Authorization Framework.
- RFC 6819 — OAuth 2.0 Threat Model and Security Considerations.
- RFC 7636 — Proof Key for Code Exchange (PKCE).
- RFC 8252 — OAuth 2.0 for Native Apps.
- RFC 9207 — OAuth 2.0 Authorization Server Issuer Identification.
- RFC 9700 — OAuth 2.0 Security Best Current Practice.
- OpenID Connect Core 1.0 — OpenID Foundation.
- OWASP API Security Top 10: owasp.org/API-Security