Kategori OWASP: MASVS-CODE: Kualitas Kode
Ringkasan
Memuat kode secara dinamis ke dalam aplikasi akan menimbulkan tingkat risiko yang harus diminimalkan. Penyerang berpotensi mengutak-atik atau mengganti kode untuk mengakses data sensitif atau mengeksekusi tindakan berbahaya.
Banyak bentuk pemuatan kode dinamis, terutama yang menggunakan sumber jarak jauh, melanggar kebijakan Google Play dan dapat menyebabkan penangguhan aplikasi Anda dari Google Play.
Dampak
Jika penyerang berhasil mendapatkan akses ke kode yang akan dimuat ke dalam aplikasi, mereka dapat mengubahnya untuk mendukung sasaran mereka. Hal ini dapat menyebabkan eksfiltrasi data dan eksploitasi eksekusi kode. Meskipun penyerang tidak dapat mengubah kode untuk melakukan tindakan arbitrer pilihan mereka, masih ada kemungkinan mereka dapat merusak atau menghapus kode sehingga memengaruhi ketersediaan aplikasi.
Mitigasi
Hindari penggunaan pemuatan kode dinamis
Kecuali jika ada kebutuhan bisnis, hindari pemuatan kode dinamis. Sebaiknya Anda menyertakan semua fungsi langsung ke dalam aplikasi, jika memungkinkan.
Menggunakan sumber tepercaya
Kode yang akan dimuat ke dalam aplikasi harus disimpan di lokasi tepercaya. Sehubungan dengan penyimpanan lokal, penyimpanan internal aplikasi atau penyimpanan terbatas (untuk Android 10 dan yang lebih baru) adalah tempat yang direkomendasikan. Lokasi ini memiliki tindakan untuk menghindari akses langsung dari aplikasi dan pengguna lain.
Saat memuat kode dari lokasi jarak jauh seperti URL, hindari penggunaan pihak ketiga jika memungkinkan, dan simpan kode di infrastruktur Anda sendiri, dengan mengikuti praktik terbaik keamanan. Jika Anda perlu memuat kode pihak ketiga, pastikan penyedianya tepercaya.
Melakukan pemeriksaan integritas
Pemeriksaan integritas direkomendasikan untuk memastikan bahwa kode tidak telah dimodifikasi. Pemeriksaan ini harus dilakukan sebelum memuat kode ke dalam aplikasi.
Saat memuat resource jarak jauh, integritas subresource dapat digunakan untuk memvalidasi integritas resource yang diakses.
Saat memuat resource dari penyimpanan eksternal, gunakan pemeriksaan integritas untuk memverifikasi bahwa tidak ada aplikasi lain yang telah memodifikasi data atau kode ini. Hash file harus disimpan dengan aman, sebaiknya dienkripsi dan di penyimpanan internal.
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!");
}
}
}
Menandatangani kode
Opsi lain untuk memastikan integritas data adalah menandatangani kode dan memverifikasi tanda tangannya sebelum memuat kode. Metode ini memiliki keunggulan karena juga memastikan integritas kode hash, bukan hanya kode itu sendiri, yang memberikan perlindungan anti-perusakan tambahan.
Meskipun penandatanganan kode memberikan lapisan keamanan tambahan, penting untuk mempertimbangkan bahwa ini adalah proses yang lebih kompleks yang mungkin memerlukan upaya dan resource tambahan agar berhasil diterapkan.
Beberapa contoh penandatanganan kode dapat ditemukan di bagian Referensi dalam dokumen ini.
Referensi
- Integritas Subresource
- Menandatangani Data Secara Digital
- Penandaan Kode
- Data Sensitif yang Disimpan di Penyimpanan Eksternal