ミツカリ技術ブログ

株式会社ミツカリの開発チームのブログです

Github Actions(ワークフロー)で無闇にSecretsを定義してはいけない話

ミツカリのたなしゅん(@tanashun_dev)です。

Github ActionsでCI/CDを構築する際、ビルドやデプロイ時に秘匿情報を環境変数として渡す場面がありますよね。

例えば以下のような形です。

jobs:
  jobA:
    runs-on: ubuntu-latest
    steps:
      - uses: docker/build-push-action@v6.18.0
        with:
          context: .
          build-args: |
            "SOME_SECRET=hogehoge"

これだと秘匿情報がハードコーディングされてしまっていてgithubのソースコードが流出したら大問題です。

そこで、Github Actionsにはsecretsという秘匿情報管理があるのでそちらに値を登録して、secretsを参照するようにします。

jobs:
  jobA:
    runs-on: ubuntu-latest
    steps:
      - uses: docker/build-push-action@v6.18.0
        with:
          context: .
          build-args: |
            "SOME_SECRET=${{ secrets. SAMPLE_SECRET }}"

これでソースコードが誤って公開されてしまっても秘匿情報は流出しなくなりました。

ちなみに過去、githubの障害でprivateリポジトリの情報が流出する事故も実際にあったので、privateリポジトリであっても秘匿情報の扱いには気をつけなければなりません。

forest.watch.impress.co.jp

実際に、secretsを参照したものをecho等で出力しようとしてもlogからマスクされるようになっています。

jobs:
  jobA:
    runs-on: ubuntu-latest
    steps:
      - name: Some
        run: |
          echo "This is job A"
          echo "Secret: ${{ secrets.SAMPLE_SECRET }}"
This is job A
Secret: ***

実際にgithub actionsで実行したログ

さて、本題ですが、このsecrets周りでひとつハマった点があったので紹介します。

先ほどの例の通り、secrets情報はlogからマスクされます。

実はマスクされるのはlogだけではありません。

jobやstep間での受け渡しとなるGITHUB_OUTPUTSでもマスクされてしまいます。

こちらのサンプルをご覧ください。

jobs:
  jobA:
    runs-on: ubuntu-latest
    outputs:
      a_out: ${{ steps.secret_output.outputs.a_out }}
    steps:
      - name: Secret Output
        id: secret_output
        run: |
          echo "a_out=${{ secrets.SAMPLE_SECRET }}" >> $GITHUB_OUTPUT
          
  jobB:
    runs-on: ubuntu-latest
    needs: jobA
    steps:
      - name: Get Secret from Job A
        run: |
          echo "Secret: ${{ needs.jobA.outputs.a_out }}"
Secret: 

実際にgithub actionsで実行したログ

jobAでoutputしたa_outにsecretsを設定しているため、jobBでa_outが見えなくなっています。

次のサンプルもご覧ください。

jobs:
  jobA:
    runs-on: ubuntu-latest
    outputs:
      a_out: ${{ steps.secret_output.outputs.a_out }}
    steps:
      - name: Secret Output
        id: secret_output
        run: |
          echo "JobA Secret ${{ secrets.SAMPLE_SECRET }}"
          echo "a_out=hogefuga" >> $GITHUB_OUTPUT
          
  jobB:
    runs-on: ubuntu-latest
    needs: jobA
    steps:
      - name: Get Secret from Job A
        run: |
          echo "Secret: ${{ needs.jobA.outputs.a_out }}"
JobA Secret ***
Secret: 

実際にgithub actionsで実行したログ

今度はa_outにはsecretsを入れていませんが、なぜかjobBで見つけられません。

実は、a_outにハードコーディングしているhogefugaという文字列と同じ文字列がsecretsのSAMPLE_SECRETの値になっています。

githubはsecretsをマスクするというのは正しいようで正しくありません。

正しくは、secretsの値と同値の文字列をすべてマスクします。

通常業務で使用されるような英数記号を含むようなパスワード文字列であれば同値が別で現れることはないと思いますが、秘匿性の薄い情報をなんとなくsecretsにしてしまうと痛い目を見ることがわかりました。

今回のように自分で定義したrunで発生した場合は上記のようにskipアラートが出ていますが、実際に私が業務中に出くわした場面ではひとつ目の例として上げたようなdockerビルドのusesによる呼び出し先で発生していたものでした。

その場合は上記のようなアラートは特に出ておらず、原因が全くわかりませんでした。

ログを見てマスクされる必要のない文字列がなぜかマスクされていることに気付き、今回紹介した原因にたどり着くことができましたが、少し時間がかかってしまいました。

この情報が誰かの助けになれば幸いです。

ちなみにこの仕様については公式Docには特に記載がありませんでした。いわゆるパスワードっぽい文字列であればこの現象に出くわすことも稀なので特に問い合わせとかもないのかもしれません。

参考

docs.github.com

docs.github.com


現在、ミツカリではITエンジニアを募集しています。興味のある方はぜひお気軽にご連絡ください!

herp.careers