Web uygulamalarında Rate Limit

Açıkçası yazıya başlarken Türkçe’sinin ne olduğunu uzun süre düşündüm. Ama bulamadım. Bu sefer kendim de bir çeviri yapamadım. Biraz daha web tabanlı düşünecek olursak "yapılan istekleri/işlemleri limitleme" diye çevirebiliriz.

Problemler ve senaryolar

Rate-limit aslında çok geniş bir konsept ve birçok noktada kullanılabiliyor. Günümüzde web uygulamalarında en çok brute-force sorunlarını çözmek için kullanılıyor. Ben bu yazıda gerçek örnekler vereceğim ve sonrasında neler yapabileceğimizi konuşacağım.

Kullanıcı tarafından tetiklenen e-postaların sonsuz sayıda gönderilmesi

Web sitelerinde suistimale açık bırakılan noktalardan birisi genelde e-postalar oluyor. Saldırganlar burayı mutlaka bir yoklar. Buralarda bir limit mekanizması unutulabiliyor. Çok basit bir örnek vermek gerekirse, "parolamı unuttum" e-postası için özel bir limit uygulanmadıysa, kullanıcının her talebinde yeni bir e-posta gönderilir. Çok basit bir şekilde e-posta sıfırlama servisine yapılacak bir ddos ile milyonlarca e-posta gönderilebilir. Ve bu gönderilen e-postalar şirkete zarar olarak yazar.

  1. Eğer Mailgun, SES tarzı bir servis kullanılıyorsa fatura çok yüksek gelir.
  2. Eğer firma kendi mail sunucusunu kullanıyorsa mail sunucusu kuyruktan dolayı kilitlenebilir ve önemli e-postaların gönderilmesi gecikir/engellenir. Sunucunun tekrar ayağa kalkması için harcanacak personel eforu da cabası.
  3. Eğer gmail, yandex tarzı bir smtp sunucu üzerinden gönderiyorsanız da yüksek olasılıkla rate-limite takılırsınız ve bir süre banlanırsınız (aslında bizim yapmamız gereken şey buydu!)

Deneme yanılma ile login bilgilerine erişilmesi

Yani eminim herkes bu saldırılarına karşı önlem almıştır. Ama ben yine de yazmak istedim :) Çünkü buralardan saldırı alan projelere çokça rastladım. Saldırganların elinde genelde bir e-posta/parola listesi oluyor ve tüm listeyi sizin üzerinizde deniyorlar. Tek bir kullanıcı için birden fazla parola denemesi de yapılabilir, birden fazla kullanıcı bilgisi de denenebilir.
"X sitesinin 100.000 kullanıcısın parolaları ele geçirildi" haberlerini mutlaka duymuşsunuzdur. İşte bu parolaları çeşitli sitelerde deniyorlar ve müşteri aynı parolayı kullandıysa ve saldırgan login olabildiyse; kayıtlı kartları, sadakat puanlarını ya da hesabı kullanabiliyorlar. Hesap kullanımı çok enteresan bir konu, bir dolandıcılık hikayesinin aktörü olabiliyorsunuz.

  1. Müşterilerinizin hesapları hacklenebilir ve kullanılabilir. Müşteriniz zarara uğrayabilir.
  2. Sitenizi kullanarak brute-force/rainbow table saldırıları ile müşterilerin parolaları ele geçirilebilir ve o haberlere konu olabilirsiniz.

Not: En çok mobile özel login endpointleri (captcha vs olmadığı için) saldırıya açık halde oluyorlar.

Sitenizdeki hassas verilerin temin edilebilmesi

En hassas veri sanıyorum ki kullanıcı bilgileridir. Yine parolamı unuttum üzerinden yola çıkarsak, bir kullanıcının sitenizde kayıtlı olup olmadığı, döndüğünüz response üzerinden anlaşılıyor olabilir. Bir e-posta forma girildiğinde "bu e-posta hesabı sistemimizde kayıtlı değildir" diyorsanız bir problemimiz var demektir.
Tabi öncelikle bu yanıtı düzeltmemiz gerekiyor ve saldırgana sır vermememiz gerekiyor. Ama öncesinde buraya uygulayacağımız bir rate-limit, saldırganı başımızdan uzaklaştırabilir.

  1. Müşterilerinizin var olup olmadığı tespit edilebilir. Bu bilgiler sonraki saldırılarda ya da müşterilere yönelik yapılacak başka saldırılarda kullanılabilir. Telefonla polis gibi arayıp "X sitesinde hesabınız tespit edildi, para verin" diyebilirler :)
  2. Hediye kodu vb. kodlar brute force ile denenebilir ve geçerli olanlar sızdırılabilir.

3.parti entegrasyonların etkilenmesi

En az e-posta sunucusunu meşgul etmemiz kadar önemli bir konu burası. Artık 3.parti sistemlerle çalışmayan websitesi yok. Algolia, Intercom, Segment ve daha bir sürü site ile çalışıyoruz. Kendi içimizde de cache yapıyoruz, invalidation yapıyoruz. Bazı işlemlerin sürekli tekrarlanması, 3. parti sistemleri yorabilir ve bize istenmeyen faturalar çıkartabilir. Örneğin Algolia, yapılan istek sayısına göre fatura kesebiliyor. Belli bir rakamın üstüne çıkınca fiyatlar inanılmaz artıyor.
Kullanıcınızın bir aksiyonu sürekli tekrarladığını ve her tekrarladığında 3. partilere request attığımızı düşünelim. Bu durumda bir ddos ile milyonlarca istek gidebilir.

  1. 3.parti sistemlere çok sayıda ve gereksiz istek gönderebilir, bunun sonucunda faturalara maruz kalabiliriz.
  2. Bazı istatistiklerimiz etkilenebilir ve bizi yanıltabilir.
  3. Cache invalidation veya veritabanı işlemleri sunucularımızı meşgul edebilir.

Çözüm önerileri

Loglayın ve takip edin

Yapılan işlemleri loglamak ve takip etmek en önemli konuların başında geliyor. Elbette neyi loglayacağınızı, raporlayacağınızı ve takip edeceğinizi işin en başında bilemeyebilirsiniz. Alarm mekanizmaları her zaman kurtarıcınız olacaktır.

Basitçe, istek yapılan sayfalarınızı/servislerinizi loglayabilir ve belli bir süre sonra ortalama sayıları çıkartabilirsiniz. Bu noktada bir alarm oluşturup, belli bir oranın üzerinde istek geldiğinde incelemeye alabilirsiniz. Verdiğim örneklerden yola çıkarsak, parola sıfırlama endpointine saatte ortalama X istek geliyor ve bir anda bu sayı 3X olarak raporlara yansıyorsa, burada bir anomali var diyebiliriz ve kontrol edebiliriz. Bir süre sonra bu süreci daha da otomatikleştirebilirsiniz.

İstek sayılarını limitleyin

Benim sıklıkla uyguladığım yöntemlerden başında gelir. Kullanıcılara yapacakları işlemler için belli limitler veririm.
Örneğin, bir IP adresi, 1 dakika içerisinde maximum 3 kez login olmayı deneyebilir. 5 dakika içerisinde 6 kez deneyebilir.
Bu kullanımı biraz daha genişletmek gerekiyor tabi. Sadece bir IP adresi bazında bakmak yerine, bir kullanıcı bazında da bakabilirsiniz. Örneğin bir e-mail adresi ile yapılan denemeleri limitleyebilirsiniz ve belli bir denemeden sonra, o hesap ile giriş yapılmasını geçici olarak devre dışı bırakabilirsiniz. Hesabının kilitlenmesi elbette sizin kullanıcılarınızı mutsuz edebilir. Bu noktada da IP kontrolü veya yapacağınız diğer kontroller devreye girmeli.

Eğer bir API servis ediyorsanız, bu noktada da bir rate-limit uygulamanızı şiddetle öneririm. Belli bir süre içerisinde belli bir sayıda istek atabilirsiniz. Bu noktada sevmeyeni olmayan Stripe dökümanından bir parça bırakıyorum: https://stripe.com/docs/rate-limits

Rate-limit belirleme konusunda sizi en çok zorlayacak şey, hangi noktalarda rate-limit uygulanacağı ve bunun ne olacağı.Bunun bir formülü yok maalesef. Log kısmında da bahsettiğim gibi, bunu ölçmeniz ve kendinize uyarlamanız gerekiyor. Bir de rate limite takılan işlemleri de loglarsanız, insanların ne sıklıkla rate-limite takıldığını görebilirsiniz.

Sitenizdeki tüm sayfalara rate-limit uygulayabilirsiniz. Örneğin Laravel size çok basit bir middleware ile rate-limit uygulayabilme imkanı sunar. https://laravel.com/docs/9.x/routing#rate-limiting

Ama bunu uygularken istekleri loglamanız ve dikkat etmeniz çok önemli. Eğer burada limitleri doğru belirleyemezseniz, kullanıcılarınızın işlem yapmasını engelleyebilirsiniz. Bu yüzden genel bir rate limit açılmasını pek doğru bulmuyorum. Ancak bir saldırı altında olduğunuzu bildiğiniz senaryolarda açmanız faydalı olabilir. Bu da bizim sıklıkla uyguladığımız bir yöntem. Yani bir nevi "Cloudflare Under Attack Mode" gibi.

3. Parti güncellemelerini kuyruğa atın

Bir kullanıcının herhangi bir bilgisi değiştiğinde Algolia üzerinde güncellediğinizi varsayalım. Bunu her güncellemede yaptığınızda muhtemelen çok fazla istek göndereceksiniz. Bunun önüne geçmek için kendinize bir politika belirleyin. Önem derecesine göre bir değişikliğin ne kadar sürede 3. parti uygulamada güncelleneceğini belirleyin. Örneğin kullanıcı adı sizin veritabanınızda güncellendikten 10 dakika sonra Algolia üzerinde güncellenebilir. Bu 10 dakika içerisinde oluşan tüm istekleri bir kuyruk sistemine yazıp, 10. 10 dakika sonra sadece 1 istek gitmesini sağlayabilirsiniz. Sonuç olarak sadece 1 kez güncellenmesi de yeterli olacaktır.

Bu noktada illaki RabbitMq tarzı bir queue sistemi de kullanmanıza gerek yok. Kendiniz için basit bir veritabanı tablosu yapıp bunun üzerinden yönetebilirsiniz. Tabi ki bir ddos altında veritabanınızın da sallanacağını unutmayın. Bu yüzden siz bu işi queue sistemi üzerinde kurgulamaya gayret edin :)

HTTP Status Code

429, rate limit için kabul gören durum kodudur. Bu kod ile dönüş yaptığınızda, bunu kolayca nginx/apache loglarından yakalayabilirsiniz. Bu da kullandığınız log toplama yazılımı ile birlikte bir rapor/uyarı mekanizması üretmenizi sağlar. Tabi farklı şekillerde de bu işlemleri loglayabilirsiniz ama http status code bence en pratik yöntem.

İstek atanlara yardımcı olması açısından rate limit ile ilgili ek bilgileri headers içerisinde verebilirsiniz. Örneğin Laravel paketinde X-RateLimit-Limit, X-RateLimit-Remaining, ve Retry-After kodlarını göreceksiniz. Retry-After eğer rate limite takıldıysanız karşınıza çıkar. Böylece client tarafında yapılan istekleri buna göre uyarlayabilirsiniz.

Leave a Reply

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir