sanadamaru's blog

学びメモや考えごとなど

Codecovセキュリティアップデートの対応~ Android編

前回のCodecovのセキュリティアップデートの対応~iOS編 - sanadamaru's blog

に引き続き今回はCodecovセキュリティアップデートで対応したことのAndroid編を書いていこうと思います。

対応内容

CircleCIのEnvironment Variablesに登録している以下のシークレット情報を更新しました。

  • PLAY_ACCOUNT_JSON_BASE64の変更(Google Playのサービスアカウント)

  • Android keystoreとアップロード鍵の変更

  • Google Play Consoleに登録している各アプリのアップロード鍵のリセット申請

PLAY_ACCOUNT_JSON_BASE64の変更

私のプロジェクトではGoogle Playにapkのアップロードを自動化するためにGradle Play Publisherプラグインを使用しています。 github.com

このプラグインを使用するためにはPlay Developer API にアクセスできるサービスアカウントが必要なため、 Gradle Play PublisherのリポジトリのREADMEのサービスアカウントの作成手順を参考に再作成しました。 多くのプロジェクトでは、サービスアカウント作成時にダウンロードできるJSON認証情報はプロジェクト内に配置すると思いますが、私のプロジェクトではCircleCIに登録しているためJSON認証情報をbase64エンコードして登録し直しました。

通常であれば上記の手順のみで済むのですが、実際にはデベロッパーアカウントの所有者権限が退職者のアカウントに紐づいていてデベロッパーアカウントを最作成しアプリを移行する必要があったり結構大変でした。(サービスアカウントの最作成はデベロッパーアカウントの所有者しか出来ないため)

Android keystoreとアップロード鍵の変更

次にkeystoreとアップロード鍵についてです。これに関してもCIで管理していたため変更が必要になりました。 私のプロジェクトのようにkeystoreをCIで管理しているプロジェクトもさほど多くないと思うので変更が必要になった方は結構少ないのではないかと思います。

まずはAndroidの公式ドキュメントを参考にkeystoreとアップロード鍵を再生成しました。 developer.android.com

上記手順で生成したファイルをサービスアカウントのJSONと同じようにbase64エンコードしてRELEASE_KEYSTORE_BASE64として再登録。

その他にkeystoreとアップロード鍵の生成時に設定した[key store password, key alias, key password]をそれぞれ

  • RELEASE_KEYSTORE_KEY_ALIAS
  • RELEASE_KEYSTORE_KEY_PASSWORD
  • RELEASE_KEYSTORE_PASSWORD

として登録し直しました。

登録したEnvironment Variablesはapp/build.gradleで

android {
        signingConfigs {
        release {
            if (System.getenv("RELEASE_KEYSTORE_BASE64") != null) {
                storeFile decodeFileFromBase64Env('RELEASE_KEYSTORE_BASE64', "tmp_ks_", ".jks")
                storePassword System.getenv('RELEASE_KEYSTORE_PASSWORD')
                keyAlias System.getenv('RELEASE_KEYSTORE_KEY_ALIAS')
                keyPassword System.getenv('RELEASE_KEYSTORE_KEY_PASSWORD')
            }
        }
    }
}

static decodeFileFromBase64Env(String name, String fileName, String ext) {
    String base64String = System.getenv(name)
    if (base64String == null) return null
    File tempFile = File.createTempFile(fileName, ext, File.createTempDir())
    FileOutputStream fos = null
    try {
        fos = new FileOutputStream(tempFile)
        fos.write(base64String.decodeBase64())
        fos.flush()
    } finally {
        if (fos != null) {
            fos.close()
        }
    }
    return tempFile
}

のような形でkeystoreとアップロード鍵を読み込んで署名しています。

Google Play Consoleに登録している各アプリのアップロード鍵のリセット申請

keystoreとアップロード鍵の変更に伴い、Google Play Consoleに登録しているアプリのアップロード鍵のリセット申請が必要になります。

先程の手順で生成したアップロード鍵のファイルを以下のコマンドで PEM 形式にエクスポートします

$ keytool -export -rfc -keystore upload-keystore.jks -alias upload -file upload_certificate.pem

※ upload-keystore.jks, upload, upload_certificate.pemは適宜置き換えてください。

Google Play Consoleで対象アプリを開いたら【設定】→【アプリの完全性】を開き、【鍵のアップグレードをリクエスト】をクリック。

事由を聞かれるので、【現在の鍵が不正使用されている】を選択し【次へ】を押下

サポートに問い合わせてほしい旨のメッセージが表示されるので、【サポートにお問合せ】を押下。

問い合わせ画面が表示されるので、各種アプリ情報を入力して【お客様のアプリは Google Play アプリ署名に登録されていますか?】で【はい、Google Play でアップロード鍵を管理しています】を選択

それ以降の選択はこのように選択。 f:id:sndyut:20210608061314p:plain

【フィードバックまたは問題の内容をご記入ください *】の項目に詳細内容を記入。

最後にpemファイルを添付して【送信】を実行。

数日経つとGoogle Play デベロッパー サポートからメールが届きます。

メールにいつから使用可能になるか記載されていたのでその日時以降にapkをアップロードできるかを試しました。

結果、問題なくアップロードできたためこれにて対応完了となりました。

最後に

Codecovセキュリティアップデート対応のAndroidの対応内容についてご紹介しました。

Androidの署名やCIの設定等は前任者が設定しており今回の対応をするまでは正直そこまで理解できていなかったので良い学びになりました。

もし似たような設定をされていたり、keystoreをCIで管理したいという方がいれば参考になると嬉しく思います。

Play Consoleの所有者権限に関してはちょっとした罠だなと思ったのでそこについては忘備録的にいつか書きたいと思います。

Codecovのセキュリティアップデートの対応~iOS編

コードのカバレッジの計測や可視化をするためにCodecovを使用しているのですが、4/30にCodecovからセキュリティの問題があったと公表したことを知らせるメールが届きました。

On Thursday, April 1st, we learned that someone had gained unauthorized access to our Bash Uploader script and modified it without our permission. The actor gained access because of an error in Codecov’s Docker image creation process that allowed the actor to extract the credential required to modify our Bash Uploader script.

Unfortunately, we can confirm that you were impacted by this security event.

We strongly recommend taking action by reading our security notice about this event to understand how you are impacted and next steps you should take.

about.codecov.io

概要を要約するとCIでCodecov使ってた場合に、CIで使ってる環境変数がもれた可能性があるのでCodecovのBash Uploader利用者はシークレット情報などを更新を推奨しますといった内容でした。

私が業務で携わってるプロジェクトはCircleCIを使用しているので、今回はiOSプロジェクトでどんな対応をしたのかについて書いていこうと思います。

対応内容

CircleCIのEnvironment Variablesに登録している以下のシークレット情報を更新しました。

  • FASTLANE_PASSWORDの変更

  • ASC_API_KEY_IDとASC_API_KEY_CONTENTの変更

  • MATCH_PASSWORDと証明書、プロファイルの変更

FASTLANE_PASSWORDの変更

FASTLANE_USERとセットでFASTLANE_PASSWORDを登録していますが、この設定項目はApple IDとApple IDのパスワードになります。

上記の秘匿情報が悪用された場合、Apple Store Connectやその他Apple関連のサービスに対してアクセスが可能になるため、公開しているアプリを削除されてしまったり、口座情報を知られてしまう等のリスクがあります。

そのため今回は、登録していたApple IDのパスワードを変更して対応しました。

ASC_API_KEY_IDとASC_API_KEY_CONTENTの変更

ASC_API_KEYはApp Store Connectにアクセスするために利用するAPI_KEYです。CircleCIでApp Store Connectの2ファクタ認証をパスするために利用してます。

これはAppleの公式ドキュメントを参考に新規作成して再設定しました。

developer.apple.com

これは今回の対応と関係ないのですが、App Store Connectの2ファクタ認証をパスするためのASC_API_KEYのFastlaneでの使用方法などはFastlaneにわかりやすいドキュメントがあるのでこちらを参照して私は実装しました。

docs.fastlane.tools

MATCH_PASSWORDと証明書、プロファイルの変更

私のチームではfastlane matchを使用して証明書とプロファイルを管理しています。

fastlane matchはチームで共通の証明書とプロファイルの作成や維持を行い、プライベートリポジトリに格納、そして全ての開発者間で共有できるようにするツールです。

docs.fastlane.tools

MATCH_PASSWORDが漏洩した場合、証明書が悪意ある利用をされるリスクがあるためMATCH_PASSWORDを変更しました。

MATCH_PASSWORDの変更は

fastlane match change_password 

のコマンドで変更可能です。このパスワードは任意のものが使えるので、1passwordで発行して設定しました。

MATCH_PASSWORD変更後、証明書とプロファイルも

fastlane match development
fastlane match adhoc
fastlane match appstore

で最設定しました。(証明書の更新期限も近かったのでタイミングとしてはちょうど良かったです。)

最後に

CIやFastlaneは一度設定するとしばらく触ることもなかったりするので「これ何に使ってるんだっけ」「このパスワードどこに保存してあるんだっけ」とか、「どうやって変更するんだっけ」みたいなものが結構あり大変でした。

Androidリポジトリも同様に対応が必要だったためAndroidのプロジェクトも対応しました。(こっちはさらに学びが多かった) その内容も後日書こうと思います。