Flutter(モバイル)アプリでのAPIキーなどをセキュアに扱う方法について
ネットで調べていると、APIキーなどを格納するために flutter_dotenv などを使い.envに格納するという記述があるものの、別の記事では「この方法はapkをunzipすることで確認できてしまうため不十分」と記載がある。
実際にそうなのか確認してみる。
参考:
How to Store API Keys in Flutter: --dart-define vs .env files
調べてみた結果は以下の通り。
- .envには機密性の高いキー情報は含めるべきではない
- envied などのパッケージを使用して難読化することは出来る
- Firebase, GoogleMapのAPIキーはクライアントに埋め込むことが出来る。
- 不正な利用の回避は主にセキュリティルールやAPIキーの制限で制御する
- Dartも含めたソースコードの難読化はリリースビルドでは常に行っておいた方が良い
assets/.envにダミーの変数を定義、アプリをReleaseモードでビルドしてみる。
build/app/outputs/apk/release/app-release.apk が生成されるので、
これをunzipする。
unzip app-release.zip
色々と展開される中にassetsフォルダも含まれていて、ここでそのままの.envファイルが含まれているのが確認できる。
apktool を使い以下のコマンドを実行することで逆アセンブルが出来るらしく、
この方法でも同様に assetsフォルダ内にそのままの.envが含まれることが確認できた。
apktool d app-release.apk
FirebaseのAPIキーについては、公式にもクライアントに埋め込むことが出来るもの(その上でセキュリティルールを使用して制限する)と記述があるので、Firebaseの場合は「APIキーとは言ってもプロジェクトを特定するためのキー的なもの」なのだと思った。
- FirebaseのAPIキーの使用と管理について学ぶ:Firebase の API キーは一般的な API キーとは異なります
- Firebaseのセキュリティチェックリスト:Firebase サービスの API キーはシークレットではありません
- https://twitter.com/_mono/status/1087680944612339713
なので、セキュリティルールに注意する必要がある。
※ただし、パスワード認証に対しブルートフォース攻撃を仕掛けられるようなことを避けるため特定のケース ではクォータを締めるなどして対策する。
Google MapsのAPIキーも、Android, iOS共にFirebaseで使うgoogle-services.jsonやGoogleService-info.plistの中に入っているAPIキーと同じものを使用するのでFirebaseのAPIキーと同じ状態に思える。
その上で、公式ドキュメントではキーを利用できるアプリケーションを制限することになっている。
Amplifyの場合はどうなるのか試したことがないので分からない。
元の記事を見ていると、.envを使用する場合は Enviedというパッケージを使用して .env自体の難読化を行うことが案として挙がっている。
.envに機密性の高い情報は含めない方が良いということだったけどこれを行えば解析し難くはなりそう。
また、どの選択をした場合でもコードの難読化は行っておいた方が良い旨が書かれている。
Firebaseのgoogle-services.json(GoogleService-Info.plist)やGoogleMap APIのAPIキーは
クライアントに埋め込んでも大丈夫そうな物と考えると今時点で機密性の高いキーは自分は持っていなそうなものの、難読化でどの程度変わるのか確認してみる。
(iOSも気になるが手元で確認できないのでAndroidだけ)
以下のコマンドでReleaseモードでビルド、難読化を有効にしてビルドする。
flutter build apk --obfuscate --split-debug-info=obfuscate/android --release --dart-define=FLAVOR=dev
ビルド後のapkを単純にunzipした状態とapktoolで逆アセンブルした状態で、AndroidManifest.xmlなどに埋め込んだAPIキーなども見れるのか確認してみる。
結果は以下の通り
情報 | unzipのみ | apktool d | 備考 |
---|---|---|---|
.env | 閲覧できる | 閲覧できる | assetsフォルダはどちらの場合もそのまま |
Firebase, GoogleMapのAPIキー | 閲覧できない | 閲覧できる | APIキー情報はAndroidManifest.xmlと、res/values/strings.xmlに記録される。apktoolでデコードしないと、AndroidManifest.xmlはバイナリ、res/valuesフォルダは存在しなかった。 |
APIキーを検索
unzipのみ
apktool d でデコードした結果
unzipしただけだとAndroidmanifest.xmlはバイナリ、res/valuesはフォルダごと存在しない