OWASP-Kategorie:MASVS-CODE: Code Quality
Übersicht
Das dynamische Laden von Code in eine Anwendung birgt ein gewisses 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 Ladens von Code, insbesondere solche, bei denen Remote-Quellen verwendet werden, verstoßen gegen die Google Play-Richtlinien und können dazu führen, dass Ihre App aus Google Play entfernt wird.
Auswirkungen
Wenn Angreifer Zugriff auf den Code erhalten, der in die Anwendung geladen wird, können sie ihn so ändern, dass er ihren Zielen entspricht. Dies kann zu Daten-Exfiltration und Codeausführung führen. Auch wenn Angreifer den Code nicht ändern können, um beliebige Aktionen auszuführen, ist es dennoch möglich, dass sie den Code beschädigen oder entfernen und so die Verfügbarkeit der Anwendung beeinträchtigen.
Gegenmaßnahmen
Dynamisches Laden von Code vermeiden
Vermeiden Sie dynamisches Laden von Code, sofern dies nicht geschäftlich erforderlich ist. 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 Orten gespeichert werden. Für den lokalen Speicher werden der interne Speicher der Anwendung oder der bereichsbezogene Speicher (für Android 10 und höher) empfohlen. An diesen Speicherorten werden Maßnahmen ergriffen, um direkten Zugriff durch andere Anwendungen und Nutzer zu verhindern.
Vermeiden Sie beim Laden von Code von Remote-Standorten wie URLs nach Möglichkeit die Verwendung von Drittanbietern und speichern Sie den Code in Ihrer eigenen Infrastruktur. Beachten Sie dabei die Best Practices für die Sicherheit. Wenn Sie Drittanbietercode 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 Subresource Integrity verwendet werden, um die Integrität der aufgerufenen Ressourcen zu validieren.
Wenn Sie Ressourcen aus dem externen Speicher laden, verwenden Sie Integritätsprüfungen, um zu prüfen, ob 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 sie nicht nur den Code selbst, sondern auch die Integrität des Hash-Codes sicherstellt, was einen zusätzlichen Manipulationsschutz bietet.
Die Codesignierung bietet zwar zusätzliche Sicherheitsebenen, ist aber ein komplexerer Prozess, der zusätzlichen Aufwand und zusätzliche Ressourcen erfordern kann.
Einige Beispiele für die Codesignierung finden Sie im Abschnitt „Ressourcen“ dieses Dokuments.
Ressourcen
- Subresource Integrity
- Daten digital signieren
- Code-Signing
- In externem Speicher gespeicherte vertrauliche Daten