Server Side Request Forgery (SSRF)

Server Side Request Forgery Nedir ?

En basit tabirle, saldırganın bir sunucuyu kendisi adına istekte bulunmaya zorlamasına dayanan bir güvenlik açığıdır. SSRF, saldırgana bu güvenlik açığını barındıran sunucudan istek oluşturması veya buradan gelen istekleri kontrol edebilmesi için web uygulamasındaki bir parametreyi değiştirmesine izin verir.

  • Kullanıcı tarafından sağlanan girdilere güvenilmesi.

  • URL Doğrulama ve Filtreleme eksikliği.

  • Kısıtlanmamış işlemler.

Yukarıdaki maddelerden herhangi biri gerçekleştiğinde ortaya çıkabilecek bir zafiyettir.

Nelere Sebep Olur ?

  • Zafiyeti barındıran sunucu ile diğerleri arasındaki güven ilişkisi kötüye kullanılabilir.

  • IP kontrolü atlatılabilir.

  • Sunucunun bağlı olduğu yerel ağ taranabilir. (Port taraması)

  • Sunucuda bulunan dosyalar okunabilir.(?url=file:///etc//passwd)

  • Host tabanlı kimlik doğrulama servisleri atlatılabilir.

  • Uzaktan kod çalıştırılabilir.

  • Başka sunuculara DOS saldırısı düzenlenebilir.

  • Cloudflare vb. hizmetler bypass edilebilir.

  • Internal servislere erişim sağlanabilir.

  • Kritik bilgilere erişebilir.

  • Kullanıcıların yapamaması gereken işlemleri yaparak bazı ekleme, silme, değiştirme vb.. işlemlere olanak sağlanabilir.

Zafiyeti Nasıl Kaynaklandığını Anlamak

SSRF i anlamak için ihtiyaç duyduğumuz temel bilgilerden biri bir URL nin hangi parçalardan oluştuğudur.

URL Hangi Parçalardan Oluşur

  • Protocol(Scheme): URL'nin başlangıcında bulunan ve iletişim protokolünü belirten kısımdır. Örneğin, "https://" veya "http://" gibi.

  • User Information: Opsiyonel olarak, kullanıcı adı ve şifre gibi bilgileri içeren kısımdır. Genellikle kullanıcı kimlik doğrulaması gerektiren sistemlerde kullanılır. Ancak, modern web uygulamalarında genellikle kullanılmaz.

  • Host (Domain): URL'nin hedeflendiği sunucunun veya web sitesinin adını belirtir. Örneğin, "www.example.com".

  • Port (Bağlantı Noktası): Sunucuya erişmek için kullanılan port numarasını belirtir. Örneğin, "https" için genellikle 443, "http" için 80 kullanılır. Ancak, belirtilmezse tarayıcılar genellikle varsayılan port numarasını kullanır.

  • Path (Yol): Sunucuda hedeflenen belgenin veya kaynağın yolunu belirtir. Örneğin, "/index.html" veya "/images/logo.png" gibi.

  • Query (Sorgu): URL'nin dinamik içeriği belirtmek için kullanılan bir kısmıdır. Genellikle "?" işareti ile başlar ve anahtar-değer çiftleri şeklinde bilgiler içerir. Örneğin, "?id=123&name=John".

  • Anchor (Kotrol): Sayfanın belirli bir bölümüne doğrudan bağlantı yapmak için kullanılır. Genellikle "#" işareti ile başlar ve bir elementin ID'sini veya bir konumu belirtir. Örneğin, "#section2" veya "#top".

Golang ile Zafiyetli Kod Örneği

app.Post("/", func(c *fiber.Ctx) error {
	url = c.FormValue("url")
	resp, err := http.Get(url)
	if err != nil {
		return c.SendString(fmt.Sprintf("GET isteği başarısız oldu: %v", err))
	}

	defer resp.Body.Close()
	body, err := io. ReadAll(resp.Body)
	if err != nil {
		return c.SendString(fmt.Sprintf("Yanıt okuma hatası: %v", err))
	}
	
	return c.SendString(string(body))
})

Yukarıdaki kod parçacı aslında bir web application back-end uç noktasına ait bir api dir. Koddan anlaşılacağı üzere bu api mize form value içerisinde "url" ismine sahip bir input geliyor ve bu inputa herhangi bir işlem uygulanmıyor sonrasında doğrudan bir http isteğinde kullanılıyor. Daha sonra http isteğinden gelen cevabı da doğrudan isteği yapan client e döndürüyor. Bu yüzden bu kod parçası SSRF zafiyetine sahiptir.

Peki ya bu koddan SSRF zafiyetini nasıl arındırıcaz ?

parsedURL, err := url.ParseRequestURI(userURL)
if err != nil {
	return c.SendString(fmt.Sprintf("Geçersiz URL: %v", err))
}

allowedHosts := []string{"example.com", "trusted-site.com"}//Güvenilir kaynakların listesi

if !contains(allowedHosts, parsedURL.Host) {
	return c.SendString("Bu kaynağa erişim izni yok")
}

Burada client den alınan URL önce parse ediliyor çünkü daha sonra izin verilen Domain adreslerini içeren bir whitelist ile karşılaştırma yapılmak isteniyor. Whitelist içeren değişkenimiz allowedHosts dur. Daha sonra client den alınan URL in host kısmı whitelist imizin içerisinde mi kontrol etmek için aşağıdaki kodunu yazdığımız fonksiyona veriliyor.

func contains(arr []string, target string) bool {
	for _, item:= range arr {
		if item==target {
			return true
		}
	}
	return false
}

Eğer client den alınan Domain adresi whitelist imizin içerisinde varsa true değeri dönüyor ve sadece izin verilen bir adrese istek yapılması sağlanarak manipülasyondan kaçınılmış olunuyor.

Nasıl Önlenir ?

  • Whitelist kullanımı.

  • Blacklist kullanımı.

  • Kullanıcıdan alınan inputların sanitize (temizleme) yöntemleri.

  • Kullanılmayan URL şemalarını devre dışı bırakma.(ftp, file)

  • İç Ağda Bulunan Servislere Erişim İçin Kimlik Doğrulama.(redis, mongodb)

  • Sunucu Taraflı Yanıt Kontrolü (Response Handling).

Yararlı Kaynaklar
Bu yazı Dinçer Güner tarafından yazılmıştır.

Last updated