モバイルアプリパイプラインに署名が必要な理由(そして安全に保つ方法)
Android または iOS アプリのビルドが完了し、テストもすべてパスして、いよいよリリースという段階になったとします。しかし、APK や IPA を誰かのデバイスに届ける前に、もうひとつ、しばしば「お役所仕事」と感じられるステップがあります。それがアプリの署名です。
署名を単なるチェック項目として扱いたくなる気持ちはわかります。しかし、キーストアを紛失した経験、金曜の夜に証明書の期限切れに気づいた経験、あるいは誤ってプロビジョニングプロファイルを公開リポジトリにコミットしてしまった経験があるなら、ここがまさに本番だと理解しているはずです。署名は単なる技術的な手続きではありません。アプリが自分自身のものであり、誰かがリパッケージしたり、なりすましたりしたものではないことを証明するセキュリティレイヤーなのです。
署名の実際の役割
アプリに署名するとは、バイナリにデジタル署名を付与し、それをあなたの身元に結びつけることです。この署名はオペレーティングシステムとアプリストアによって検証されます。署名が一致しなければ、アプリはインストールされないか、ストアがアップロードを拒否します。
Android の場合、署名にはキーストアファイルを使用します。このファイルには秘密鍵とデジタル証明書が含まれています。いわばあなたの公式スタンプです。リリース用の APK や AAB をビルドするたびに、そのキーストアでスタンプを押します。後で別のキーストアを使用すると、パッケージ名が同じでも、Android はそのアプリをまったく別のアプリケーションとして扱います。つまり、ユーザーは既存のインストールの上からアプリを更新できず、古いバージョンをアンインストールしてから再インストールする必要があり、すべてのローカルデータが失われます。
iOS の場合は、さらにレイヤーが増えます。必要なものは2つです。証明書とプロビジョニングプロファイルです。証明書は開発者としてのデジタルアイデンティティです。プロビジョニングプロファイルは、証明書、App ID、およびアプリの実行を許可されたデバイスのリストを結びつけます。App Store 配布用には、配布証明書と App Store プロビジョニングプロファイルを使用します。内部テストや開発用には、開発証明書とアドホックプロビジョニングプロファイルを使用します。
本当の問題:パイプライン内で秘密情報を守る方法
署名に必要なものがわかったところで、次の疑問が浮かびます。これらの認証情報をコードや設定ファイルに書き込まずに、CI/CD パイプラインでどのように保存するのか?
答えはシークレット管理です。ただし、まずはやってはいけないことを明確にしておきましょう。
キーストア、証明書、プロビジョニングプロファイルを Git リポジトリに保存してはいけません。これらのファイルは設定ではなく、秘密情報です。公開リポジトリに紛れ込めば、誰でもあなたになりすましてアプリに署名できます。プライベートリポジトリであっても問題があります。リポジトリにアクセスできるすべての開発者が、本番用の署名鍵を保持することになるからです。これはセキュリティと監査のリスクです。
代わりに、CI/CD プラットフォームが提供するシークレットストアを使用しましょう。GitHub Actions、GitLab CI、Jenkins、その他ほとんどのプラットフォームには、組み込みのシークレット変数があります。キーストアや証明書を base64 エンコードした文字列としてアップロードし、シークレット変数として保存します。パイプライン実行時に、それをデコードしてファイルに戻します。シークレットは実行時までビルドマシンのディスクに書き込まれず、ログにも表示されません。
以下は、GitHub Actions を使用した Android の具体例です。
- name: Decode keystore
run: echo "${{ secrets.KEYSTORE_BASE64 }}" | base64 --decode > app/release.keystore
Fastlane と GitLab CI を使用した iOS の例です。
- name: Decode certificate
run: echo $MATCH_PASSWORD | fastlane match import --git_url $MATCH_REPO
より細かい制御が必要なチームは、AWS Secrets Manager、Azure Key Vault、HashiCorp Vault などの専用シークレットマネージャーの使用を検討してください。パイプラインは実行時にこれらのサービスから認証情報を取得します。このアプローチにより、監査ログ、アクセス制御、集中管理によるローテーションが可能になります。認証情報がパイプライン設定自体の中に置かれることはありません。
以下は、AWS Secrets Manager からキーストアを取得し、jarsigner で APK に署名し、署名を検証する完全な Bash スクリプトです。
#!/bin/bash
set -euo pipefail
# AWS Secrets Manager からキーストアを取得
KEYSTORE_SECRET=$(aws secretsmanager get-secret-value \
--secret-id "mobile-app/keystore" \
--query SecretString --output text)
echo "$KEYSTORE_SECRET" | base64 --decode > /tmp/release.keystore
# APK に署名
jarsigner -verbose -sigalg SHA256withRSA \
-digestalg SHA-256 \
-keystore /tmp/release.keystore \
-storepass "$STORE_PASSWORD" \
app-release-unsigned.apk \
mykeyalias
# 署名を検証
jarsigner -verify -verbose -certs app-release-unsigned.apk
# クリーンアップ
rm -f /tmp/release.keystore
このスクリプトにより、キーストアがリポジトリに保存されることはなく、実行時に安全に取得され、署名後すぐに削除されます。
証明書の有効期限:静かなるパイプラインキラー
よくあるシナリオです。パイプラインは何ヶ月も問題なく動作していました。ところがある日、リリースビルドが失敗します。ログを調べると、署名証明書の有効期限が切れていることがわかります。すでにストアにあるアプリはユーザーのデバイスで問題なく動作しています。しかし、新しいバージョンをアップロードすることはできません。新しいバイナリの署名が無効なため、ストアが拒否するからです。
Android のキーストアと iOS の証明書には有効期限があります。自動的に更新されることはありません。パイプラインは期限切れを検出し、認証情報が使用できなくなる前にチームに警告する必要があります。キーストアや証明書の有効期限を確認し、チームのチャットチャンネルに通知を送る簡単なスクリプトがあれば、リリースのブロックを防げます。
Android の場合、キーストアの有効期限は次のコマンドで確認できます。
keytool -list -v -keystore release.keystore -storepass $STORE_PASSWORD | grep "Valid until"
iOS の場合、Fastlane の match ツールには --readonly モードがあり、証明書の有効期限を表示できます。macOS の security コマンドも使用できます。
security find-identity -v -p codesigning | grep "iPhone Distribution"
パイプラインにおける署名の実践的チェックリスト
初めて署名を設定する場合、または現在の設定を見直す場合は、次のチェックリストを確認してください。
- キーストア、証明書、プロビジョニングプロファイルは Git の外部に保存されていますか?
- 署名認証情報は CI/CD プラットフォームのシークレットストア、または外部のシークレットマネージャーに保存されていますか?
- base64 エンコードされたキーストアまたは証明書は、設定ファイルではなくシークレット変数として保存されていますか?
- パイプラインは、一時ディレクトリで実行時にのみシークレットをデコードしていますか?
- 一時的な署名ファイルはビルド完了後に削除されていますか?
- パイプラインは証明書の有効期限をチェックし、期限切れ前にチームに警告していますか?
- 署名認証情報のローテーションまたは更新手順が文書化されていますか?
- 本番用の署名認証情報は、信頼できる少数のチームメンバーに制限されていますか?
署名は終わりではない
アプリに署名が完了すると、アーティファクトはテストの準備が整います。しかし、ビルドマシン上にある署名済みバイナリは、テスト済みリリースと同じではありません。モバイルアプリは、コードを読んだり単体テストを実行したりするだけでは完全に検証できません。ランタイムでのみ発生する問題を発見するには、エミュレーター、シミュレーター、または実機で署名済みアプリを実行する必要があります。
署名ステップはゲートです。これからテストし、出荷しようとしているものが、本当にあなたのものであることを保証します。本番データベースの認証情報と同じ注意を払って扱ってください。なぜなら、多くの点でそれ以上に価値があるからです。データベースのパスワードを失っても、バックアップから復元できます。しかし、キーストアを失うと、アプリを更新する能力そのものを失うことになります。