動的コード読み込み
コレクションでコンテンツを整理
必要に応じて、コンテンツの保存と分類を行います。
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!")
}
}
}
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!");
}
}
}
コードに署名する
データの整合性を確保する別の方法として、コードに署名し、読み込む前に署名を検証することもできます。この方法には、コード自体だけでなく、ハッシュコードの整合性も確保できるという利点があります。これにより、改ざん防止が強化されます。
コード署名は追加のセキュリティ レイヤを提供しますが、より複雑なプロセスであり、実装に追加の労力とリソースが必要になる可能性があることを考慮することが重要です。
コード署名の例については、このドキュメントのリソース セクションをご覧ください。
リソース
このページのコンテンツやコードサンプルは、コンテンツ ライセンスに記載のライセンスに従います。Java および OpenJDK は 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)"]]