Категория OWASP: MASVS-CODE: Качество кода
Обзор
Динамическая загрузка кода в приложение сопряжена с определенным уровнем риска, который необходимо минимизировать. Злоумышленники потенциально могут внести изменения в код или заменить его, чтобы получить доступ к конфиденциальным данным или выполнить вредоносные действия.
Многие формы динамической загрузки кода, особенно те, которые используют удаленные источники, нарушают правила Google Play и могут привести к блокировке вашего приложения в Google Play.
Влияние
Если злоумышленникам удастся получить доступ к коду, который будет загружен в приложение, они смогут модифицировать его для достижения своих целей. Это может привести к утечке данных и выполнению уязвимостей в коде. Даже если злоумышленники не смогут модифицировать код для выполнения произвольных действий по своему выбору, всё равно существует вероятность того, что они смогут повредить или удалить код и тем самым повлиять на доступность приложения.
Меры по смягчению последствий
Избегайте использования динамической загрузки кода.
Если в этом нет необходимости с точки зрения бизнеса, избегайте динамической загрузки кода. По возможности, отдавайте предпочтение включению всех функций непосредственно в приложение.
Используйте проверенные источники.
Код, который будет загружен в приложение, следует хранить в доверенных местах. Что касается локального хранилища, рекомендуемыми местами являются внутренняя память приложения или ограниченное хранилище (для Android 10 и более поздних версий). В этих местах предусмотрены меры защиты от прямого доступа со стороны других приложений и пользователей.
При загрузке кода из удалённых источников, таких как URL-адреса, по возможности избегайте использования сторонних сервисов и храните код в собственной инфраструктуре, соблюдая лучшие практики безопасности. Если вам необходимо загрузить сторонний код, убедитесь, что поставщик является надёжным.
Проведите проверки целостности.
Рекомендуется проводить проверки целостности, чтобы убедиться в отсутствии изменений в коде. Эти проверки следует выполнять перед загрузкой кода в приложение.
При загрузке удалённых ресурсов для проверки целостности обращаемых ресурсов может использоваться проверка целостности подресурсов.
При загрузке ресурсов с внешнего хранилища используйте проверки целостности, чтобы убедиться, что никакое другое приложение не внесло изменения в эти данные или код. Хэши файлов следует хранить безопасным способом, предпочтительно в зашифрованном виде, во внутренней памяти устройства.
Котлин
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!");
}
}
}
Подпишите код
Еще один способ обеспечить целостность данных — подписать код и проверить его подпись перед загрузкой. Преимущество этого метода заключается в том, что он гарантирует целостность не только самого кода, но и хеш-кода, что обеспечивает дополнительную защиту от несанкционированного изменения.
Хотя цифровая подпись кода обеспечивает дополнительные уровни безопасности, важно учитывать, что это более сложный процесс, который может потребовать дополнительных усилий и ресурсов для успешной реализации.
Примеры подписывания кода можно найти в разделе «Ресурсы» этого документа.
Ресурсы
- Целостность подземных ресурсов
- Подпись данных в цифровом виде
- Подписание кода
- Конфиденциальные данные, хранящиеся во внешнем хранилище.