กำลังโหลดโค้ดแบบไดนามิก

หมวดหมู่ OWASP: MASVS-CODE: คุณภาพโค้ด

ภาพรวม

การโหลดโค้ดแบบไดนามิกลงในแอปพลิเคชันจะทำให้เกิดความเสี่ยงในระดับที่ต้องได้รับการบรรเทา ผู้โจมตีอาจดัดแปลงหรือแทนที่โค้ดเพื่อเข้าถึงข้อมูลที่ละเอียดอ่อนหรือดําเนินการที่เป็นอันตราย

การโหลดโค้ดแบบไดนามิกหลายรูปแบบ โดยเฉพาะการโหลดที่ใช้แหล่งที่มาจากระยะไกลละเมิดนโยบายของ Google Play และอาจส่งผลให้แอปของคุณถูกระงับไม่ให้แสดงใน Google Play

ผลกระทบ

หากผู้โจมตีสามารถเข้าถึงโค้ดที่จะโหลดลงในแอปพลิเคชันได้ ก็จะแก้ไขโค้ดเพื่อสนับสนุนเป้าหมายของตนได้ ซึ่งอาจนำไปสู่การลักลอบนำข้อมูลออกและการหาช่องโหว่เพื่อเรียกใช้โค้ด แม้ว่าผู้โจมตีจะแก้ไขโค้ดเพื่อดำเนินการตามต้องการไม่ได้ แต่ก็ยังอาจทำให้โค้ดเสียหายหรือนำโค้ดออกได้ ซึ่งจะส่งผลต่อความพร้อมใช้งานของแอปพลิเคชัน

การลดปัญหา

หลีกเลี่ยงการใช้การโหลดโค้ดแบบไดนามิก

หลีกเลี่ยงการโหลดโค้ดแบบไดนามิก เว้นแต่จะมีความจำเป็นทางธุรกิจ คุณควรรวมฟังก์ชันการทำงานทั้งหมดไว้ในแอปพลิเคชันโดยตรงทุกครั้งที่เป็นไปได้

ใช้แหล่งที่มาที่เชื่อถือได้

โค้ดที่จะโหลดลงในแอปพลิเคชันควรจัดเก็บไว้ในตำแหน่งที่เชื่อถือได้ สำหรับพื้นที่เก็บข้อมูลในเครื่อง เราขอแนะนำให้ใช้พื้นที่เก็บข้อมูลภายในของแอปพลิเคชันหรือพื้นที่เก็บข้อมูลแบบจำกัด (สำหรับ Android 10 ขึ้นไป) ตำแหน่งเหล่านี้มีมาตรการเพื่อหลีกเลี่ยงการเข้าถึงโดยตรงจากแอปพลิเคชันและผู้ใช้รายอื่น

เมื่อโหลดโค้ดจากตำแหน่งระยะไกล เช่น URL ให้หลีกเลี่ยงการใช้บุคคลที่สามเมื่อเป็นไปได้ และจัดเก็บโค้ดในโครงสร้างพื้นฐานของคุณเองตามแนวทางปฏิบัติแนะนำด้านความปลอดภัย หากต้องโหลดโค้ดของบุคคลที่สาม โปรดตรวจสอบว่าผู้ให้บริการดังกล่าวเชื่อถือได้

ดำเนินการตรวจสอบความสมบูรณ์

เราขอแนะนำให้ทำการตรวจสอบความสมบูรณ์เพื่อให้แน่ใจว่าไม่มีการเปลี่ยนแปลงรหัส ควรทำการตรวจสอบเหล่านี้ก่อนโหลดโค้ดลงในแอปพลิเคชัน

เมื่อโหลดทรัพยากรระยะไกล คุณสามารถใช้ความสมบูรณ์ของทรัพยากรย่อยเพื่อตรวจสอบความสมบูรณ์ของทรัพยากรที่เข้าถึงได้

เมื่อโหลดทรัพยากรจากพื้นที่เก็บข้อมูลภายนอก ให้ใช้การตรวจสอบความสมบูรณ์เพื่อยืนยันว่าไม่มีแอปพลิเคชันอื่นใดที่ดัดแปลงข้อมูลหรือโค้ดนี้ แฮชของไฟล์ควรจัดเก็บอย่างปลอดภัย โดยควรเข้ารหัสและจัดเก็บไว้ในพื้นที่เก็บข้อมูลภายใน

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

ลงชื่อในรหัส

อีกทางเลือกหนึ่งในการตรวจสอบความสมบูรณ์ของข้อมูลคือการเซ็นชื่อโค้ดและยืนยันลายเซ็นก่อนโหลด ข้อดีของวิธีนี้คือการตรวจสอบความสมบูรณ์ของรหัสแฮชด้วย ไม่ใช่แค่โค้ดเท่านั้น ซึ่งจะเพิ่มการป้องกันการแทรกแซง

แม้ว่าการรับรองโค้ดจะเพิ่มความปลอดภัยอีกชั้นหนึ่ง แต่สิ่งสำคัญคือต้องคำนึงถึงว่ากระบวนการนี้ซับซ้อนกว่ามากและอาจต้องใช้ความพยายามและทรัพยากรเพิ่มเติมจึงจะใช้งานได้

ดูตัวอย่างการรับรองโค้ดได้ในส่วนแหล่งข้อมูลของเอกสารนี้

แหล่งข้อมูล