OWASP kategorisi: MASVS-CODE: Kod Kalitesi
Genel Bakış
Bir uygulamaya dinamik olarak kod yüklemek, azaltılması gereken bir risk düzeyi oluşturur. Saldırganlar, hassas verilere erişmek veya zararlı işlemler gerçekleştirmek için kodu kurcalayabilir ya da değiştirebilir.
Özellikle uzak kaynakları kullanan birçok dinamik kod yükleme biçimi Google Play politikalarını ihlal eder ve uygulamanızın Google Play'den askıya alınmasına neden olabilir.
Etki
Saldırganlar uygulamaya yüklenecek koda erişmeyi başarırsa kodu kendi amaçlarına uygun şekilde değiştirebilir. Bu durum, veri hırsızlığı ve kod yürütme güvenlik açıklarına yol açabilir. Saldırganlar, kodu istedikleri gibi değiştiremeseler bile kodu bozabilir veya kaldırabilir ve böylece uygulamanın kullanılabilirliğini etkileyebilirler.
Çözümler
Dinamik kod yükleme kullanmaktan kaçının
İşletmeyle ilgili bir ihtiyaç olmadığı sürece dinamik kod yüklemeyi kullanmayın. Mümkün olduğunda tüm işlevleri doğrudan uygulamaya eklemeyi tercih etmelisiniz.
Güvenilir kaynakları kullanma
Uygulamaya yüklenecek kod, güvenilir konumlarda depolanmalıdır. Yerel depolama alanıyla ilgili olarak, uygulama dahili depolama alanı veya kapsamlı depolama alanı (Android 10 ve sonraki sürümler için) önerilen yerlerdir. Bu konumlar, diğer uygulamalardan ve kullanıcılardan doğrudan erişimi önlemek için önlemler içerir.
URL'ler gibi uzak konumlardan kod yüklerken mümkün olduğunda üçüncü tarafları kullanmaktan kaçının ve kodu güvenlikle ilgili en iyi uygulamalara uyarak kendi altyapınızda saklayın. Üçüncü taraf kodu yüklemeniz gerekiyorsa sağlayıcının güvenilir olduğundan emin olun.
Bütünlük kontrolleri gerçekleştirme
Kodun değiştirilmediğinden emin olmak için bütünlük kontrolleri yapılması önerilir. Bu kontroller, koda uygulamaya yüklenmeden önce yapılmalıdır.
Uzak kaynaklar yüklenirken, erişilen kaynakların bütünlüğünü doğrulamak için alt kaynak bütünlüğü kullanılabilir.
Harici depolama alanından kaynak yüklerken, başka bir uygulamanın bu verilerle veya kodla oynamadığını doğrulamak için bütünlük kontrollerini kullanın. Dosyaların karma değerleri güvenli bir şekilde, tercihen şifrelenmiş olarak ve dahili depolama alanında saklanmalıdır.
Kotlin
package com.example.myapplication
import java.io.BufferedInputStream
import java.io.FileInputStream
import java.io.IOException
import java.security.MessageDigest
import java.security.NoSuchAlgorithmException
object FileIntegrityChecker {
@Throws(IOException::class, NoSuchAlgorithmException::class)
fun getIntegrityHash(filePath: String?): String {
val md = MessageDigest.getInstance("SHA-256") // You can choose other algorithms as needed
val buffer = ByteArray(8192)
var bytesRead: Int
BufferedInputStream(FileInputStream(filePath)).use { fis ->
while (fis.read(buffer).also { bytesRead = it } != -1) {
md.update(buffer, 0, bytesRead)
}
}
private fun bytesToHex(bytes: ByteArray): String {
val sb = StringBuilder(bytes.length * 2)
for (b in bytes) {
sb.append(String.format("%02x", b))
}
return sb.toString()
}
@Throws(IOException::class, NoSuchAlgorithmException::class)
fun verifyIntegrity(filePath: String?, expectedHash: String): Boolean {
val actualHash = getIntegrityHash(filePath)
return actualHash == expectedHash
}
@Throws(Exception::class)
@JvmStatic
fun main(args: Array<String>) {
val filePath = "/path/to/your/file"
val expectedHash = "your_expected_hash_value"
if (verifyIntegrity(filePath, expectedHash)) {
println("File integrity is valid!")
} else {
println("File integrity is compromised!")
}
}
}
Java
package com.example.myapplication;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
public class FileIntegrityChecker {
public static String getIntegrityHash(String filePath) throws IOException, NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("SHA-256"); // You can choose other algorithms as needed
byte[] buffer = new byte[8192];
int bytesRead;
try (BufferedInputStream fis = new BufferedInputStream(new FileInputStream(filePath))) {
while ((bytesRead = fis.read(buffer)) != -1) {
md.update(buffer, 0, bytesRead);
}
}
byte[] digest = md.digest();
return bytesToHex(digest);
}
private static String bytesToHex(byte[] bytes) {
StringBuilder sb = new StringBuilder(bytes.length * 2);
for (byte b : bytes) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
public static boolean verifyIntegrity(String filePath, String expectedHash) throws IOException, NoSuchAlgorithmException {
String actualHash = getIntegrityHash(filePath);
return actualHash.equals(expectedHash);
}
public static void main(String[] args) throws Exception {
String filePath = "/path/to/your/file";
String expectedHash = "your_expected_hash_value";
if (verifyIntegrity(filePath, expectedHash)) {
System.out.println("File integrity is valid!");
} else {
System.out.println("File integrity is compromised!");
}
}
}
Kodu imzalama
Verilerin bütünlüğünü sağlamanın bir diğer yolu da kodu imzalamak ve yüklemeden önce imzasını doğrulamaktır. Bu yöntem, yalnızca kodun kendisinin değil, karma kodunun da bütünlüğünü sağlaması açısından avantajlıdır. Bu sayede, ek bir kurcalamaya karşı koruma sağlanır.
Kod imzalama ek güvenlik katmanları sağlasa da başarılı bir şekilde uygulanması için ek çaba ve kaynak gerektirebilecek daha karmaşık bir süreç olduğunu göz önünde bulundurmak önemlidir.
Kod imzalama örneklerini bu belgenin Kaynaklar bölümünde bulabilirsiniz.
Kaynaklar
- Alt Kaynak Bütünlüğü (Subresource Integrity)
- Verileri Dijital Olarak İmzalama
- Kod İmzalama
- Harici Depolama Alanında Saklanan Hassas Veriler