İstemciye Asla Güvenme: Mobil Uygulamada Güvenliğin Temeli
Mobil uygulama, kullanıcının cihazında çalışan ve incelenebilen bir program. Buradan çıkan tek kural var: istemciden gelen hiçbir şey doğru kabul edilemez. Bu kuralın pratikte ne anlama geldiğini yazdım.
Mobil uygulama, kullanıcının kendi cihazında çalışan bir program. Kullanıcı o dosyayı açabilir, içine bakabilir, çalışırken ağ trafiğini izleyebilir, isterse değiştirip yeniden paketleyebilir. Bu bir güvenlik açığı değil — işin doğası.
Buradan çıkan tek bir kural var ve bütün mobil güvenlik pratiği bu kuralın uygulanmasından ibaret: istemciden gelen hiçbir bilgi doğru kabul edilemez. Bu yazıda bu kuralın somut karşılıklarını ve en sık gördüğüm ihlallerini yazdım.
1. Gizli anahtarı uygulamaya gömmek
En yaygın hata bu. Bir servis anahtarını sabit olarak koda yazmak, onu gizlemiyor — sadece bulunmasını birkaç dakika zorlaştırıyor. Derlenmiş bir uygulamadan metin dizilerini çıkarmak ilk adımda yapılan şey.
Burada ayrım yapmak gerekiyor:
- Herkese açık olması sorun olmayan anahtarlar — bazı servislerin istemci anahtarları böyle tasarlanmıştır; onları uygulamada tutmak normaldir, ama mutlaka kullanım kısıtı (paket adı, imza, alan adı, kota) tanımlanmalıdır.
- Gizli kalması gereken anahtarlar — ödeme sağlayıcısının gizli anahtarı, veritabanı yönetici erişimi, e-posta gönderim anahtarı. Bunlar uygulamada hiçbir koşulda bulunmamalı.
İkinci gruptaki her işlem, uygulamadan değil sunucudan yapılmalı. Uygulama sunucuya “şunu yap” der; gizli anahtarı kullanan taraf sunucudur. Bu, tek satırlık bir kural ama mimariyi baştan belirliyor.
Karartma (obfuscation) ve küçültme araçları, kodun okunmasını zorlaştırır ve uygulama boyutunu düşürür — ikisi de faydalı. Ama bir sırrı korumazlar. Karartılmış bir uygulamadan da anahtar çıkarılabilir, sadece biraz daha uzun sürer. Karartmayı bir katman olarak kullanın; tek savunma olarak değil.
2. Doğrulamayı yalnızca istemcide yapmak
Form doğrulaması, kullanıcı deneyimi için istemcide yapılır — kullanıcı sunucuya gidip gelmeden hatasını görsün diye. Ama güvenlik için yapılan doğrulama sunucuda olmak zorunda, çünkü istemci tarafı atlanabilir.
Somut örnek: bir ilan oluşturma ekranında fiyatın sıfırdan büyük olması gerekiyor. İstemcide kontrol ediyorsunuz ve sunucuya doğrudan istek gönderen biri sıfır fiyatlı ilan oluşturabiliyor. Aynı kontrolün sunucuda tekrar yapılması gerekiyor — “tekrar” değil, aslında asıl kontrol orası.
Kendime koyduğum ölçüt şu: bu kontrol atlanırsa veri bozulur mu, para kaybedilir mi, biri başkasının verisine erişir mi? Cevap evetse kontrol sunucuda olmalı. Hayırsa istemcide kalabilir.
3. Fiyatı ve miktarı istemciden almak
Bu, ikinci maddenin en pahalı özel hali. Sipariş isteğinin içinde ürünün fiyatını göndermek, kullanıcının fiyatı belirlemesine izin vermek demek.
Doğru akış şöyle: istemci sadece ne istediğini gönderir — ürün kimliği ve adet. Fiyat, indirim, kargo ve toplam sunucuda hesaplanır:
// İstemcinin gönderdiği
{ "urunId": "p_4471", "adet": 2, "kuponKodu": "YAZ25" }
// Sunucunun yaptığı
// 1. Ürünü veritabanından oku, güncel fiyatı al
// 2. Stok yeterli mi kontrol et
// 3. Kupon geçerli mi, bu kullanıcı için uygun mu, süresi dolmuş mu
// 4. Toplamı hesapla
// 5. Ödemeyi bu toplam üzerinden başlat
Aynı mantık uygulama içi satın almalarda da geçerli. Kullanıcının bir aboneliği olup olmadığına, uygulamanın “satın alma başarılı” demesine bakarak karar verilmez. Mağazadan gelen makbuz, sunucu tarafında mağazanın doğrulama servisiyle kontrol edilir ve kullanıcının yetkisi sunucudaki kayda göre belirlenir.
4. Kimlik doğrulama ile yetkilendirmeyi karıştırmak
Kimlik doğrulama (authentication) “sen kimsin” sorusunu cevaplar. Yetkilendirme (authorization) “bunu yapmaya hakkın var mı” sorusunu. İkincisi çok daha sık atlanıyor.
Klasik hata: kullanıcı giriş yapmış olduğu için isteği kabul etmek. Oysa giriş yapmış bir kullanıcı, isteğin içindeki kimliği değiştirip başkasının verisine erişmeye çalışabilir. Sunucu her istekte şunu sormalı: bu oturumdaki kullanıcı, bu belirli kaydın sahibi mi?
Kullanıcı kimliğini istekten değil, doğrulanmış oturum bilgisinden okumak bu sorunu kökten çözüyor. İstek gövdesindeki bir kullaniciId alanına asla güvenmiyorum.
Yönetilen bir veritabanı kullanıyorsanız, kurallar dosyası çoğu zaman en kritik güvenlik katmanı oluyor — çünkü istemci veritabanına doğrudan konuşuyor. Geliştirme sırasında “herkese açık” bırakılan kuralları yayına almadan önce daraltmak şart. Ben bu dosyayı, uygulamanın kendisi kadar dikkatle gözden geçiriyorum ve her koleksiyon için okuma/yazma haklarını ayrı ayrı yazıyorum.
5. Hassas veriyi cihazda uygun olmayan yerde tutmak
Oturum jetonu, yenileme jetonu gibi bilgiler basit tercih deposunda değil, platformun sunduğu güvenli depoda tutulmalı — iOS tarafında anahtar zinciri, Android tarafında şifrelenmiş depolama. Basit tercih dosyaları, kök erişimi olan bir cihazda okunabiliyor.
Buna ek olarak: hassas veriyi gereksiz yere saklamamak da bir güvenlik önlemi. Sakladığınız her veri, korunması gereken bir yükümlülük. Kredi kartı bilgisini hiç almamak, onu güvenle saklamaktan her zaman daha iyidir — ödeme sağlayıcısının arayüzüne yönlendirmek bu yüzden hem güvenli hem ucuz.
6. Günlüklere her şeyi yazmak
Hata ayıklarken günlüğe istek ve cevapların tamamını basmak alışkanlık haline geliyor. Yayın sürümünde bu kod kalırsa, jetonlar ve kişisel veriler cihaz günlüklerine düşüyor.
Yaptığım şey, günlükleme çağrılarını yayın derlemesinde tamamen devre dışı bırakmak ve hassas alanları yazan yerleri baştan maskelemek. Bir sürümü yayına almadan önceki kontrol listemde “hata ayıklama günlükleri kapalı mı” maddesi bu yüzden var.
7. Ağ trafiğini korumasız bırakmak
Bugün her istek şifreli bağlantı üzerinden gidiyor; bu artık varsayılan. Buna ek olarak yapılabilecek şey sertifika sabitleme (certificate pinning): uygulamanın yalnızca belirli bir sertifikaya güvenmesi.
Bu, araya girme saldırılarına karşı ciddi bir katman ama bedeli var: sertifika yenilendiğinde uygulamayı da güncellemek gerekiyor, aksi halde sahadaki bütün kullanıcılar bağlantı kuramıyor. Bu yüzden sabitlemeyi her projede değil, gerçekten yüksek riskli akışları olan projelerde ve yedek sertifika tanımlayarak kullanıyorum.
Bir kontrol listesi
Bir uygulamayı yayına almadan önce geçtiğim güvenlik listesi:
- Uygulamanın içinde gizli kalması gereken bir anahtar var mı
- Herkese açık anahtarların kullanım kısıtı tanımlı mı
- Her yazma işlemi sunucuda yetki kontrolünden geçiyor mu
- Tutar ve fiyat sunucuda mı hesaplanıyor
- Satın almalar sunucuda doğrulanıyor mu
- Veritabanı güvenlik kuralları daraltıldı mı
- Jetonlar güvenli depoda mı
- Yayın sürümünde hata ayıklama günlükleri kapalı mı
- Uygulamanın topladığı veri, mağaza beyanıyla uyuşuyor mu
Bu listedeki maddelerin hiçbiri karmaşık değil. Hepsi, en başta yazdığım tek cümlenin farklı görünümleri: istemci sizin kontrolünüzde değil, o yüzden önemli olan her karar sunucuda verilmeli.
- Arka Uç
- Güvenlik
- Mobil Geliştirme
- Mimari
Demir Taşdemir
Mobil Uygulama & Web Geliştirici
2018'den beri yazılım geliştiriyorum. App Store ve Google Play'de 11 uygulama yayınladım; şu an 6 mobil uygulama, 1 e-ticaret platformu ve 1 masaüstü oyun üzerinde çalışıyorum.
Uygulamanızın güvenlik tarafı gözden geçirilmeli mi?
Yetkilendirme kuralları, anahtar yönetimi ve sunucu tarafı doğrulama üzerine mevcut yapınızı inceleyebilirim. İletişim sayfasından yazmanız yeterli.