コース目次 / 第4章
直す — 秘密を抜き、マニフェストを締める
ハードコードされた秘密を除去し、allowBackup/exportedを締め、usesCleartextTrafficを外して再ビルドします。jadx/apktoolで直った状態を再検証します。
4つの穴を、この章で一気に直します。原則は、これまでと同じ——事実(秘密)はクライアントに置かない、既定は最も締まった設定に。
直し1 — 秘密を、コードから抜く
MainActivity.smali から、API_KEY の行を削除します。
.class public Lcom/example/vulnnotes/MainActivity;
.super Landroid/app/Activity;
.method public constructor <init>()V
.locals 0
invoke-direct {p0}, Landroid/app/Activity;-><init>()V
return-void
.end method
直した先の設計は、「アプリの起動時に、サーバから必要なトークンを取得する」形です(sec-auth のセッション管理と同じ発想——アプリが持つのは、都度取得する短命な認証情報であって、埋め込まれた恒久的な秘密鍵ではありません)。APIキーのような恒久的な秘密は、そもそもクライアントのバイナリに含めないのが本筋です。
直し2〜4 — マニフェストを、締める
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.vulnnotes">
<uses-permission android:name="android.permission.INTERNET" />
<application
android:label="VulnNotes"
android:allowBackup="false">
<activity android:name=".MainActivity" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".AdminActivity" android:exported="false" />
</application>
</manifest>
allowBackup=“false” — バックアップ経由の抽出を禁じる。
usesCleartextTraffic の指定を削除 — 指定しなければ、Android の既定(平文禁止)に従う。「緩めない」のがいちばん簡単な直し方です。
AdminActivity は exported=“false” — 起動画面ではないので、他アプリから直接起動される理由がありません。もし将来、他アプリと連携する正当な理由ができたら、そのときは限定した権限(android:permission)を要求する形で、慎重に開けます。
再ビルドして、再検証する
apktool b vulnnotes -o vulnnotes.apk
jarsigner -keystore debug.keystore -storepass android vulnnotes.apk debugkey
秘密が消えたか、jadx で確認します。
jadx -d out -f vulnnotes.apk
cat out/sources/com/example/vulnnotes/MainActivity.java
package com.example.vulnnotes;
import android.app.Activity;
public class MainActivity extends Activity {
}
マニフェストが締まったか、apktool で確認します。
apktool d -f -o decompiled vulnnotes.apk
cat decompiled/AndroidManifest.xml
<application android:label="VulnNotes" android:allowBackup="false">
<activity android:name=".MainActivity" android:exported="true">
...
</activity>
<activity android:name=".AdminActivity" android:exported="false" />
</application>
秘密は消え、allowBackup は false、usesCleartextTraffic の記述自体が無くなり(既定の安全側)、AdminActivity は exported=“false” になりました。これで、この章の4つの穴はすべて塞がっています。MainActivity の exported=“true” は、起動画面として必要な設定なので、そのままです——「すべて閉じる」のではなく、「開ける理由があるものだけ、開けたままにする」のが正しい締め方です。
持ち帰る一言
開ける理由があるものだけ、開けたままにする。 秘密はクライアントから抜き、バックアップと平文通信は既定で禁じ、exportedは本当に必要なものだけ。再ビルドして、逆コンパイルで直ったことを目で確かめる——これも旗艦コースからの「攻撃→修正→再検証」でした。次はまとめて、静的解析の限界と、この先の道を確認します。
こうなっていればOK
卒業まであと1章です。
この章はまだ完了していません。
保存できませんでした(プライベートブラウズ中かもしれません)。この端末に進捗は残りませんが、先へは進めます。