動態程式碼載入

OWASP 類別:MASVS-CODE:程式碼品質

總覽

動態將程式碼載入應用程式會帶來風險,必須採取措施降低風險。攻擊者可能會竄改或替換程式碼,以便存取機密資料或執行有害動作。

許多形式的動態程式碼載入作業 (尤其是使用遠端來源的作業) 都違反 Google Play 政策,可能導致應用程式從 Google Play 停權。

影響

如果攻擊者成功取得將載入應用程式的程式碼,就能修改程式碼以達成目標,進而導致資料外洩和程式碼執行漏洞。即使攻擊者無法修改程式碼來執行任意動作,仍有可能損毀或移除程式碼,進而影響應用程式的可用性。

因應措施

避免使用動態程式碼載入

除非有業務需求,否則請避免動態程式碼載入。建議您盡可能將所有功能直接納入應用程式。

使用可信賴的來源

要載入至應用程式的程式碼應儲存在可信任的位置。就本機儲存空間而言,建議您使用應用程式內部儲存空間或限定範圍儲存空間 (適用於 Android 10 以上版本)。這些位置已採取措施,避免其他應用程式和使用者直接存取。

從遠端位置 (例如網址) 載入程式碼時,請盡量避免使用第三方,並按照安全性最佳做法將程式碼儲存在您自己的基礎架構中。如果您需要載入第三方程式碼,請確認供應商是可信任的供應商。

執行完整性檢查

建議您進行完整性檢查,確保程式碼未遭竄改。應在將程式碼載入應用程式前執行這些檢查。

載入遠端資源時,可以使用子資源完整性來驗證所存取資源的完整性。

從外部儲存空間載入資源時,請使用完整性檢查來確認沒有其他應用程式竄改這項資料或程式碼。檔案的雜湊應以安全的方式儲存,最好是加密並儲存在內部儲存空間。

KotlinJava
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!")
       
}
   
}
}
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!");
       
}
   
}
}

簽署程式碼

另一種確保資料完整性的做法,是在載入程式碼前簽署程式碼並驗證其簽名。這個方法的好處在於,除了程式碼本身,也能確保雜湊碼的完整性,因此可提供額外的防竄改保護機制。

雖然程式碼簽署可提供額外的安全層,但請務必考量到這項程序較為複雜,可能需要額外的努力和資源才能成功實作。

您可以在本文的「資源」部分找到一些程式碼簽署範例。

資源