アプリ解析について

リバースエンジニアリングについて質問があるのですが、アプリを解析した場合、どこまでわかるのですか? 例えば、関数の内容や変数の中身まで?

いい質問ですね!リバースエンジニアリングで「どこまで分かるか」は、対象のアプリの種類(ネイティブアプリか、スクリプト系か)、コンパイル方法、難読化の有無などによって大きく変わります。

ざっくり整理すると:


1. ネイティブアプリ(例:C/C++で書かれてコンパイルされたバイナリ)

  • 関数名や変数名
    → 基本的に失われます(最適化やシンボル削除により)。sub_401000 のような無意味なラベルになることが多いです。
    ただし、デバッグ情報が残っている場合(例えば開発用ビルドを解析した場合)は、関数名や変数名も見えます。
  • 関数の内容
    → 機械語を逆アセンブルすることで命令列(アセンブリ)として見えます。
    IDA Pro や Ghidra のような逆コンパイラを使うと「C風の疑似コード」として表示可能。
    つまり「何をしているか」は追えるが、ソースコードそのものには戻らない。
  • 変数の中身
    → 逆アセンブルだけでは「名前」までは分からないけど、メモリのどの位置に何が入るかは追える。
    動的解析(デバッガで実行してレジスタやメモリを見る)と組み合わせれば、「実行時の値」まで把握可能。

2. バイトコード系(Java, .NET, Androidアプリなど)

  • 関数名や変数名
    → そのまま残っている場合が多い(特にローカル変数は削除されやすいが、メソッド名やクラス名は残る)。
    ProGuard などの難読化をしていなければ、ほぼソースそのまま。
  • 関数の内容
    → .class や .dll はデコンパイルすれば、オリジナルに近いソースコードが得られる。
    例:Javaなら jad や cfr、.NETなら ILSpy
  • 変数の中身
    → 変数名は最適化で消えることもあるけど、実行時にどういう値が入るかは動的解析で把握可能。

3. スクリプト系(JavaScript, Python, PHP など)

  • 関数名や変数名
    → 基本的にそのまま見える(ただし圧縮や難読化されている場合は読みにくくなる)。
  • 関数の内容
    → ソースそのまま、または軽い変換を経た状態で取得可能。
  • 変数の中身
    → 実行時の値はデバッガで確認可能。

まとめ

  • ネイティブアプリ:ソースコードレベルではなく、アセンブリや疑似コードで解析。変数名は消えるが処理の流れは分かる。
  • バイトコード系アプリ:ほぼソースに近い形で再現可能(難読化してなければほぼ丸見え)。
  • スクリプト系:ソースがそのまま手に入ることも多い。

「どこまで外から覗けるのか(セキュリティ観点)」を知りたい です。 iOSアプリで自作のREST APIを使用する際の認証トークンを得る、初めの通信において、iOSアプリ側から送る固定パスをどうするか考えていました、どうすれば秘匿できるのかな?と。

いいですね — セキュリティ観点での「初回の固定パス(いわゆるクライアントシークレット)」の扱いについて、結論から言うと:

「クライアント側(iOSアプリ)に完全に秘密を置くことはできない」
端末を攻撃者が持てばバイナリを解析・改変できるため、埋め込んだ固定値(パスワード/シークレット)は最終的に露出する可能性があります。だから目的は「絶対に漏れない」にするのではなく「漏れても被害を限定/検出できる設計」をすることです。

以下、実践的な推奨と具体案を優先度高い順にまとめます。


高信頼で推奨する方法(サーバで秘匿・端末検証を組み合わせる)

  1. クライアントに対する“永久的な共有シークレット”を持たせない
    → 代わりに「端末ごと/インストールごとの資格情報(公開鍵方式)」を使う。
  2. App Attest(Apple)+チャレンジ/レスポンス方式(最も推奨)
  3. 端末内の秘密は Secure Enclave / Keychain に格納
  4. 短寿命のアクセストークン+リフレッシュはサーバ経由で

実務的な補助対策(これだけで安全とは言えないが有効)

  • TLS(HTTPS)は必須。証明書ピンニングを検討
    中間者対策としてピンニングを行うと安全性が上がるが、運用コスト・更新運用に注意(失効時のロックアウトリスク)。
  • 難読化(バイナリ難読化)
    固定文字列やAPIパスを見えにくくする。効果は限定的だが検出コストを上げられる。反リバースエンジニア向けの一手。
  • Jailbreak/root 検知(だが頼り過ぎない)
    Jailbreak を検知して警告・機能制限するのは有効。ただし回避方法も多数あるので「追加の防御層」として扱う。
  • サーバ側で異常検知・レート制限・ワークフロー
  • ローテーションと失効
    固定値が漏れた場合のために、サーバ側の鍵・認証方式をローテーションできるしくみを持つ。

実装パターン(簡潔なサンプル設計)

A)ユーザー認証あり(普通のケース)

  1. ユーザーはID/PWでログイン(またはOAuth)。
  2. サーバは短期アクセストークン(例:15分)とリフレッシュトークンを発行。
  3. リフレッシュは端末固有の証明(App Attest or device key)を要求して発行。

→ クライアント側に「固定パス」を置かない。

B)ユーザー無しでアプリ単体の認証(APIキー的に使いたい場合)

  1. 初回起動でクライアントが端末鍵を生成(Secure Enclave)。
  2. サーバに公開鍵登録要求を送る。サーバはチャレンジを返す。
  3. クライアントがチャレンジに署名して返す。サーバで署名検証&Apple App Attest 検証を行う。
  4. サーバは短期アクセストークンを返す(以降はこのトークンでAPI呼び出し)。

→ 固定文字列は不要。端末ごとの鍵+attestationで代替。


注意点(現実的な限界)

  • 脱獄/物理アクセスされている端末では秘密は抜かれる可能性が高い。Secure Enclaveがあっても、OS改変やメモリ抽出で突破されるリスクはゼロではない。
  • ネットワーク越しの攻撃を想定した設計(TLS/ピンニング) と サーバ側での監視・制限 が必須。
  • 「完璧に隠す」ことは出来ないので、被害最小化(短寿命トークン、監査、ローテーション)が鍵。

まとめ(短く)

  • iOSアプリに固定パスを埋め込むのは避ける。
  • 代わりに 端末固有キー+App Attest(またはSecure Enclaveでの鍵生成)+チャレンジ署名 の組合せで初回認証を行い、短寿命トークン を使う。
  • さらに TLS + 証明書ピンニング、難読化、サーバ側の異常検知/レート制限 を組み合わせる。
  • それでも端末が完全に乗っ取られたら安全は保証できないので、サーバ側で被害を限定・検知できる設計にする。

Secure Enclave と チャレンジを返す。 のところを詳しく教えてください。

Secure Enclave とチャレンジ/レスポンスの実装を 実践できるレベル で掘り下げます。
以下は「iOS クライアント(Swift)」 ⇄ 「サーバ(例:Node.js)」 のフロー+コード例+運用上の注意点です。長めですがそのままコピペして使えるレベルにしてあります。


概要フロー(短く)

  1. クライアント(初回)で鍵ペア(EC P-256)を Secure Enclave に生成・保存。
  2. クライアントは公開鍵(SPKI DER を Base64)をサーバへ送る要求を出す(POST /register)。
  3. サーバは 一回限りのランダムチャレンジ(nonce) を生成(例:32バイト)、保存してクライアントに返す。
  4. クライアントはチャレンジを Secure Enclave の秘密鍵で署名し、署名(DER)を返す(POST /register/complete)。
  5. サーバは公開鍵で署名を検証。成功ならその公開鍵を端末IDに紐付けて登録し、短寿命のアクセストークンを発行する。

iOS (Swift) — Secure Enclave に鍵をつくる・公開鍵を送る・チャレンジに署名する

ポイント:

  • kSecAttrTokenIDSecureEnclave を付けると秘密鍵は Secure Enclave 内に生成・保持され、外に出てきません(安全性向上)。
  • 公開鍵は SecKeyCopyExternalRepresentation で取得 → 上に示した SPKI プレフィックス を付けて DER(SPKI) にします(そのままだと raw 公開点なのでサーバが使いにくいため)。
  • 署名アルゴリズムは ecdsaSignatureMessageX962SHA256(メッセージを SHA-256 でハッシュして ECDSA、出力は ASN.1 DER 形式)を例にしています。チャレンジは生のバイト列をそのまま渡して構いません。

サーバ(Node.js) — チャレンジ発行と署名検証の例

ポイント:

  • サーバに渡す公開鍵は SPKI DER(上で作った prefix + raw 公開点) を base64 にしたもの。Node の createPublicKey({format:'der', type:'spki'}) でそのまま扱えます。
  • 署名は iOS が返す ASN.1 DER(ECDSA の r/s)なので、そのまま verify(publicKey, signature) で検証できます。
  • チャレンジは短時間・一回限りで運用(例:60秒・使い切り)。リプレイ防止の要です。

実装/運用上の重要な注意点(実務で絶対見てほしい)

  • チャレンジは短命・一回限り(例 32 バイトランダム、TTL 30〜120 秒)。必ず DB に pending として保存し、検証後削除する。
  • チャレンジは「その公開鍵に紐づく」 ようにする(上の例では fingerprint)。そうしないと別公開鍵で悪用される可能性がある。
  • SSL/TLS は必須(HTTPS)。可能なら 証明書ピンニング を検討(運用の負担あり)。
  • 公開鍵の fingerprint を作って device-id とする(SHA-256(publicKeyDer) など)。これで後続の API 呼び出しでデバイスを識別できる。
  • 鍵ローテーション・失効手段を用意する。端末紛失や鍵漏洩時にサーバから取り消せる設計にする。
  • リジェクト基準:同一公開鍵の大量登録や短時間大量リクエストはブロック/アラート。
  • Secure Enclave がない端末のフォールバックをどう扱うか決める(Keychain にソフト鍵を置くならリスクを理解したうえで)。
  • App Attest を組み合わせるとさらに強くなる(App Attest は「このアサーションは正規のAppから来ている」ことを Apple が保証してくれる)。可能なら Attest をチャレンジに含めて検証する運用を強く推奨。

なぜこれが安全か(&欠点)

  • 秘密鍵は 端末から取り出せない(Secure Enclave)。つまりバイナリから固定シークレットを引っ張られるリスクを避けられる。
  • サーバ側は 公開鍵で署名を検証 するだけ。固定パスワードをバイナリに埋めるより遥かに安全。
  • ただし:脱獄端末や OS を改変された環境ではキー操作を傍受されるリスクが残る(Secure Enclave自体は強いが、周辺の攻撃ベクトルは存在する)。だから App Attest やヒューリスティック検知、ログ監査と組み合わせる。

追加で実装すると良いもの(優先度高い順)

ポイント:

  • サーバに渡す公開鍵は SPKI DER(上で作った prefix + raw 公開点) を base64 にしたもの。Node の createPublicKey({format:'der', type:'spki'}) でそのまま扱えます。
  • 署名は iOS が返す ASN.1 DER(ECDSA の r/s)なので、そのまま verify(publicKey, signature) で検証できます。
  • チャレンジは短時間・一回限りで運用(例:60秒・使い切り)。リプレイ防止の要です。

実装/運用上の重要な注意点(実務で絶対見てほしい)

  • チャレンジは短命・一回限り(例 32 バイトランダム、TTL 30〜120 秒)。必ず DB に pending として保存し、検証後削除する。
  • チャレンジは「その公開鍵に紐づく」 ようにする(上の例では fingerprint)。そうしないと別公開鍵で悪用される可能性がある。
  • SSL/TLS は必須(HTTPS)。可能なら 証明書ピンニング を検討(運用の負担あり)。
  • 公開鍵の fingerprint を作って device-id とする(SHA-256(publicKeyDer) など)。これで後続の API 呼び出しでデバイスを識別できる。
  • 鍵ローテーション・失効手段を用意する。端末紛失や鍵漏洩時にサーバから取り消せる設計にする。
  • リジェクト基準:同一公開鍵の大量登録や短時間大量リクエストはブロック/アラート。
  • Secure Enclave がない端末のフォールバックをどう扱うか決める(Keychain にソフト鍵を置くならリスクを理解したうえで)。
  • App Attest を組み合わせるとさらに強くなる(App Attest は「このアサーションは正規のAppから来ている」ことを Apple が保証してくれる)。可能なら Attest をチャレンジに含めて検証する運用を強く推奨。

なぜこれが安全か(&欠点)

  • 秘密鍵は 端末から取り出せない(Secure Enclave)。つまりバイナリから固定シークレットを引っ張られるリスクを避けられる。
  • サーバ側は 公開鍵で署名を検証 するだけ。固定パスワードをバイナリに埋めるより遥かに安全。
  • ただし:脱獄端末や OS を改変された環境ではキー操作を傍受されるリスクが残る(Secure Enclave自体は強いが、周辺の攻撃ベクトルは存在する)。だから App Attest やヒューリスティック検知、ログ監査と組み合わせる。

追加で実装すると良いもの(優先度高い順)

  1. App Attest の導入(Apple が発行するアサーションを使って「正規アプリか」を検証)。
  2. 短期トークン(15分等) + リフレッシュには device-bound 証明を要求
  3. 登録時にユーザーアクションを入れる(CAPTCHA、メール確認、SMSなど) — 特に大規模に公開する場合。
  4. サーバの異常検知(同 IP からの高頻度登録、地理的矛盾など)。

結論:個人データやパスを扱うアプリの作成は極力避ける。lol

コメント

タイトルとURLをコピーしました