Eskiden her şey daha basitti; bir sistemin güvenliğini sağlamak için etrafına devasa bir 'firewall' örer, 'DMZ' bölgesini ayarlar ve akşam huzurla uyurduk. Ama dünya değişti. Artık kimse kurumsal uygulamalara sadece ofisindeki masaüstü bilgisayardan girmiyor. O devasa güvenlik duvarlarının yerini, herkesin cebinde taşıdığı, metroda, kafede, hatta uçakta kullandığı mobil cihazlar aldı. Trendler öyle bir değişti ki; artık siber saldırganlar kurumsal ağın kapısını zorlamak yerine, o ağın anahtarını cebinde taşıyan son kullanıcının mobil uygulamasına odaklanıyor.
Sizin için 'FaceID ile giriş yapıyorum, çok güvenliyim' dediğiniz noktada, biz Red Team tarafında aslında başka bir senaryoyu izliyoruz. Mobil güvenlik sadece ekran kilidi veya biyometrik veriyle bitmiyor. O uygulamanın kodunun içinde ne saklı? Veriler cihazda nasıl tutuluyor? İşte bugün, testCompany bünyesinde yaptığımız simülasyonlardan ve sahadaki tecrübelerden yola çıkarak, mobil güvenliğin o pek konuşulmayan, 'mutfak' kısmına dalacağız.
Hardcoded Sırların İronisi
Bir mobil uygulamayı analiz etmeye başladığımızda ilk baktığımız yer, geliştiricinin 'kimse burayı görmez' diyerek kodun arasına serpiştirdiği statik verilerdir. Android dünyasında bir APK dosyasını jadx veya apktool ile açmak sadece saniyeler sürüyor. Geliştiriciler bazen API anahtarlarını, şifreleme tuzlarını (salt) hatta test ortamı kimlik bilgilerini kodun içine gömüyorlar.
Şu örneğe bir bakın (zararsızlaştırılmış/defanged):
// Kötü bir pratik örneği
public class APIClient {
private static final String API_KEY = "AKIA_MOCK_12345_67890_EXAMPLE";
private static final String BASE_URL = "https://api.testcompany-example.com/v1/";
public void connect() {
// Bağlantı mantığı...
}
}
Yukarıdaki kodu decompile eden herhangi biri, testCompany platformunun hassas API anahtarına saniyeler içinde ulaşabilir. 'Ama ben kodu obfuscate (karmaşıklaştırma) ediyorum' diyebilirsiniz. ProGuard veya R8 kullanmak değişken isimlerini a, b, c yapar ama o string değeri (API Key) orada kabak gibi kalmaya devam eder. Çözüm mü? Hassas anahtarları asla istemci tarafında tutmamak veya güvenli elementlerde (Keystore/Keychain) dinamik olarak yönetmek.
Veri Depolama: 'Shared Preferences' Bir Kasa Değildir
Hemen hemen her sızma testinde karşılaştığımız bir başka klasik: Hassas verilerin SharedPreferences (Android) veya UserDefaults (iOS) içerisinde düz metin olarak saklanması. Bu alanlar, uygulamanın ayarlarını (karanlık mod açık mı, dil seçeneği ne vb.) tutmak içindir, kullanıcının JWT token'ını veya kredi kartı numarasının son 4 hanesini değil.
Root'lanmış bir cihazda veya bir yedekleme (backup) saldırısında, şu dosya içeriğine ulaşmak çocuk oyuncağıdır:
<!-- /data/data/com.testcompany.app/shared_prefs/UserPrefs.xml -->
<map>
<string name="session_token">eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.mock_data_for_education</string>
<string name="user_email">[email protected]</string>
</map>
Bunu gören bir saldırgan, session token'ı alıp kendi cihazına 'inject' edebilir ve sizin kimliğinize bürünebilir. Eğer bir veriyi cihazda saklamanız şartsa, mutlaka EncryptedSharedPreferences gibi donanım tabanlı şifreleme kütüphanelerini kullanmalısınız.
Frida ile Gerçek Zamanlı Manipülasyon
Şimdi işin biraz daha 'geek' ve eğlenceli kısmına gelelim. Diyelim ki uygulamanızda sıkı bir 'Root Detection' (Root tespiti) mekanizması var. Uygulama açılırken cihazın root'lu olup olmadığını kontrol ediyor ve eğer öyleyse kendini kapatıyor. Güvenli mi? Hayır.
Frida gibi 'dynamic instrumentation' araçları sayesinde, biz uygulamanın çalışma anındaki mantığını değiştirebiliyoruz. Uygulama 'Bu cihaz root'lu mu?' diye sorduğunda, biz araya girip 'Hayır, tertemiz!' cevabını verdirtiyoruz.
İşte basit bir Frida script örneği (Bypass mantığını anlamak için):
// root_bypass_mock.js
Java.perform(function () {
var RootCheckClass = Java.use("com.testcompany.security.RootChecker");
// isDeviceRooted fonksiyonunu her zaman false dönecek şekilde override ediyoruz
RootCheckClass.isDeviceRooted.implementation = function () {
console.log("[!] Root check çağrıldı, 'false' dönülerek bypass ediliyor...");
return false;
};
});
Bu tarz araçlara karşı savunma yapmak için tek bir kontrol noktasına güvenmek yerine, 'multi-layered' (çok katmanlı) bir kontrol yapısı kurmak ve hatta uygulamayı çalışma zamanında kendi bütünlüğünü kontrol eder (RASP - Runtime Application Self-Protection) hale getirmek gerekiyor.
SSL Pinning: Ortadaki Adamın (MITM) Kabusu
Birçok kişi HTTPS kullanmanın yeterli olduğunu düşünür. Ancak saldırgan, cihazın sertifika deposuna kendi sahte sertifikasını eklerse (ki fiziksel erişim veya sosyal mühendislikle bu mümkün), uygulamanız ile sunucu arasındaki tüm trafiği Burp Suite gibi araçlarla 'cleartext' olarak izleyebilir.
Hassas finansal veriler veya kişisel veriler taşıyorsanız, SSL Pinning (Sertifika Sabitleme) olmazsa olmazdır. Uygulama, sadece kendi bildiği spesifik bir sertifika veya public key ile konuşmayı kabul etmelidir. Tabii Frida'nın SSL Pinning bypass script'leri olduğunu da unutmamak lazım; bu yüzden kontrolü sadece client tarafında bırakmayıp, sunucu tarafındaki anomalileri de izlemek şart.
Son Söz Yerine: Geliştiricilere Tavsiyeler
Arkadaşlar, mobil dünya bir 'vahşi batı' gibidir. Kullanıcının cihazı asla güvenli bir ortam değildir. Uygulamanızı yazarken şu üç kuralı aklınızdan çıkarmayın:
- Cihazı Güvensiz Kabul Et: Cihaz root'lu olabilir, belleği izleniyor olabilir. Kritik kararları (yetkilendirme gibi) asla sadece istemci tarafında vermeyin.
- Gereksiz Veri Tutma: Cihazda saklamadığınız veri çalınamaz. Eğer saklamanız gerekiyorsa, işletim sisteminin sunduğu en yüksek şifreleme seviyelerini kullanın.
- Obfuscation Yeterli Değildir: Kodunuzu karıştırmak bir güvenlik önlemi değil, sadece bir hız tümseğidir. Gerçek güvenlik, doğru mimari ve güvenli kodlama prensipleriyle başlar.
Mobil güvenlik, bir kedi-fare oyunudur. Biz Red Team olarak o fare deliklerini bulmaya, sizler de o delikleri kapatmaya devam edeceksiniz. testCompany gibi yapılarda bu bilinci yaymak, sadece kod yazmaktan çok daha kritik bir hal aldı.
Güvenli kalın, sistemlerinize iyi bakın!
