Mobil Uygulama Güvenliği Nedir ve Neden Önemlidir?

Yusuf Pamukçu
Ford Otosan
Published in
6 min readDec 7, 2023

Merhabalar, bu yazımda mobil uygulama geliştirme süreçlerinin önemli bir kısmını oluşturan uygulama güvenliği konularından bahsediyor olacağım.

APK ve IPA Dosyaları Nedir?

Mobil uygulamalar, genellikle Android için APK (Android Package) ve iOS için IPA (iOS App Store Package) dosyaları şeklinde dağıtılır. Ancak bu dosyaların root veya jailbreak adı verilen işlemlerle açılması, uygulama güvenliğini tehlikeye atabilir. Root veya jailbreak, kullanıcıların cihazlarında daha fazla kontrol sağlayan işlemlerdir ancak aynı zamanda güvenlik riskleri taşır.

Bu işlemlerle uğraşan bir kişi, uygulamanın içindeki kodları görebilir ve hatta değiştirebilir. Bu da, uygulamanın tasarlandığı güvenlik önlemlerinin atlatılmasına yol açabilir.

Neden Root veya Jailbreak Tehlikeli Olabilir?

Root veya jailbreak yapılmış bir cihaz, normalde erişilemeyen sistem dosyalarına ve uygulama verilerine erişim sağlar. Bu, kötü niyetli kişilerin kullanıcı bilgilerine ve diğer hassas verilere ulaşmasını kolaylaştırabilir. Bu nedenle uygulama geliştiricileri bu tür durumları tespit etmeye ve önlem almaya çalışırlar.

Bu ilk bakışta karmaşık gibi görünen konular, aslında günlük hayatımızda kullandığımız uygulamaların güvenliği açısından oldukça önemlidir. Yazının devamında mobil uygulama güvenliğinin diğer önemli yönlerini ve bu güvenliği artırmak için alınabilecek önlemleri inceleyeceğiz.

APK ve IPA Dosyalarının Root ile Açılmasını Engelleme

Mobil uygulama güvenliğini sağlamak için en temel adımlardan biri, uygulama dosyalarının (APK ve IPA) root veya jailbreak işlemleriyle açılmasını önlemektir. İster Android (React Native, Kotlin) kullanıyor olun, ister iOS (React Native, Swift) platformunda geliştirme yapıyor olun, bu konuda alınabilecek önlemler benzerdir.

Root veya Jailbreak Tespitinin Yapılması

Bu işlemleri engellemenin ilk adımı, uygulama içinde root veya jailbreak işlemlerinin tespit edilmesidir. Bu tespiti yapmak için genellikle sistem çağrılarını kontrol eden kütüphaneler kullanılır. Örneğin; bir React Native uygulamasında bu tür bir kontrol için şu örneği kullanabilirsiniz:

import DeviceInfo from 'react-native-device-info';

// Cihaz root veya jailbreak işlemi ile mi açılmış kontrolü
const isRootedOrJailbroken = async () => {
const isRooted = await DeviceInfo.isRooted();
return isRooted;
};

// Kontrolü kullanma örneği
if (await isRootedOrJailbroken()) {
console.log('Uygulama root veya jailbreak işlemi ile açılmış.');
} else {
console.log('Uygulama güvenli.');
}

React Native İçin Özel Güvenlik Adımları

React Native kullanırken uygulama güvenliğini artırmak için şu önlemleri alabilirsiniz:

1 — Proguard Konfigürasyonu: Android için kullanılan Proguard, uygulama kodlarını karıştırarak ve optimize ederek tersine mühendislik girişimlerini zorlaştırabilir.

# Proguard kuralları

# Sınıf ve yöntem adlarını karıştırma
-optimizationpasses 5
-keepattributes SourceFile,LineNumberTable
-renamesourcefileattribute SourceFile
-keepattributes Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,*Annotation*,EnclosingMethod

# Diğer Proguard kuralları...

2 — Code Obfuscation (Kod Karıştırma): iOS platformunda, Swift kodlarını karıştırarak okunabilirliği azaltabilir ve kodunuzun güvenliğini artırabilirsiniz.

class ObfuscatedClass {
// Obfuscated properties and methods
private var x: Int

init(x: Int) {
self.x = x
}

func obfuscatedMethod() -> Int {
return x * 2
}
}

// Kullanım örneği
let obfuscatedInstance = ObfuscatedClass(x: 5)
let result = obfuscatedInstance.obfuscatedMethod()
print(result)

3 — Gelişmiş Şifreleme Yöntemleri: Hassas verileri güvende tutmak için gelişmiş şifreleme yöntemlerini uygulayın. Bu, özellikle kullanıcı giriş bilgileri gibi hassas verileri içeren uygulamalar için önemlidir.

import CryptoJS from 'react-native-crypto';

const encryptUserData = (username, password) => {
// Anahtar oluştur
const key = CryptoJS.lib.WordArray.random(128 / 8); // 128-bit anahtar

// Kullanıcı adını şifrele
const encryptedUsername = CryptoJS.AES.encrypt(username, key).toString();

// Şifreyi şifrele
const encryptedPassword = CryptoJS.AES.encrypt(password, key).toString();

// Anahtarı base64'e çevir
const base64Key = CryptoJS.enc.Base64.stringify(key.words);

return {
encryptedUsername,
encryptedPassword,
base64Key,
};
};

// Kullanım örneği
const { encryptedUsername, encryptedPassword, base64Key } = encryptUserData(
'exampleUser',
'securePassword'
);

console.log('Encrypted Username:', encryptedUsername);
console.log('Encrypted Password:', encryptedPassword);
console.log('Base64 Key:', base64Key);

Tersine Mühendislik Nedir ve Nasıl Çalışır?

Tersine mühendislik, bir yazılımın çalışma prensiplerini ve iç yapısını anlamak amacıyla kullanılan bir tekniktir. Bu, genellikle bir uygulamanın derlenmiş halini veya paketlenmiş dosyalarını açarak içindeki kodu incelemeyi içerir. Tersine mühendislik, hem olumlu hem de olumsuz amaçlarla kullanılabilir.

Neden Tersine Mühendislik Yapılır?

1 — Uygulama Analizi için:

— — — Amaç: Bir uygulamanın içindeki işleyişi anlamak.

— — — Örnek Uygulama: Android APK veya iOS IPA dosyalarını inceleyerek uygulamanın içindeki kaynak kodları ve algoritmaları anlamak.

2 — Güvenlik İncelemeleri için:

— — — Amaç: Uygulama güvenliği açısından potansiyel zayıf noktaları tespit etmek.

— — — Örnek Uygulama: Bir uygulamanın güvenliğini değerlendirmek için uygulamanın iç yapısını inceleyerek zayıf noktaları tespit etmek.

Örnek: React Native ile Basit Bir Tersine Mühendislik Senaryosu

1 — APKTool’un İndirilmesi: APKTool’un resmi web sitesinden APKTool’u indirin ve bilgisayarınıza kurun.

2 — React Native APK’sinin Kopyalanması: React Native ile geliştirdiğiniz uygulamanın APK dosyasını bulun. Bu genellikle “android/app/build/outputs/apk/release/app-release.apk” şeklinde bir dizinde bulunabilir.

APK Dosyasını Çıkartma: Aşağıdaki komutu kullanarak APK dosyasını çıkartın

apktool d -o çıkartma_dizini uygulama-release.apk

Çıkartılan Dizin İnceleme

Çıkartma işlemi sonrasında oluşan dizine gidin. (örneğin; çıkartma_dizini)

  • React Native projenizin ana kodları genellikle assets veya src klasörlerinde yer alır.
  • index.android.js, App.js gibi dosyaları inceleyerek uygulamanın ana mantığını anlayabilirsiniz.

Yetkisiz İsteklerde Bulunulabilmesi ve Güvenlik Önlemleri

Yetkisiz istekler, uygulamanın beklenmeyen şekilde dış dünyayla iletişim kurmasına neden olabilir. Bu durum, kötü niyetli saldırılara ve kullanıcı verilerinin tehlikeye atılmasına yol açabilir.

Güvenlik Önlemleri:

SSL Kullanımı: İletişimi şifreleyerek verilerin güvenli bir şekilde transferini sağlar.

fetch('https://api.example.com/data', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
// Diğer header bilgileri...
},
// Diğer fetch seçenekleri...
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Hata:', error));

Token Bazlı Kimlik Doğrulama: Her isteği, kimlik doğrulamasına tabi tutarak yetkisiz erişimleri engeller.

Uygulama içi kullanıcı değişiminin önlenmesi için oturum yönetimi ve token güvenliği önemlidir. Kullanıcının oturum durumu ve kimlik bilgileri güvenli bir şekilde saklanmalı ve kontrol edilmelidir.

// Örnek bir kimlik doğrulama token'ı
const authToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';

fetch('https://api.example.com/data', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${authToken}`,
// Diğer header bilgileri...
},
// Diğer fetch seçenekleri...
})
.then(response => response.json())
.then(data => console.log(data))
.catch(error => console.error('Hata:', error));

Daha düzenli ve genel token kullanımı ve “react-native-sensitive-info” ile güvenli depolama sağlıyoruz. Bu kütüphaneden daha detaylı bahsediyor olacağım.

import SInfo from 'react-native-sensitive-info';

const checkSession = async () => {
try {
const userToken = await SInfo.getItem('userToken', {});

if (userToken) {
// Oturum açık, kullanıcı izin verilen işlemleri yapabilir.
return true;
} else {
// Oturum kapalı, kullanıcı oturum açma sayfasına yönlendirilebilir.
return false;
}
} catch (error) {
console.error('Hata:', error);
return false;
}
};

Güvenli Depolama Kullanımı: Kullanıcı bilgilerini güvenli bir şekilde depolayarak yetkisiz erişimleri engeller.

Mobil uygulamalarda kullanıcı bilgilerini güvenli bir şekilde depolamak, uygulamanın güvenliği açısından kritik bir öneme sahiptir. Bu yazıda, react-native-sensitive-info ve @react-native-async-storage/async-storage kütüphanelerini karşılaştırarak güvenli depolama için hangi kütüphanenin tercih edilebileceğini inceleyeceğiz.

  1. react-native-sensitive-info Kütüphanesi:

Avantajları:

  • Şifreleme ve Güvenlik: Kütüphane, kullanıcı bilgilerini şifreleyerek depolama imkanı sağlar.
  • Android ve iOS Desteği: Hem Android hem de iOS platformlarında kullanılabilmektedir.
  • Anahtar Yönetimi: Kütüphane, anahtar yönetimi konusunda kullanıcıya daha fazla kontrol sağlar.

Dezavantajları:

  • Belirli Senaryolara Odaklı: Daha çok kimlik bilgileri gibi hassas verilerin depolanması için odaklanmıştır.

2. @react-native-async-storage/async-storage Kütüphanesi:

Avantajları:

  • Basit Kullanım: AsyncStorage basit bir API yapısına sahiptir, bu nedenle kullanımı kolaydır.
  • Genel Depolama: Tüm veri türlerini depolamak için genel bir mekanizma sunar.
  • Topluluk Desteği: React Native topluluğunda yaygın olarak kullanıldığı için daha büyük bir destek ve belgeler kümesi vardır.

Dezavantajları:

  • Şifreleme Zayıf: AsyncStorage, verileri düz metin olarak depolar. Daha fazla güvenlik gerektiren durumlar için yetersiz olabilir.

Sonuç ve Tavsiye:

Eğer uygulamanızda hassas verilerin depolanması gerekiyorsa ve daha fazla güvenlik önlemi almak istiyorsanız, react-native-sensitive-info kütüphanesi tercih edilebilir. Ancak genel kullanım senaryoları için veya basit depolama ihtiyaçları için @react-native-async-storage/async-storage oldukça uygundur.

react-native-sensitive-info Örneği:

import SInfo from 'react-native-sensitive-info';

// Güvenli bilgileri depolama
const saveSecureData = async () => {
try {
await SInfo.setItem('username', 'exampleUser', {});
await SInfo.setItem('password', 'securePassword', {});
console.log('Kullanıcı bilgileri güvenli şekilde kaydedildi.');
} catch (error) {
console.error('Hata:', error);
}
};

// Güvenli bilgileri getirme
const getSecureData = async () => {
try {
const username = await SInfo.getItem('username', {});
const password = await SInfo.getItem('password', {});
console.log('Kullanıcı Bilgileri:', { username, password });
} catch (error) {
console.error('Hata:', error);
}
};

// Örnek kullanım
saveSecureData();
getSecureData();

@react-native-async-storage/async-storage Örneği:

import AsyncStorage from '@react-native-async-storage/async-storage';

// Güvensiz bilgileri depolama
const saveInsecureData = async () => {
try {
await AsyncStorage.setItem('username', 'exampleUser');
await AsyncStorage.setItem('password', 'insecurePassword');
console.log('Kullanıcı bilgileri güvensiz şekilde kaydedildi.');
} catch (error) {
console.error('Hata:', error);
}
};

// Güvensiz bilgileri getirme
const getInsecureData = async () => {
try {
const username = await AsyncStorage.getItem('username');
const password = await AsyncStorage.getItem('password');
console.log('Kullanıcı Bilgileri:', { username, password });
} catch (error) {
console.error('Hata:', error);
}
};

// Örnek kullanım
saveInsecureData();
getInsecureData();

Buraya kadar sabırla okuduğunuz için teşekkür ederim.

Farklı konularda yeniden buluşana kadar hatasız kodlamalar. :)

Ford Otosan — Dijital Ürünler ve Servisler
Plan 2 Delivery Tribe Liderliği — Software Development Team Member
Yusuf PAMUKÇU

--

--