OWASP-Kategorie: MASVS-CODE: Codequalität
Übersicht
Das dynamische Laden von Code in eine Anwendung erhöht das Risiko, das minimiert werden muss. Angreifer könnten den Code manipulieren oder ersetzen, um auf vertrauliche Daten zuzugreifen oder schädliche Aktionen auszuführen.
Viele Formen des dynamischen Code-Ladens, insbesondere solche, die Remote-Quellen verwenden, verstoßen gegen die Google Play-Richtlinien und können dazu führen, dass Ihre App von Google Play gesperrt wird.
Positiv beeinflussen
Wenn Angreifer Zugriff auf den Code erhalten, der in die Anwendung geladen wird, können sie ihn so ändern, dass er ihren Zielen dient. Dies kann zu Datenexfiltration und Codeausführungs-Exploits führen. Selbst wenn Angreifer den Code nicht ändern können, um beliebige Aktionen auszuführen, ist es möglich, dass sie den Code beschädigen oder entfernen und so die Verfügbarkeit der Anwendung beeinträchtigen.
Abhilfemaßnahmen
Vermeiden Sie das dynamische Laden von Code.
Vermeiden Sie das Laden dynamischen Codes, es sei denn, dies ist geschäftlich erforderlich. Sie sollten nach Möglichkeit alle Funktionen direkt in die Anwendung einbinden.
Vertrauenswürdige Quellen verwenden
Code, der in die Anwendung geladen wird, sollte an vertrauenswürdigen Speicherorten gespeichert werden. Für den lokalen Speicher werden der interne Speicher der App oder der Speicher mit Bereichsbeschränkung (für Android 10 und höher) empfohlen. An diesen Speicherorten sind Maßnahmen ergriffen, um den direkten Zugriff durch andere Anwendungen und Nutzer zu verhindern.
Verwenden Sie beim Laden von Code von Remote-Standorten wie URLs nach Möglichkeit keine Drittanbieter und speichern Sie den Code in Ihrer eigenen Infrastruktur gemäß den Best Practices für Sicherheit. Wenn Sie Code von Drittanbietern laden müssen, achten Sie darauf, dass der Anbieter vertrauenswürdig ist.
Integritätsprüfungen durchführen
Integritätsprüfungen werden empfohlen, um sicherzustellen, dass der Code nicht manipuliert wurde. Diese Prüfungen sollten durchgeführt werden, bevor Code in die Anwendung geladen wird.
Beim Laden von Remote-Ressourcen kann die Integrität von Unterressourcen verwendet werden, um die Integrität der zugegriffenen Ressourcen zu überprüfen.
Führen Sie beim Laden von Ressourcen aus dem externen Speicher Integritätsprüfungen durch, um sicherzustellen, dass keine andere Anwendung diese Daten oder diesen Code manipuliert hat. Die Hashes der Dateien sollten sicher gespeichert werden, vorzugsweise verschlüsselt und im internen Speicher.
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!");
}
}
}
Code signieren
Eine weitere Möglichkeit, die Integrität der Daten zu gewährleisten, besteht darin, den Code zu signieren und seine Signatur vor dem Laden zu überprüfen. Diese Methode hat den Vorteil, dass nicht nur der Code selbst, sondern auch der Hash-Code für Integrität sorgt. Das bietet zusätzlichen Schutz vor Manipulation.
Die Codesignatur bietet zwar zusätzliche Sicherheitsebenen, aber es ist wichtig zu beachten, dass es sich um einen komplexeren Prozess handelt, der möglicherweise zusätzlichen Aufwand und Ressourcen erfordert, um erfolgreich implementiert zu werden.
Einige Beispiele für die Codesignatur finden Sie im Abschnitt „Ressourcen“ dieses Dokuments.
Ressourcen
- Integrität von Unterressourcen
- Daten digital signieren
- Code Signing
- Im externen Speicher gespeicherte sensible Daten