İçeriğe geç
Sedat Özdemir
Yazılar

android-security

Cepteki Casus: Mobil Uygulamanın Arka Kapısını Kendi Ellerinle mi Açtın?

Mobil dünyada 'binary' güvenliği bir efsaneden ibaret. APK'nın içine sakladığın o secret anahtarların aslında ne kadar açıkta olduğunu hiç merak ettin mi?

Sedat Özdemir
· 4 dk okuma

Uygulamanın APK veya IPA dosyasını markete yüklediğin an, içindeki tüm sırların korunaklı bir kasaya kilitlendiğini mi sanıyorsun? Eğer öyleyse, kahveni tazele çünkü birazdan o kasanın aslında şeffaf bir camdan ibaret olduğunu konuşacağız.

Selam millet, ben Sedat. Bugün testCompany’deki yoğun Red Team mesaisinden bir boşluk bulup, çoğumuzun 'mobil işte, web kadar kolay sızılmaz' diyerek biraz üvey evlat muamelesi yaptığı mobil uygulama güvenliğine dalmak istedim. Genelde web tarafında SQLi, XSS kovalarken mobil uygulamalar bir kara kutuymuş gibi algılanıyor. Ama o kutunun kapağını bir kere araladığında, aslında ne kadar 'savunmasız' bırakıldığını görüyorsun.

Binary Güvenliği Bir İllüzyondur

Bir mobil uygulama geliştiricisi olarak yazdığın kodlar derleniyor, paketleniyor ve kullanıcıya ulaşıyor. Birçok ekip, bu derleme (compilation) sürecinin kodu okunamaz kıldığını varsayıyor. Ancak gerçek şu ki; jadx-gui gibi bir aracı açıp APK dosyasını içine sürüklemek, bir kitabı kütüphaneden alıp okumaktan daha zor değil.

Eğer kodun içinde bir yerlere hardcoded (sabitlenmiş) API anahtarları, şifreleme salt değerleri veya test ortamına ait URL’ler bıraktıysan, bunlar Red Team operasyonunda bizim için 'altın vuruş' oluyor.

Bak, sahada karşılaştığımız klasik bir senaryodan örnek vereyim:

<!-- res/values/strings.xml içinde saklanan 'gizli' bilgiler -->
<resources>
    <string name="app_name">GüvenliMobilUygulama</string>
    <string name="api_key">AIzaSy_MOCK_KEY_FOR_DEMO_PURPOSES_12345</string>
    <string name="firebase_database_url">https://testcompany-prod-default-rtdb.firebaseio.com</string>
</resources>

Yukarıdaki gibi bir strings.xml dosyası bulduğumuzda, o uygulamanın backendine giden yolu yarıladık demektir. Bu sadece başlangıç. Kodun içine gömülen şifreleme anahtarlarını bulduğumuzda ise veritabanındaki kullanıcı verilerini çözmek an meselesi haline geliyor.

Yerel Veri Depolama: Güvenli mi, Şehir Efsanesi mi?

Mobil cihazlar kişiseldir, bu yüzden 'veriyi cihazda tutarsam bir şey olmaz' yanılgısı çok yaygın. Ancak rootlanmış bir cihazda veya jailbreak yapılmış bir iPhone’da, uygulamanın sandbox (kum havuzu) koruması devre dışı kalır.

SharedPreferences (Android) veya UserDefaults (iOS) üzerinde saklanan hassas veriler, düz metin (plain text) olarak orada öylece bekler. Bir Red Teamer olarak cihaza fiziksel veya uzaktan erişim sağladığımızda ilk baktığımız yer /data/data/com.example.app/shared_prefs/ klasörüdür.

Yanlış Uygulama:

SharedPreferences pref = getSharedPreferences("MyPrefs", MODE_PRIVATE);
Editor editor = pref.edit();
editor.putString("user_token", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..."); // KESİNLİKLE YAPILMAMALI
editor.apply();

Doğru Uygulama (Defensive Approach): Veriyi mutlaka Android’in EncryptedSharedPreferences kütüphanesiyle veya iOS tarafında Keychain kullanarak, donanım tabanlı (Keystore) anahtarlarla şifrelemelisin. Sadece şifrelemek de yetmez, anahtarın nerede durduğu da kritiktir.

SSL Pinning: Ortadaki Adam (MitM) Seni İzliyor

'Bizim uygulamamız tüm trafiği HTTPS üzerinden gönderiyor, yani güvendeyiz' diyorsan, Burp Suite kullanan birinin ağ trafiğini nasıl manipüle edebileceğini henüz görmemişsin demektir. Standart HTTPS bağlantılarında cihazdaki güvenilir sertifika otoriteleri (CA) kullanılır. Eğer bir saldırgan kendi sahte sertifikasını cihaza yüklemeyi başarırsa (veya kullanıcıyı buna ikna ederse), senin tüm trafiğini 'Man-in-the-Middle' saldırısıyla okuyabilir.

Bunu engellemenin yolu SSL Pinning yapmaktır. Yani uygulamanın sadece senin sunucuna ait belirli bir sertifikayı veya public key'i tanımasını sağlamaktır.

Ancak dikkat: Red Team ekipleri olarak biz burada Frida kullanıyoruz. Frida ile çalışma zamanında (runtime) uygulamanın içine kanca (hook) atıp, senin SSL Pinning kontrolü yapan fonksiyonunu her zaman true dönecek şekilde manipüle edebiliyoruz.

Savunma Stratejisi: SSL Pinning yaparken sadece tek bir yöntem kullanma. Hem ağ seviyesinde (Network Security Config) hem de kod seviyesinde kontroller ekle. Ayrıca uygulamanın 'Rooted' veya 'Debuggable' olup olmadığını kontrol eden mekanizmalar geliştir ki, Frida gibi araçların çalışmasını zorlaştır.

Koda Kanca Atmak: Frida ile Sihirbazlık

Az önce Frida’dan bahsettim, biraz daha açalım çünkü bu bizim en sevdiğimiz oyuncaklardan biri. Diyelim ki uygulamanın içinde çok kritik bir 'Premium Check' fonksiyonu var veya bir şifre doğrulama mantığı kurdun.

// Uygulama içindeki mock fonksiyon
public boolean isUserAdmin(String username) {
    // Veritabanı sorgusu veya API çağrısı yapıldığını varsayalım
    return false;
}

Frida kullanarak bu fonksiyonun sonucunu, uygulamanın koduna dokunmadan, sadece o an bellekteki halini değiştirerek manipüle edebiliriz:

// Zararsızlaştırılmış Frida Script Örneği
Java.perform(function () {
    var targetClass = Java.use("com.testcompany.auth.UserChecker");
    targetClass.isUserAdmin.overload('java.lang.String').implementation = function (user) {
        console.log("Frida ile kanca atıldı, kullanıcı: " + user);
        return true; // Her zaman admin olarak dön!
    };
});

Bu durumdan korunmak için Code Obfuscation (kod karartma) araçları (ProGuard, R8 veya daha gelişmiş DexGuard gibi) kullanmalısın. Kodun okunabilirliğini ne kadar zorlaştırırsan, bizim o kancayı atacağımız doğru noktayı bulmamız o kadar vakit alır.

Gerçek Hayatta Ne Yapmalı?

testCompany gibi büyük yapılarda mobil uygulama geliştirirken şu checklist’i yanından ayırma:

  1. Kod Karartma (Obfuscation): ProGuard/R8 kullanmadan markete uygulama çıkarma. En azından sınıf ve metod isimlerin a, b, c olsun.
  2. Hardcoded Sırları Temizle: API key, secret gibi verileri asla kodun içine yazma. Bunları çalışma zamanında güvenli yollarla al veya NDK (C++ tarafı) içine gömerek bir nebze daha zorlaştır (ama unutma, NDK de imkansız değil).
  3. Bütünlük Kontrolü (Integrity Check): Uygulamanın imzası (signature) değişmiş mi? APK modifiye edilmiş mi? Bunu kontrol eden bir mekanizma kur.
  4. Hassas Veri Depolama: Kullanıcı token’larını asla düz metin saklama. EncryptedSharedPreferences senin dostundur.
  5. Log Yönetimi: Logcat’te asla kullanıcı verisi veya teknik detay (stacktrace gibi) bırakma. Bizim için en iyi bilgi kaynaklarından biri senin unuttuğun debug loglarıdır.

Mobil güvenlik, sadece kodu yazıp bitirmek değil; o kodun saldırganın elinde nasıl bir silaha dönüşebileceğini öngörmektir. Bir sonraki mobil projenizde 'Biri bu APK'yı açsa ne görür?' sorusunu kendinize sorun.

Güvende kalın, kodunuza sahip çıkın!

İlgili yazılar