モバイルアプリにおけるRoot Detection — 検出方法と動作原理

著者: IT Sectr 公開日: 2026-04-03 読了時間: 9 分

Root Detectionは、スーパーユーザー権限を持つデバイスでのAndroidアプリケーションの実行を防ぐセキュリティメカニズムです。銀行、決済、エンタープライズアプリケーションは、root化されたデバイスで機能をブロックまたは制限します。rootアクセスはAndroidサンドボックスの制限を解除し、トラフィックの傍受、プロセスメモリの読み取り、データ改ざんの可能性を開くためです。OWASP Mobile Top 10 (2024)によると、Root Detectionの欠如はカテゴリM8(Security Decisions via Untrusted Inputs)に該当します。Root Detectionは、ファイルシステムの静的チェックとランタイム動作の動的分析の組み合わせに基づいています。

重要ポイント

  • Root Detection — 危険な環境での実行からアプリを保護するために、Androidデバイスのrootアクセスを確認する
  • 静的メソッドは、suバイナリ、Superuserアプリ、システムパーティションの変更をファイルシステムで確認する
  • 動的メソッドはランタイムを分析:Build.TAGSの確認、特権プロセスでの/proc/self/mapsのオープン試行
  • ネイティブ実装はJNIを介したC/C++で、JavaレベルのXposedやFridaによる回避への耐性を提供する
  • セキュリティ Root Detectionは、チェック結果の改ざんを防ぐために継続的な難読化とサーバー側検証を必要とする

Root Detectionとは?

Root Detectionは、Androidデバイス上のrootアクセスの有無を検出するソフトウェアメカニズムです。rootアクセスはオペレーティングシステムを完全に制御し、アプリやスクリプトがUID 0でコマンドを実行できるようにします。root化されたデバイスでは、アプリの分離(Android Sandbox)が失われ、キーボード入力の傍受、他のアプリのSQLiteデータベースの読み取り、プロセスへのコード注入、信頼できるストアのSSL証明書の置換が可能になります。

金融およびエンタープライズアプリケーションにとって、root化されたデバイスでの実行は許容できないリスクをもたらします。攻撃者はトークン、セッションキー、個人データにアクセスできるようになります。PCI Security Standards Councilを含む規制当局は、決済アプリケーションにrootアクセスの検出と対応を義務付けています。これに対応して、Android開発者はプロアクティブな保護戦略の一部としてRoot Detectionを組み込んでいます。

検出には2つのアプローチがあります。ファイルシステムとインストール済みパッケージを分析する静的アプローチと、ランタイムでチェックを実行する動的アプローチです。組み合わせたアプローチが最も信頼性が高いと考えられています。これは、さまざまな回避ベクトルをカバーするためです。NowSecure(2025)の調査によると、Google Playのトップ100にある銀行アプリの76%が何らかの形のRoot Detectionを含んでいます。

root検出の静的メソッド

静的メソッドはアプリケーション起動時に実行され、ルート化ツールがファイルシステムに残すrootアクセスの痕跡を確認します。これらのメソッドは特権コマンドの実行を必要とせず、通常のアプリケーションのコンテキストで動作します。

suバイナリの存在確認

rootアクセスの主な指標は、標準パスでのsu実行可能ファイルの存在です:/system/bin/su、/system/xbin/su、/sbin/su、/su/bin/su。アプリケーションはFile.exists()またはlibcのaccess()のネイティブ実装を介してファイルの存在を確認します。さらに、su --versionまたはsu -c idを実行して終了コードを確認することもできます。

root管理アプリの検索

rootアクセスを管理する代表的なアプリケーション:Superuser、SuperSU、Magisk Manager、KingRoot。これらの存在は、PackageManager.getPackageInfo()または/data/app/ディレクトリの読み取りを介して確認されます。確認するパッケージ:com.topjohnwu.magisk、eu.chainfire.supersu、com.noshufou.android.su、com.thirdparty.superuser、com.koushikdutta.superuser、com.zacharee1.systemuituner。

システムプロパティの確認

AndroidはSystem.getPropertyおよびBuild.TAGSを介してアクセス可能なシステムプロパティにシステム状態情報を保存します。Build.TAGSにrelease-keysではなくtest-keysが含まれている場合、これはカスタムファームウェアを示し、多くの場合rootアクセスが可能です。さらに、/system/build.propを読み取ってro.build.tags、ro.debuggable、ro.secureを確認します。

java
public class RootDetectionChecker {
    private static final String[] SU_PATHS = {
        "/system/bin/su",
        "/system/xbin/su",
        "/sbin/su",
        "/su/bin/su",
        "/system/sd/xbin/su"
    };

    public boolean checkRootByFiles() {
        for (String path : SU_PATHS) {
            if (new File(path).exists()) {
                return true;
            }
        }
        return false;
    }

    public boolean checkRootByPackages(Context ctx) {
        String[] packages = {
            "com.topjohnwu.magisk",
            "eu.chainfire.supersu",
            "com.noshufou.android.su",
            "com.koushikdutta.superuser"
        };
        for (String pkg : packages) {
            try {
                ctx.getPackageManager().getPackageInfo(pkg, 0);
                return true;
            } catch (PackageManager.NameNotFoundException e) {
                // package not found
            }
        }
        return false;
    }
}

検証の動的メソッド

動的メソッドはアプリケーションの動作中に実行され、実行環境を分析します。静的メソッドとは異なり、Magisk HideやZygiskを介して隠されたルート化を検出できます。ファイル構造だけでなくシステムの動作をチェックするためです。

マウントポイントの確認

rootアクセスがあると、一部のシステムパーティションはro(読み取り専用)ではなくrw(読み書き)フラグでマウントされます。アプリケーションは/proc/mountsを読み取り、/systemがroとしてマウントされていることを確認します。/systemがrwとしてマウントされている場合、これは変更されたシステムを示します。さらに、Magiskを介した/suマウントの有無も確認されます。

セーフモードテスト

Androidセーフモードは、root管理アプリを含むサードパーティアプリケーションを無効にします。適切に実装されたRoot Detectionは、デバイスがセーフモードで動作しているかどうかを確認できます。アプリケーションがroot管理アプリは表示されないがsuバイナリが存在することを検出した場合、これはMagisk Hideの指標です。

コマンド実行確認

ProcessBuilderまたはRuntime.execを介してsu -c idを実行しようとすることは、rootアクセスの直接的なテストです。ただし、Magiskはこの呼び出しをインターセプトできます。より信頼性の高いアプローチは、ネイティブコードを介した確認です:/proc/1/limitsまたは/proc/self/mapsを開き、実行中のプロセスのUIDを分析します。アプリケーションがUID 0を取得できる場合、またはrootのみがアクセス可能なファイルを読み取れる場合、デバイスは危険にさらされています。

java
public boolean checkRootDynamically() {
    // Build flags check
    String buildTags = Build.TAGS;
    if (buildTags != null && buildTags.contains("test-keys")) {
        return true;
    }

    // Checking /system mount
    try {
        BufferedReader reader = new BufferedReader(
            new InputStreamReader(new FileInputStream("/proc/mounts"))
        );
        String line;
        while ((line = reader.readLine()) != null) {
            if (line.contains("/system")
                && line.contains("rw")) {
                reader.close();
                return true;
            }
        }
        reader.close();
    } catch (IOException e) {
        // error reading mounts
    }

    return false;
}

C++でのRoot Detectionのネイティブ実装

Javaで実装されたRoot Detectionは、Javaメソッドをインターセプトして戻り値を置き換えるXposedモジュールやFridaを介して簡単に回避できます。JNIを介したC++でのネイティブ実装は、はるかに耐性があります。Javaレベルで動作する動的分析ツールは、stat、access、popen、dlopenなどのネイティブlibc呼び出しを認識できないためです。

cpp
#include <unistd.h>
#include <sys/stat.h>
#include <cstring>
#include <vector>

extern "C"
JNIEXPORT jboolean JNICALL
Java_com_example_checker_RootCheck_nativeCheck(
    JNIEnv* env, jobject instance) {

    std::vector<const char*> paths = {
        "/system/bin/su",
        "/system/xbin/su",
        "/sbin/su",
        "/data/local/su"
    };

    struct stat st;
    for (const char* path : paths) {
        if (stat(path, &st) == 0) {
            return JNI_TRUE;
        }
    }
    return JNI_FALSE;
}

ネイティブチェックはJava APIを使用しないため、Dalvik/ARTレベルで動作する回避ツールからは見えません。追加の保護として、定数(パスリスト)を読み取り専用セクションに保存するのではなく、単純な可逆関数を介して計算することをお勧めします。libcのstat呼び出しはJavaラッパーをバイパスしてLinuxカーネルに直接アクセスし、Xposedを介してインターセプトすることはできません。

Root Detectionの回避手法

保護開発者は、堅牢な検出システムを構築するために、既存の回避手法を理解する必要があります。各回避手法には、適切なレベルでの対策が必要です。

Magisk HideとZygiskを介した回避

MagiskはAndroid 9–14で最も人気のあるルート化ツールです。Magisk Hideは/procからsuの存在を隠し、パスチェックの結果を偽装します。Magiskはカーネルレベルで動作し、アプリケーションが確認する前にstat()とaccess()をインターセプトします。対策:/sbin/.magiskの存在を介してMagisk自体の有無を確認するか、アプリケーション自身のmapsを読み取って確認します。Magiskはすべてのプロセスに自身のライブラリを注入します。

Fridaを介した回避

Fridaは、PtraceまたはDobbyを介してネイティブ関数をインターセプトできる動的インストルメンテーションツールです。Fridaは任意のチェックの戻り値を置き換え、statの結果をENOENTに偽装します。対策:メモリ内の命令のチェックサムを計算してネイティブ関数の整合性を検証し、frida-agent.soまたはfrida-helperの存在について/proc/self/mapsを分析してFridaを検出します。

APKパッチを介した回避

Javaで実装されたRoot Detectionは2〜3分で削除できます。APKはapktoolを介して逆コンパイルされ、smaliコードでメソッドの戻り値がfalseに変更され、APKが再構築されて署名されます。対策:重要なロジックをネイティブコードに移行し、Signature APIを介してランタイムでアプリケーションのデジタル署名を検証するか、APKハッシュをサーバー上の参照と比較します。

信頼性の高い保護のための推奨事項

効果的なRoot Detectionは多層アーキテクチャに基づいて構築されています。単一のメソッドだけでは十分な保護を提供できません。静的および動的チェック、ネイティブコード、サーバー側検証の組み合わせが最大の耐性を提供します。

サーバー側検証

クライアント側のチェックだけに依存しないでください。Root Detectionの結果をワンタイムセッショントークンとともにサーバーに送信します。サーバーが機能をブロックまたは制限するかどうかを決定します。これにより、クライアントアプリケーションが変更される可能性があるがサーバーが信頼できるままであるAPIレベルでの攻撃を防ぎます。

コードの難読化

Root Detectionコードは難読化する必要があります。攻撃者がjadxでsuパスチェックの明確なシーケンスを見た場合、回避には数分しかかかりません。ProGuardまたはDexGuardを使用して制御フローを難読化し、文字列を暗号化します。難読化により、保護コードの分析時間が数分から数時間に増加します。

定期的なシグネチャ更新

チェック対象のパス、パッケージ、インジケーターのリストは、アプリケーションのリリースごとに更新する必要があります。新しいルート化ツールや回避ツールは毎月登場します。1年間変更されていない静的リストでは、最新の手法を検出できません。チェックを実行する前に、アプリケーション起動時にサーバーから最新のシグネチャをロードすることをお勧めします。

よくある質問

アプリケーションにRoot Detectionが必要な理由は?

Root Detectionは、Androidサンドボックスが無効になっているデバイスでのアプリケーション実行から保護します。root化されたデバイスでは、任意のアプリケーションが他のアプリケーションのデータを読み取ることができます。銀行および決済アプリケーションは、PCI DSS要件およびOWASP Mobile Securityの推奨事項に従って、root化されたデバイスでの動作をブロックする義務があります。

Magisk Hideはどのように機能しますか?

Magisk Hideはマウント名前空間メカニズムを使用します。除外リスト内の各プロセスに対して、Magiskはsuバイナリが見えない隔離された名前空間を作成します。この名前空間でのシステムコールstat、access、openはMagiskファイルを認識しません。/proc/self/mapsの存在を確認し、magiskダンプを検索することでMagiskを検出できます。

Root Detectionはrootなしで回避できますか?

はい、アプリケーションがコードの整合性を検証しない場合可能です。Fridaを介して、Javaのチェックメソッドをインターセプトし、強制的にfalseを返させることができます。対策は、C++での重要なロジックのネイティブ実装とDEXファイルハッシュによる整合性検証です。難読化なしでは、Java上のRoot Detectionは5〜10分で回避されます。

SafetyNetとアテステーションとは?

SafetyNet(非推奨)とPlay Integrity APIは、デバイスの整合性を確認するGoogleのサーバー側チェックです。ブートローダー、システム署名、rootステータスの確認が含まれます。Play Integrity APIはSafetyNetの推奨代替であり、BASIC、DEVICE、STRONGの3つのレベルを提供します。クライアント側のRoot Detectionは、サーバー側のアテステーションを補完します。

アプリでRoot Detectionをテストする方法は?

実際のroot化されたデバイス(MagiskをインストールしたPixelなど)にアプリケーションをインストールします。ブロックが機能するか確認します。次に、アプリケーションに対してMagisk Hideを介してrootを隠し、テストを再実行します。詳細なテストには、Fridaを使用してターゲットメソッドをインターセプトし、ネイティブ保護が回避できないことを確認します。

まとめ

  • Root Detectionは、銀行、決済、エンタープライズAndroidアプリケーションに必須のセキュリティコンポーネントであり、静的および動的チェックの組み合わせで動作する
  • 静的メソッドには、標準パスでのsuバイナリの検索、インストール済みroot管理アプリの確認、Build.TAGSシステムプロパティの分析が含まれる
  • 動的メソッドは、rwモードでの/systemマウントの分析、/proc/mountsの整合性検証、特権コマンド実行テストを実行する
  • ネイティブ実装はJNIを介したC++で、Javaレベルで動作するXposedやFridaからチェックを不可視にし、DobbyやPtraceを介した回避が必要
  • Magisk Hideはマウント名前空間を介したRoot Detection回避の主要ツールであり、magiskライブラリの/proc/self/maps分析を通じて検出可能
  • 推奨アーキテクチャ:ネイティブチェック+コード難読化+サーバー側結果検証+定期的なシグネチャ更新
  • Play Integrity APIはGoogleのクライアント側Root Detectionをサーバー側デバイスアテステーションで補完し、包括的な保護を提供する

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください