セキュリティエンジニアの小竹(aka tkmru)です。iOSアプリを診断する際に行う再署名について今回は解説していきたいと思います。 このエントリーはAkatsuki Games Advent Calendar 2022の1日目の記事です。
iOS、iPadOSには署名という概念があります。 アプリがApp Storeから配布されている正当なものであることを保証するために、開発者がApple発行の証明書を使用して署名を実施したアプリしかインストールできない仕様になっています。
この仕様が脆弱性診断の際に障害になることがあり、セキュリティエンジニアは再署名(re-sign)を度々行います。 再署名は、開発者が使用したのとは違う別の証明書でアプリに対して再度署名を行う行為のことです。 再署名は複雑な処理で手動で行うと度々ミスが発生します。 そのため、再署名を行うツール「aktsk/ipautil」を社内で作成し、使用しています。
なぜ脆弱性診断に再署名が必要なのか
脆弱性診断の際には開発チームの方から診断対象のアプリを受け取り、検査を行いますが、開発環境のAdHocビルドのアプリはどんな環境でも動作する訳ではありません。 AdHocビルドのアプリは、Apple Developer Program内でDevice ID(UDID)を登録している端末にしかインストールできないようになっています。そのため、脆弱性診断用の検証端末にインストールするのに再署名が必要になります。
これだけであれば、開発者の方に検証端末のDevice IDを登録してもらえばいい話ではあります。 しかし、脆弱性診断の際には、アプリ内のファイルに変更を加え、動作させる必要性に迫られることが多々あり、その際には再署名が必須になります。
診断時にはBurp Suiteなどのプロキシツールが発行する独自のCA証明書を検証端末にインストールし、中間者攻撃を行い、APIとの通信内容を確認します。 しかし、アプリ内に組み込まれたCA証明書以外での通信をできなくするSSL Pinning(Certificate Pinning)が実装されている場合には、中間者攻撃を行えません。この場合、SSL Pinningを無効化するパッチを当てる必要があります。パッチを当て、ファイルに変更を加えた場合は、署名が無効になるため再署名が必要になります。 また、クライアント側に複雑なロジックを実装しているアプリ(ex. ゲームアプリ)を対象に、検査を行う際には、アプリに変更を加え、不正な動作を引き起こせないか確認する必要があります。iOS、iPadOS上で動作するアプリはIPAというファイル形式(.ipa)で、このファイルの実態はZIPファイルです。そのため、容易に中身を閲覧し、変更できます。
再署名の手順
再署名に必要なファイルや具体的な手順を解説します。
事前準備
次のファイルが必要です。
- 開発用証明書(.cerファイル)
- Provisioning Profile(.mobileprovisionファイル)
- entitlements.plist
開発用証明書とProvisioning Profileを生成するにはApple Developer Programを契約している必要があります。 それぞれ、Apple Developer Programの管理画面上で作成し、ダウンロードしてください。Provisioning Profileには、証明書の情報とインストール可能な端末のリストが記載されています。Provisioning Profileを作成する際にはインストールしたい端末のDevice IDを管理画面に登録するのを忘れないでください。
プロビジョニングプロファイルを編集、ダウンロード、削除する - デベロッパアカウントヘルプ
開発用証明書は再署名を行うMac上にインストールする必要があります。 キーチェーンアクセスを開き、「システム」にドラッグアンドドロップしてインストールしてください。有効な証明書をインストールできているかどうかは次のコマンドで確認できます。
$ security find-identity -p codesigning -v 1) XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX "iPhone Developer: Taichi Kotake (XXXXXXXXXX)" 1 valid identities found
entitlements.plistはアプリのIDや作成した組織などの基本情報や特殊な操作を許可するかの設定を記述しておくファイルです。実態はXMLファイルで次のようなファイルです。application-identifier
にはApple Developer ProgramのアカウントのApp ID Prefix(Team ID)とApple Developer Programの管理画面で指定したBundle IDを結合したものをそれぞれ指定します。
com.apple.developer.team-identifier
にはApple Developer ProgramのアカウントのApp ID Prefixを指定します。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <!-- application-identifierにワイルドカードは使えない --> <key>application-identifier</key> <string>XXXXXXXXXX.jp.aktsk.hoge</string> <key>com.apple.developer.team-identifier</key> <string>XXXXXXXXXX</string> <!-- get-task-allow属性をtrueにしておくとデバッガを使用できる --> <key>get-task-allow</key> <true/> <key>keychain-access-groups</key> <array> <string>XXXXXXXXXX.jp.aktsk.*</string> </array> </dict> </plist>
上記のファイルをテンプレートとして、必要な情報を書き換えることで使うこともできますが、ミスを防ぐためにXcodeを使って生成したアプリからentitlements.plist
を抽出し、必要に応じて一部だけを書き換えるのがおすすめです。
$ codesign -d --entitlements :- Payload/hoge.app > entitlements.plist
手順
ここでは、再署名の詳細な手順を解説します。 手動で行う方法を説明したあとに便利なツールについても紹介します。
手動で行う
1. IPAファイルを解凍する
アプリをunzipコマンドで解凍すると、Payloadフォルダが出現します。 このフォルダの中にアプリを動作させるのに必要な各種ファイルが含まれています。
$ unzip <IPAファイルへのパス>
2. Provisioning Profileを置き換える
まず、Payloadフォルダの中にあるembedded.mobileprovision
をApple Developer Programの管理画面からダウンロードしたProvisioning Profileで置き換えてください。
$ cp <ダウンロードしたProvisioning Profile> Payload/hoge.app/embedded.mobileprovision
3. 各ファイルに署名をする
コードに相当する内容の実行ファイル、共有ライブラリとそのファイルが配置されているフォルダに署名を行う必要があります。
コマンドの実行例は次のようになります。
XXXXXXXXXXの部分にはentitlements.plist
で指定しているcom.apple.developer.team-identifier
の値を指定してください。
$ codesign --force --sign XXXXXXXXXX --entitlements entitlements.plist Payload/Hoge.app/Hoge Payload/Hoge.app/Hoge: replacing existing signature
4. IPAファイルを生成
最後にzipコマンドでIPAファイルを生成してください。
$ zip -ry <IPAファイル名> Payload SwiftSupport
便利なツール
ここまで手動でやる方法を解説してきましたが、私が実装したaktsk/ipautilというツールを使うと事前準備は必要なものの、再署名作業をコマンド一発で楽に行えます。
$ ipautil decode hoge.ipa Decoding IPA... Archive: hoge.ipa creating: Payload/ creating: Payload/Hoge.app/ ... $ ipautil build ./Payload Signing IPA by codesign... Payload/Hoge.app/Hoge: replacing existing signature ... Signed Building IPA... adding: Payload/ (stored 0%) adding: Payload/Hoge.app/ (stored 0%) adding: Payload/Hoge.app/_CodeSignature/ (stored 0%) adding: Payload/Hoge.app/_CodeSignature/CodeResources (deflated 74%) adding: Payload/Hoge.app/Hoge(deflated 66%) ... Building IPA...
ハマりどころ
再署名の際に何らかのミスをして、アプリをインストールできなくても、codesignコマンドの実行時にエラーが出ないケースやApple Configuratorから確認できるログには原因究明につながるエラーが出ないケースがあります。そのため、ハマると何時間も浪費してしまいがちです。ここでは、よくあるミスを紹介します。
証明書は「常に信頼」では使えない
証明書はインストールされていても信頼されていなければ使用できません。
しかし、「常に信頼」されている状態でも使用できません。
codesignコマンドで署名を行った際にWarning: unable to build chain to self-signed root for signer
というエラーが出た場合は、キーチェーンアクセスで証明書を確認し、「信頼」→「この証明書を使用するとき」が「システムデフォルトを使用」になっていることを確認してください。
「常に信頼」「システムデフォルトを使用」のどちらの状態でも、security find-identity -p codesigning -v
の実行結果では、valid identities
と表示されてしまうため、キーチェーンアクセスでも証明書の状態を確認するほうが良いです。
application-identifierにはワイルドカードは使えない
entitlements.plist
の例の中でコメントとしても記載していますが、application-identifierにはワイルドカードは使えないので注意してください。
application-identifier
にワイルドカードを使用していると署名したアプリをインストールできません。
これでハマると、エラーメッセージからはapplication-identifier
が原因だと判別するのが難しいため、解決に時間がかかりがちです。
署名ができていそうなのに、アプリをインストールできない場合はapplication-identifier
にワイルドカードを使用していないか確認してみてください。
再署名漏れのファイルがありがち
アプリのファイル構造は、開発によって使用されたフレームワークによって異なります。 そのため、手癖で決まったディレクトリにあるファイルに対して再署名を行っていると、漏れが発生し得ます。 どのファイルの署名がおかしいのかエラーメッセージからは確認できないため、解決に時間がかかりがちです。
アプリ内のファイル一つ一つに対し、次のコマンドで署名を確認できます。手間はかかりますが、この方法で署名がおかしいファイルを発見できます。
$ codesign -d --entitlements :- Payload/Hoge.app/Data/Managed/Metadata/global-metadata.dat Executable=/Users/tkmru/Downloads/Payload/Hoge.app/Data/Managed/Metadata/global-metadata.dat <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>application-identifier</key> <string>XXXXXXXXXX.jp.aktsk.hoge</string> <key>com.apple.developer.team-identifier</key> <string>XXXXXXXXXX</string> <key>get-task-allow</key> <true/> <key>keychain-access-groups</key> <array> <string>XXXXXXXXXX.jp.aktsk.*</string> </array> </dict> </plist>
これは、手動で再署名を行っている場合に起こるミスです。 aktsk/ipautilはPayloadファルダ内のすべてのファイルに再署名を行う実装になっているので、ipautilを使えばこのようなミスは防げます。
まとめ
脆弱性診断での再署名の必要性と手順を紹介しました。 SSL Pinningなどの攻撃を妨害する仕組みは、脆弱性診断の際にも障害となります。 それらを無効化するためにファイルに変更を加えた場合は、署名が無効になるため再署名が必要になります。 また、再署名にはハマりどころが多いため、それらの解決策についても紹介しました。 脆弱性診断に従事する方や再署名の必要性に迫られている開発者の方の助けになれば幸いです。