동적 코드 로드
컬렉션을 사용해 정리하기
내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요.
OWASP 카테고리: MASVS-CODE: 코드 품질
개요
애플리케이션에 코드를 동적으로 로드하면 완화해야 하는 위험 수준이 발생합니다. 공격자는 코드를 조작하거나 대체하여 민감한 정보에 액세스하거나 유해한 작업을 실행할 수 있습니다.
많은 형식의 동적 코드 로드, 특히 원격 소스를 사용하는 코드는 Google Play 정책을 위반하여 Google Play에서 앱이 정지될 수 있습니다.
영향
공격자가 애플리케이션에 로드될 코드에 대한 액세스 권한을 얻는 경우 목표를 지원하도록 이를 수정할 수 있습니다. 이로 인해 데이터 무단 반출 및 코드 실행 악용이 발생할 수 있습니다. 공격자가 코드를 수정하여 자신이 선택한 임의의 작업을 실행할 수 없더라도 코드를 손상시키거나 삭제하여 애플리케이션의 가용성에 영향을 미칠 수 있습니다.
완화 조치
동적 코드 로드 사용하지 않기
비즈니스 요구사항이 없는 한 동적 코드 로드를 피하세요. 가능하면 모든 기능을 애플리케이션에 직접 포함하는 것이 좋습니다.
신뢰할 수 있는 소스 사용
애플리케이션에 로드될 코드는 신뢰할 수 있는 위치에 저장해야 합니다. 로컬 저장소와 관련해서는 애플리케이션 내부 저장소나 범위 지정 저장소 (Android 10 이상)를 추천합니다. 이러한 위치에는 다른 애플리케이션 및 사용자의 직접 액세스를 방지하기 위한 조치가 있습니다.
URL과 같은 원격 위치에서 코드를 로드할 때는 가능하면 서드 파티를 사용하지 말고 보안 권장사항에 따라 코드를 자체 인프라에 저장하세요. 서드 파티 코드를 로드해야 하는 경우 제공업체가 신뢰할 수 있는지 확인합니다.
코드가 조작되지 않았는지 확인하려면 무결성 검사를 실행하는 것이 좋습니다. 이러한 검사는 코드를 애플리케이션에 로드하기 전에 실행해야 합니다.
원격 리소스를 로드할 때 하위 리소스 무결성을 사용하여 액세스된 리소스의 무결성을 검사할 수 있습니다.
외부 저장소에서 리소스를 로드할 때 무결성 검사를 사용하여 다른 애플리케이션이 이 데이터 또는 코드를 조작하지 않았는지 확인합니다. 파일의 해시는 안전한 방식으로 저장해야 하며, 가능하면 암호화하여 내부 저장소에 저장해야 합니다.
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!")
}
}
}
자바
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!");
}
}
}
코드 서명
데이터 무결성을 보장하는 또 다른 방법은 코드에 서명하고 코드를 로드하기 전에 서명을 확인하는 것입니다. 이 방법은 코드 자체뿐만 아니라 해시 코드의 무결성도 보장하므로 추가적인 조작 방지 보호 기능을 제공합니다.
코드 서명은 추가 보안 레이어를 제공하지만, 성공적으로 구현하려면 추가 노력과 리소스가 필요할 수 있는 더 복잡한 프로세스라는 점을 고려해야 합니다.
코드 서명의 몇 가지 예는 이 문서의 리소스 섹션에서 확인할 수 있습니다.
리소스
이 페이지에 나와 있는 콘텐츠와 코드 샘플에는 콘텐츠 라이선스에서 설명하는 라이선스가 적용됩니다. 자바 및 OpenJDK는 Oracle 및 Oracle 계열사의 상표 또는 등록 상표입니다.
최종 업데이트: 2025-07-26(UTC)
[[["이해하기 쉬움","easyToUnderstand","thumb-up"],["문제가 해결됨","solvedMyProblem","thumb-up"],["기타","otherUp","thumb-up"]],[["필요한 정보가 없음","missingTheInformationINeed","thumb-down"],["너무 복잡함/단계 수가 너무 많음","tooComplicatedTooManySteps","thumb-down"],["오래됨","outOfDate","thumb-down"],["번역 문제","translationIssue","thumb-down"],["샘플/코드 문제","samplesCodeIssue","thumb-down"],["기타","otherDown","thumb-down"]],["최종 업데이트: 2025-07-26(UTC)"],[],[],null,["# Dynamic Code Loading\n\n\u003cbr /\u003e\n\n**OWASP category:** [MASVS-CODE: Code Quality](https://mas.owasp.org/MASVS/10-MASVS-CODE)\n\nOverview\n--------\n\nDynamically loading code into an application introduces a risk level that has to\nbe mitigated. Attackers could potentially tamper with or substitute the code to\naccess sensitive data or execute harmful actions.\n\nMany forms of dynamic code loading, especially those that use remote sources,\n[violate Google Play policies](https://support.google.com/googleplay/android-developer/answer/9888379) and may lead to a suspension of your app from\nGoogle Play.\n\nImpact\n------\n\nIf attackers manage to gain access to the code that will be loaded into the\napplication, they could modify it to support their goals. This could lead to\ndata exfiltration and code execution exploits. Even if attackers cannot modify\nthe code to perform arbitrary actions of their choice, it is still possible that\nthey can corrupt or remove the code and thus affect the availability of the\napplication.\n\nMitigations\n-----------\n\n### Avoid using dynamic code loading\n\nUnless there is a business need, avoid dynamic code loading. You should prefer\nto include all functionalities directly into the application, whenever possible.\n\n### Use trusted sources\n\nCode that will be loaded into the application should be stored in trusted\nlocations. Regarding local storage, the application internal storage or scoped\nstorage (for Android 10 and later) are the recommended places. These locations\nhave measures to avoid direct access from other applications and users.\n\nWhen loading code from remote locations such as URLs, avoid using third parties\nwhen possible, and store the code in your own infrastructure, following security\nbest practices. If you need to load third-party code, ensure that the provider\nis a trusted one.\n\n### Perform integrity checks\n\nIntegrity checks are recommended in order to ensure that the code has not been\ntampered with. These checks should be performed before loading code into the\napplication.\n\nWhen loading remote resources, subresource integrity can be used in order to\nvalidate the integrity of the accessed resources.\n\nWhen loading resources from the external storage, use integrity checks to verify\nthat no other application has tampered with this data or code. The hashes of the\nfiles should be stored in a secure manner, preferably encrypted and in the\ninternal storage. \n\n### Kotlin\n\n package com.example.myapplication\n\n import java.io.BufferedInputStream\n import java.io.FileInputStream\n import java.io.IOException\n import java.security.MessageDigest\n import java.security.NoSuchAlgorithmException\n\n object FileIntegrityChecker {\n @Throws(IOException::class, NoSuchAlgorithmException::class)\n fun getIntegrityHash(filePath: String?): String {\n val md = MessageDigest.getInstance(\"SHA-256\") // You can choose other algorithms as needed\n val buffer = ByteArray(8192)\n var bytesRead: Int\n BufferedInputStream(FileInputStream(filePath)).use { fis -\u003e\n while (fis.read(buffer).also { bytesRead = it } != -1) {\n md.update(buffer, 0, bytesRead)\n }\n\n }\n\n private fun bytesToHex(bytes: ByteArray): String {\n val sb = StringBuilder(bytes.length * 2)\n for (b in bytes) {\n sb.append(String.format(\"%02x\", b))\n }\n return sb.toString()\n }\n\n @Throws(IOException::class, NoSuchAlgorithmException::class)\n fun verifyIntegrity(filePath: String?, expectedHash: String): Boolean {\n val actualHash = getIntegrityHash(filePath)\n return actualHash == expectedHash\n }\n\n @Throws(Exception::class)\n @JvmStatic\n fun main(args: Array\u003cString\u003e) {\n val filePath = \"/path/to/your/file\"\n val expectedHash = \"your_expected_hash_value\"\n if (verifyIntegrity(filePath, expectedHash)) {\n println(\"File integrity is valid!\")\n } else {\n println(\"File integrity is compromised!\")\n }\n }\n }\n\n### Java\n\n package com.example.myapplication;\n\n import java.io.BufferedInputStream;\n import java.io.FileInputStream;\n import java.io.IOException;\n import java.security.MessageDigest;\n import java.security.NoSuchAlgorithmException;\n\n public class FileIntegrityChecker {\n\n public static String getIntegrityHash(String filePath) throws IOException, NoSuchAlgorithmException {\n MessageDigest md = MessageDigest.getInstance(\"SHA-256\"); // You can choose other algorithms as needed\n byte[] buffer = new byte[8192];\n int bytesRead;\n\n try (BufferedInputStream fis = new BufferedInputStream(new FileInputStream(filePath))) {\n while ((bytesRead = fis.read(buffer)) != -1) {\n md.update(buffer, 0, bytesRead);\n }\n }\n\n byte[] digest = md.digest();\n return bytesToHex(digest);\n }\n\n private static String bytesToHex(byte[] bytes) {\n StringBuilder sb = new StringBuilder(bytes.length * 2);\n for (byte b : bytes) {\n sb.append(String.format(\"%02x\", b));\n }\n return sb.toString();\n }\n\n public static boolean verifyIntegrity(String filePath, String expectedHash) throws IOException, NoSuchAlgorithmException {\n String actualHash = getIntegrityHash(filePath);\n return actualHash.equals(expectedHash);\n }\n\n public static void main(String[] args) throws Exception {\n String filePath = \"/path/to/your/file\";\n String expectedHash = \"your_expected_hash_value\";\n\n if (verifyIntegrity(filePath, expectedHash)) {\n System.out.println(\"File integrity is valid!\");\n } else {\n System.out.println(\"File integrity is compromised!\");\n }\n }\n }\n\n### Sign the code\n\nAnother option to ensure the integrity of the data is to sign the code and\nverify its signature before loading it. This method has the advantage of also\nensuring the integrity of the hash code, not only the code itself, which\nprovides an additional anti-tampering protection.\n\nAlthough code signing provides additional security layers, it is important to\ntake into account that it is a more complex process that may require additional\neffort and resources to be successfully implemented.\n\nSome examples of code signing can be found in the Resources section of this\ndocument.\n\nResources\n---------\n\n- [Subresource Integrity](https://en.wikipedia.org/wiki/Subresource_Integrity)\n- [Digitally Sign Data](https://developers.google.com/tink/digitally-sign-data#java)\n- [Code Signing](https://en.wikipedia.org/wiki/Code_signing)\n- [Sensitive Data Stored in External Storage](/privacy-and-security/risks/sensitive-data-external-storage)"]]