<

OAuth 2.1 ve Modern Kimlik Doğrulama Akışları: Hangi Akış, Ne Zaman?

Platformda eski bir servisin güvenlik gözden geçirmesini yapıyorduk; OAuth istemci kaydında redirect URI alanına gelince ekipten bir arkadaş "bir dakika" dedi. Alan şöyleydi: https://*.alan-adimiz.com/*. Joker karakterli bir redirect URI — yani yetkilendirme sunucusunun, token'ı o alan adının herhangi bir alt alanına göndermeye razı olması. Subdomain takeover'la birleştiğinde bu, kullanıcı token'larını gümüş tepside sunan bir kapıdır. Uygulama üç yıldır üretimdeydi ve "Google ile giriş" butonu her gün binlerce kez tıklanıyordu.

"Google ile giriş yap" butonu masum görünür; arkasındaki protokol ailesi ise yanlış kurgulandığında en şık uygulamayı güvenlik enkazına çevirir. İyi haber şu: OAuth 2.1 ile birlikte kurallar hiç olmadığı kadar net. Bu yazıda hem 2.1'in neyi değiştirdiğini hem de hangi senaryoda hangi akışın doğru olduğunu, sahadan örneklerle anlatacağım.

OAuth 2.1: yenilik değil, budama

OAuth 2.1'i bir özellik sürümü gibi düşünmeyin; yeni bir yetenek getirmedi. Yaptığı şey bahçıvanlık — on yılın kötü pratiklerini resmen kesip attı. Implicit flow öldü: access token'ın URL fragment'ında taşındığı o eski akış artık spesifikasyonun dışında; token URL'de yolculuk etmez, nokta. Password grant öldü: kullanıcının şifresini üçüncü parti uygulamaya yazdırmak (hatırlıyor musunuz, bazı uygulamalar "Gmail şifrenizi girin" derdi) resmen tarih oldu. Ve PKCE herkes için zorunlu hâle geldi — eskiden "mobil uygulamalar için önerilir" diye geçen mekanizma, artık sunuculu web uygulaması dahil tüm istemciler için standart.

PKCE'nin ne işe yaradığını iki cümleyle anlatayım, çünkü "zorunlu oldu" deyip geçmek haksızlık olur. Yetkilendirme sunucusundan dönen authorization code, token'a çevrilmeden önce bir yerlerde (mobil cihazda, tarayıcıda) yolculuk eder ve bu yolculuk sırasında çalınabilir. PKCE, akışın başında üretilen tek kullanımlık bir sırla bu kodu istemciye bağlar: kodu çalan taraf, sırra sahip olmadığı için token alamaz. Maliyeti birkaç satır kod; kapattığı delik, gerçek dünyada defalarca istismar edilmiş bir saldırı sınıfı. Zorunlu olması bu yüzden isabet.

Elinizde hâlâ implicit flow ya da password grant ile çalışan bir sistem varsa, bunu "bir gün bakarız" listesinden çıkarıp bu çeyreğin planına alın. Kimlik sağlayıcılar bu akışları peyderpey kapatıyor; göçü kriz anında değil, sakin bir dönemde yapmak isteyeceksiniz.

Hangi senaryoya hangi akış

Yıllardır projelerde kullandığımız eşleme, aslında dört satıra sığıyor:

  • Sunuculu web uygulaması: Authorization Code + PKCE. Token'lar sunucuda kalır; tarayıcıya yalnızca HttpOnly + Secure + SameSite bayraklı bir oturum çerezi iner.
  • SPA (sunucusuz frontend): Yine Authorization Code + PKCE — ama token'ı localStorage'a koymak, XSS'e açık büfe açmaktır. Tercihimiz BFF (backend-for-frontend) deseni: token tarayıcıya hiç inmez.
  • Mobil uygulama: Authorization Code + PKCE, sistem tarayıcısı üzerinden — gömülü WebView değil. Token'lar Keychain/Keystore'da saklanır.
  • Sunucudan sunucuya: Client Credentials. Ortada kullanıcı yoksa kullanıcı akışı da yok; bu kadar basit.

Tabloya sığmayan bir senaryo daha var: klavyesi ve tarayıcısı olmayan cihazlar — televizyon uygulamaları, IoT panoları, komut satırı araçları. Bunlar için Device Authorization Flow tasarlanmış durumda: cihaz size kısa bir kod gösterir, siz telefonunuzdan bir adrese girip kodu onaylarsınız. Yıllar önce akıllı TV uygulaması geliştirdiğimiz bir projede tam bu deseni kullanmıştık; kullanıcıya kumandayla şifre yazdırmaya çalışmaktan hem daha güvenli hem kıyaslanamayacak kadar konforlu.

Mobil maddesindeki WebView uyarısının altını çizeyim, çünkü sahada hâlâ çok karşılaşıyoruz. Giriş ekranını uygulamanın kendi WebView'ında açmak iki açıdan yanlış: birincisi, uygulama o WebView'ın içini görebilir — kullanıcının kimlik sağlayıcıya yazdığı şifre dahil. İkincisi, kimlik sağlayıcılar bunu tespit edip engelliyor. Sistem tarayıcısı (iOS'ta ASWebAuthenticationSession, Android'de Custom Tabs) hem güvenli hem de kullanıcının mevcut oturumunu devralabildiği için daha akıcı.

Sahada en çok kanayan üç yara

Birincisini girişte anlattım: redirect URI gevşekliği. Joker karakterli kayıt, token hırsızlığının ana kapısıdır; kural birebir eşleşmedir. "Ama staging, test ve prod ortamlarımız var" itirazının cevabı da basit: üç ortam, üç ayrı tam URI kaydı. Beş dakikalık iş. Girişteki vakada da öyle oldu — joker kaydı üç açık URI ile değiştirmek yarım saat sürdü; üç yıllık riskin kapanma maliyeti buydu.

İkinci yara, ölümsüz token. Süresi hiç dolmayan ya da 30 gün geçerli access token, çalındığı gün saldırgana 30 günlük abonelik hediye etmektir. Doğru kurgu: kısa ömürlü access token (dakikalar mertebesinde) artı rotasyonlu refresh token. Rotasyon şu demek — her yenilemede eski refresh token geçersizleşir; çalınan bir refresh token ikinci kez kullanıldığında sunucu bunu fark edip tüm oturumu düşürebilir. Çalıntı token'ın raf ömrü dakikalarla ölçülmeli, haftalarla değil.

Üçüncüsü, benim "scope obezitesi" dediğim şey. Uygulama, "belki ileride lazım olur" diye kullanıcının takvimine, kişilerine, dosyalarına birden yetki istiyor. Bu hem onay ekranında kullanıcıyı ürkütür (dönüşüm oranınıza yansır, ölçtük) hem de olası bir ihlal günü faturayı katlar. En az yetki ilkesi OAuth'ta da geçerlidir: bugün ne lazımsa onu isteyin, yarın lazım olanı yarın istersiniz — incremental authorization tam bunun için var.

Kimlik katmanında cimrilik en pahalı tasarruftur

Bu üçlünün dışında, daha az bilinen ama sorulduğunda şaşırtan bir konu daha var: çıkış. Ekiplerin çoğu giriş akışını titizlikle kurar, çıkışı unutur. Kullanıcı "çıkış yap" dediğinde ya da hesabı ele geçirildiğinde, dolaşımdaki access ve refresh token'lara ne oluyor? Token iptal mekanizması kurgulanmamışsa cevap "hiçbir şey" olabilir — kullanıcı çıktığını sanır, token bir süre daha yaşar. Refresh token'ları sunucu tarafında kayıtlı ve iptal edilebilir tutmak, bu senaryonun tek sağlam çözümü. Oturumun ölümü de en az doğumu kadar tasarım ister.

Yılların özeti şu cümle: kimlik katmanı, sonradan düzeltilmesi en pahalı katmandır. Veritabanı şemasını göç ettirirsiniz, API'yi sürümlersiniz; ama üretimdeki yüz bin kullanıcının oturum mekanizmasını değiştirmek, uçağın motorunu havada değiştirmeye benzer. Yeni projeye başlarken doğru akışı seçmeye harcanan bir günün, yanlış akışı yamamaya harcanan bir aydan ucuz olduğunu defalarca yaşadım — hem sıfırdan kurduğum sistemlerde hem sonradan içine girdiklerimde. İyi tarafı şu: OAuth 2.1 sayesinde "doğru" artık yoruma açık değil, yazılı.

Mevcut sisteminizin OAuth kurgusundan emin değilseniz, redirect URI'larınızdan token ömürlerine kadar bakan kısa bir gözden geçirme çoğu zaman yarım gün sürüyor — ve enkaz kaldırmaktan her zaman ucuz. Kimlik katmanı sohbetlerine hep açığım; iletişim sayfası orada duruyor.

📅 Yayınlanma:  ·  Yakup Zengin