Yük Dengeleyiciler ve Gerçek IP Adresi Karmaşası

Güvenlik uzmanı olarak çalışıyor olmanın getirdiği en güzel artılardan bir tanesi şüphesiz hemen hemen her ekip ile içli dışlı oluyor olmaktır. Güvenlik denetimi sonrası tespit edilen bulgular nedeniyle başta yazılım geliştiriciler olmak üzere  orta ve alt katman sorumluluları, veri tabanı yöneticileri ve analistler ile sayısız kez çalışma imkanım oldu. Bir uygulamanın düzgün ve güvenli çalışması için teoride bir arada, gerçekte ise birbirinden tamamiyle uç noktalarda çalışan bu ekipler temeli ortak olan güvenlik zafiyetleri ile baş etmeye çalışırlar. Bu yazıda dile getireceğim hususlar ve yaşanmış tecrübeler de tam olarak bununla ilgili olacak.

Günümüz web uygulamaları birden fazla uygulama sunucusu ve veri tabanı sistemleri üzerinde çalışır. Temel anlamda tamamiyle aynı kaynak kodu paylaşan iki adet uygulama sunucusu düşünün. Kullanıcıdan gelen istekler, yük durumuna göre bu iki sunucudan herhangi birisi tarafından karşılanmak üzere hazırdır. Hangi talebin hangi uygulama sunucusuna aktarılacağının kararını, HTTP taleplerini uygulama sunucularının önünde karşılayan yük dengeleyici -load balancer- mekanizma yapar. Buda bizi, orta katman sistem yöneticileri ve yazılımcıların dahil bir büyük probleme sürükler. Kullanıcının gerçek IP adresi nedir ?

load-balancing-diagram-2

Yukarıdaki diagram, “yük dengeleyici” kelimesini çok net bir şekilde anlatan grafiktir. Güvenlik uzmanları açısında yük dengeleyiciler, time-based sql injection ataklarında çok ciddi sorunlar yaşatıyor olsada bizim bu yazıda ki konumuz tamamiyle IP Spoofing ile ilgili olacak.

Proxy ve Real IP

Proxy kelime olarak vekil sunucu anlamına gelir. Yani iki sistem arasındaki iletişimi taşımaktan sorumlu vekillerdir. Yukarıdaki resme bakıldığında da vekil görevini load balancer’ın yüklendiği görülmektedir. Yani hem kullanıcı hemde uygulama sunucusu ile konuşan tek bir sistem mevcuttur. Ağ trafiği anlamında baktığımızda da web1 veya web2 sunucuları her zaman load balancer’ın IP adresi ile konuşmaktadır. Aynı durum kullanıcılar içinde geçerlidir pek tabi.

Bir Yazılımcı, Bir Orta Katman Sorumlusu ve XFF

Varsayalım ki, bu uygulamayı geliştiren kişi bir kişi var. İş birimide diyor ki “Kullanıcıların hatalı parola denemeleri vb gibi tüm aktiviteler IP adresleri ile birlikte kayıt altına alınacaktır.” . İlk başta kullanılan programlama dilinin sağladığı imkan ile HTTP talebinin karşılandığı anda kullanıcının IP adresini tespit eden geliştirici, uygulamanın tüm akışı içerisinde bu veriyi kullanmaya devam eder. Geliştirme süreci boyunca her zaman kendi ip adresi sabit olacağı için (kurumsal ağlarda kullanıcı bilgisayarları MAC adresi üzerinden static IP tahsis edilir) kendi testleri boyunca hep aynı adresi görmesi normal olacaktır. Bir süre sonra iş birimi tarafından gerçekleştirilen kabul testlerinde bu sorun ortaya çıkar ve yazılım geliştiriciye durum iletilir. Bu noktada kişi “E madem bir şekilde herkesin IP adresi aynı geliyor… Development ortamında kendime bir controller yazayımda uygulamaya iletilen HTTP request’ini raw halde döküp bakayım.” diyecektir. İşte belkide ilk defa, belkide çok benzer bir hikayeyi daha önce yaşadığı için X-Forwarded-For ile tanışılan an gelir.

Uygulama sunucusuna X-Forwarded-For adında bir header bilgisi gelmektedir ve içerisinde hep loglarda gördüğü load balancer’ın değil ipconfig ile kontrol ettiği  kendi ip adresini görmektedir. Peki o zaman aşağıdakine çok benzer kod ile bu sorunu çözebilirim der.

function get_ip_address() {
    $ip_keys = array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR');
    foreach ($ip_keys as $key) {
        if (array_key_exists($key, $_SERVER) === true) {
            foreach (explode(',', $_SERVER[$key]) as $ip) {
                // trim for safety measures
                $ip = trim($ip);
                // attempt to validate IP
                if (validate_ip($ip)) {
                    return $ip;
                }
            }
        }
    }
    return isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : false;
}

(Çok iyimser yaklaşmış olsamda, gelen değerin geçerli bir IP adresi olup olmadığının kontrol edilmesinin elzem olduğunu yazılım başka bir hikayede öğrenecektir aslında.)

Bu yaklaşım ile yazılımcının aslında farkında olmadan kabul ettiği bir varsayım ortaya çıkmaktadır.

Uygulama sunucusuna gelen X-Forwarded-For güvenilirdir. Kullanıcı zaten proxy kullanıyorsa onun IP adresi değiştirilemez ki… Zira HTTP protokolünde IP spoofing yapılamaz.(!)

Hikayemiz burada yazılım geliştirici için son buldu. Lakin girizgahta söylediğim “Bir uygulamanın düzgün ve güvenli çalışması için teoride bir arada, gerçekte ise birbirinden tamamiyle uç noktalarda çalışan bu ekipler temeli ortak olan güvenlik zafiyetleri ile baş etmeye çalışırlar” cümlesini hatırlamakta fayda var. Birde load balancer’ın konfigürasyonundan sorumlu kişi açısından olayı ele alalım.

Network alanında son derece donanımlı olan sistem yöneticisi aşağıdaki cümleleri düşünür.

Yazılımcılar belkide X-Forwarded-For vb gibi bilgileri kayıt altına alıyordur. Zaten HTTP talebi içerisinde ki hiçbir veriye güvenilmeyeceğini herkes bilir. Ben gelen X-Forwarded-For’u de ileteyim. Herhangi bir probleme sebebiyet vermek istemiyorum.

Bu nedenle dış dünyadan gelen X-Forwarded-For’ü iletir. Lakin bunun yanı sıra kendisine talebi gönderen sistemin TCP source adresini ikinci bir header değeri olarak -Örneğin; True-Client-IP- gönderir.

İki farklı birimin özünde aynı olan sorun ile ilgili iki farklı yaklaşımı bir zafiyet doğurmuş olur. Client IP Spoofing zafiyeti. Bu nedenle hiçbir IP bazlı loglama ve daha da kritiği IP bazlı yetkilendirmenin işe yaramayacağı son derece kritik bir durum ortaya çıkar. Benim pentestlerde tespit ettiğim ilginç olaylar bir yana dursun -NDA-  http://blog.ircmaxell.com/2012/11/anatomy-of-attack-how-i-hacked.html şu hikayeyi herkesin okumasını öneririm. Bu yazıda ele alınan konuya harika bir örnektir.

Pentestler

Şahsen ben tüm güvenlik denetimlerimde Firefox kullanmaktayım. Firefox’ü basit bir eklenti ile konfigüre ederek tüm HTTP taleplerinde “X-Forwarded-For: 127.0.0.1” eklenmesini sağlıyorum. Böylece tüm pentestlerde bu tür zafiyetleri yakalama imkanım artmakta. Tabi OWASP Checklist’e göre denetim gerçekleştirmek, bu tür zafiyetleri kontrol etmeyi unutmayı engeller lakin XFF zafiyetnin varlığını tespit etmek için, uygulama içerisinde sizin veya alınan aksiyonların ip adresini gösteren vb bir modülünün olması şart.

Çözüm ve Öneriler

Kurumlar için olmazsa olmazlardan bir tanesi, tüm yazılım ekiplerine ve dış kaynak firmalara uymaları için zorunlu tutulacak bir güvenli uygulama geliştirme dokümanıdır. Bunu hazırlamak şart. Örneğin kurum olarak; “Kullanıcı IP adresine ihtiyacın varsa, XXX isimli header bilgisini kullanın” deniyor olmalıdır. Aksi halde farklı ekipler ve özelliklede kod kalitesinin ölçülmesi son derece zor olan ve ihale ile iş verilen dış kaynak firmalarda ki yazılım geliştiricilerin insiyatifine kalacaktır.

Spesifik olarak bu problemi çözmek için aşağıdaki F5 kuralı kullanılabilir.

when HTTP_REQUEST {
  HTTP::header remove X-Forwarded-For    
  HTTP::header insert X-Forwarded-For [IP::remote_addr]
}

Bu kural, dış dünyadan gelen HTTP talebi içerisindeki X-Forwarded-For alanını kaldırır. Ardından kendisine talebi ileten sistemin IP adresini (buraya dikkat, bu değeri TCP source adresinden hesaplar. Böylece spoof edilemezdir) ekleyerek talebi iletir. Böylece hali hazırda X-Forwarded-For’a göre hareket eden yazılımlar için güvenilir bir liste sunulmuş olur.

Ve son önerim ise, yazılım geliştiricilere “SDLC Eğitimi” haricinde gerçek hayatta karşılaşılan zafiyetleri ve bu gibi durumların analizlerini çözümleri ile birlikte anlatan özel eğitimlerin organize edilmesi. INVICTUS EUROPE olarak özellikle kurumsal firmalarda çalışan yazılım geliştiriciler için hazırladığım “Gerçek Hayat Örnekleri ile Web Uygulama Güvenliği” eğitiminde, 10 yılı aşkın süredir tecrübe ettiğim bu tür disipliner farklılıkların ürettiği zafiyetlere karşı yazılım geliştiriciler olarak hangi alışkanlıklar edinilmelidiri anlatmaktayım.