OWASP 類別:MASVS-CODE:程式碼品質
總覽
將程式碼動態載入應用程式會帶來風險,因此必須降低風險。攻擊者可能會竄改或替換程式碼,藉此存取機密資料或執行有害動作。
許多形式的動態程式碼載入 (尤其是使用遠端來源的載入方式) 違反 Google Play 政策,可能導致應用程式遭到 Google Play 停權。
影響
如果攻擊者設法存取將載入應用程式的程式碼,就能修改程式碼來達成目標,進而導致資料竊取和程式碼執行漏洞。即使攻擊者無法修改程式碼來執行任意動作,他們仍有可能損毀或移除程式碼,進而影響應用程式的可用性。
因應措施
避免使用動態程式碼載入
除非有業務需求,否則請避免動態載入程式碼。建議盡可能將所有功能直接納入應用程式。
使用信任的來源
載入應用程式的程式碼應儲存在信任的位置。至於本機儲存空間,建議使用應用程式內部儲存空間或限定範圍儲存空間 (適用於 Android 10 以上版本)。這些位置設有防護措施,可避免其他應用程式和使用者直接存取。
從網址等遠端位置載入程式碼時,請盡可能避免使用第三方,並遵循安全最佳做法,將程式碼儲存在自己的基礎架構中。如需載入第三方程式碼,請務必確認供應商值得信賴。
執行完整性檢查
建議進行完整性檢查,確保程式碼未遭竄改。將程式碼載入應用程式前,請先執行這些檢查。
載入遠端資源時,可以使用子資源完整性驗證所存取資源的完整性。
從外部儲存空間載入資源時,請使用完整性檢查,確認沒有其他應用程式竄改這項資料或程式碼。檔案的雜湊值應以安全方式儲存,最好是加密並儲存在內部儲存空間。
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!");
}
}
}
簽署程式碼
確保資料完整性的另一種做法是簽署程式碼,並在載入前驗證簽章。這個方法不僅能確保程式碼的完整性,還能確保雜湊碼的完整性,因此具有額外優勢,可進一步防範竄改行為。
雖然程式碼簽署可提供額外的安全層,但請務必考量到,這項程序較為複雜,可能需要投入額外心力和資源才能順利實作。
如需程式碼簽署範例,請參閱本文的「資源」一節。