2026年7月13日
2026年7月13日
GitHub Actionsのシークレット管理方法|登録・参照・漏えい対策とOIDCの使いどころ
GitHub Actions で API キーや SSH 鍵、デプロイ先のパスワードを扱うときは、ワークフローの YAML に直接書かず「シークレット」として登録し、${{ secrets.名前 }} で参照します。この記事では、シークレットの種類(リポジトリ・環境・組織)と使い分け、登録と参照の方法、ログへの漏えいを防ぐ書き方、フォークからのプルリクエストでの扱い、そして長期間有効な鍵そのものを持たずに済む OIDC 連携までを説明します。
シークレットを使った具体的なデプロイの流れは「CI/CDパイプラインを構築」の記事で扱っているので、本記事では「シークレットをどう管理するか」に絞ります。
シークレットの種類と使い分け
GitHub Actions のシークレットには、保存する場所によって次の種類があります。
- リポジトリのシークレット: そのリポジトリのすべてのワークフローから参照できます。小規模なプロジェクトではこれで足りることが多いです。
- 環境(Environment)のシークレット:
productionやstagingなどの環境ごとに登録し、ジョブでenvironment:を指定したときだけ参照できます。承認者の必須化やデプロイできるブランチの制限といった保護ルールと組み合わせられるため、本番の認証情報はここに置くのが向いています。 - 組織のシークレット: 組織内の複数のリポジトリで共有できます。どのリポジトリに公開するかを選べるので、共有範囲は必要なリポジトリだけに絞ります。
同じ名前のシークレットが複数の階層にある場合は、より狭い範囲(環境 → リポジトリ → 組織の順)の値が優先されます。
なお、Dependabot が作成したプルリクエストで動くワークフローには、通常の Actions のシークレットは渡されません。Dependabot 用のシークレットを別に登録する必要があります。保護ルールなど一部の機能はリポジトリの公開・非公開やプランによって使える範囲が異なるため、GitHub の公式ドキュメントで確認してください(2026年10月現在)。
シークレットとして登録すべきもの、しないもの
シークレットに入れるのは、漏れると困る値です。API キー、アクセストークン、SSH の秘密鍵、データベースのパスワードなどが該当します。
一方、接続先のドメイン名や、公開しても問題のない設定値は、シークレットではなく「変数(Variables)」として登録し、${{ vars.名前 }} で参照します。変数はログにそのまま表示されるため、デバッグがしやすくなります。何でもシークレットにすると、ログが伏せ字だらけになって原因を追いにくくなります。
もう一つの注意点は、JSON のような構造化データを丸ごと1つのシークレットにしないことです。ログの伏せ字はシークレットの値と一致する文字列に対して行われるため、ツールが JSON の一部だけを出力すると伏せ字にならない場合があります。値ごとに別のシークレットとして登録するほうが安全です。
登録する方法
ブラウザでは、リポジトリの Settings から「Secrets and variables」の Actions を開いて登録します(画面の表記は変わることがあります)。一度登録した値は画面から読み出せず、上書きか削除しかできません。
GitHub CLI(gh)を使うと、ターミナルから登録できます。
# リポジトリのシークレットを登録(値は対話的に入力)
gh secret set DEPLOY_API_KEY
# ファイルの内容を登録(SSH の秘密鍵など)
gh secret set SSH_PRIVATE_KEY < ./deploy_key
# 環境のシークレットとして登録
gh secret set DEPLOY_API_KEY --env production
# 登録済みの名前を一覧表示(値は表示されない)
gh secret list
コマンドの引数に値を直接書く方法もありますが、シェルの履歴に値が残るため、対話入力かファイルからの読み込みを使います。シークレット名には英数字とアンダースコアが使え、GITHUB_ で始まる名前は予約されているため使えません。
ワークフローから参照する
シークレットは secrets コンテキストで参照します。おすすめは、ステップの env: で環境変数に入れてから使う書き方です。
jobs:
notify:
runs-on: ubuntu-latest
environment: production
steps:
- name: 外部 API を呼び出す
env:
API_KEY: ${{ secrets.DEPLOY_API_KEY }}
run: |
curl -fsS -H "Authorization: Bearer $API_KEY" https://api.example.com/deploy
run: の中に ${{ secrets.DEPLOY_API_KEY }} を直接埋め込むと、ワークフローの実行前に値がスクリプトへ展開されるため、値に引用符などの記号が含まれているとシェルの解釈が崩れることがあります。環境変数経由ならその心配がありません。
また、シークレットは if: の条件で直接参照できません。「シークレットが登録されているときだけ実行する」といった判定をしたい場合は、ジョブやステップの env: に入れたうえで、その環境変数が空かどうかを判定します。
env:
API_KEY: ${{ secrets.DEPLOY_API_KEY }}
steps:
- name: キーがあるときだけ実行
if: env.API_KEY != ''
run: ./scripts/deploy.sh
ログへの漏えいを防ぐ
GitHub Actions は、ログに出力された文字列のうちシークレットの値と一致する部分を *** に置き換えます。ただし、この仕組みは万能ではありません。
- 値を Base64 などに変換したもの、一部だけを切り出したものは、元の値と一致しないため伏せ字になりません。
set -xでシェルのコマンドを表示させたり、envやprintenvで環境変数を一覧したりすると、意図せず値が出力される経路が増えます。- シークレットから別の値(一時トークンなど)を生成した場合は、その値は自動では伏せ字になりません。
生成した値を伏せ字にしたいときは、ワークフローコマンドの ::add-mask:: を使います。
TEMP_TOKEN="$(./scripts/get-temp-token.sh)"
echo "::add-mask::$TEMP_TOKEN"
さらに、シークレットを成果物(artifact)やキャッシュ、Docker イメージに含めないことも大切です。これらはログの伏せ字の対象外で、取得できる人に中身を読まれます。
フォークからのプルリクエストと pull_request_target
公開リポジトリでは、外部の人がフォークからプルリクエストを送ってきます。pull_request イベントで動くワークフローでは、フォークからのプルリクエストに対してシークレットが渡されず、GITHUB_TOKEN も読み取り中心の権限に制限されます。これは、悪意のあるコードを含むプルリクエストがシークレットを盗み出すのを防ぐための仕組みです。
これを回避しようとして pull_request_target イベントを使う例がありますが、注意が必要です。このイベントはベースリポジトリの権限とシークレットで動くため、プルリクエスト側のコードをチェックアウトして実行すると、外部の人が書いたコードがシークレットにアクセスできてしまいます。pull_request_target を使うのは、ラベル付けやコメントのようにプルリクエストのコードを実行しない処理に限るのが基本です。
あわせて、ワークフロー全体またはジョブごとに permissions: で GITHUB_TOKEN の権限を必要最小限に絞っておくと、万一の場合の被害を小さくできます。
長期の鍵を持たない:OIDC 連携
AWS、Google Cloud、Azure などのクラウドにデプロイする場合は、アクセスキーをシークレットに保存する代わりに、OpenID Connect(OIDC)による連携を検討してください。ワークフローが実行のたびに GitHub から短時間だけ有効なトークンを受け取り、クラウド側でそれを検証して一時的な権限を与える仕組みです。長期間有効な鍵がどこにも保存されないため、漏えいやローテーション忘れのリスクを減らせます。
permissions:
id-token: write
contents: read
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789012:role/your-deploy-role
aws-region: ap-northeast-1
id-token: write が OIDC のトークンを発行するための権限です。クラウド側では、信頼するリポジトリやブランチ、環境を条件として指定し、どのワークフローからでも引き受けられる設定にならないよう絞り込みます。設定方法は各クラウドと GitHub の公式ドキュメントに手順があり、アクションのバージョンも更新されるので確認してから使ってください。上の ARN はダミーです。
定期的な見直しと漏えい時の対応
シークレットは登録して終わりではありません。担当者が変わったとき、プロジェクトが終わったとき、不要になったときに見直し、使っていないものは削除します。API キーやトークンには有効期限を設定できるものが多いので、可能なら短めの期限にします。
万一、シークレットがログやコミットに出てしまった場合は、まず発行元(クラウドや API の管理画面)でその鍵を無効化し、新しい鍵を発行して登録し直します。コミット履歴から削除するだけでは、すでに複製された可能性を消せないためです。リポジトリへの秘密情報のコミットを防ぐには、GitHub のシークレットスキャンやプッシュ保護、CI でのスキャンツールの併用が有効です。
まとめ
- 秘密の値はシークレット、公開してよい設定値は変数(
vars)に分けて登録します。 - 本番の認証情報は環境のシークレットに置き、保護ルールと組み合わせます。
- 参照は
env:経由で行い、if:では環境変数を介して判定します。 - ログの伏せ字は一致した文字列だけが対象です。派生値は
::add-mask::で伏せ、成果物やイメージにシークレットを含めません。 - フォークからのプルリクエストにはシークレットが渡されません。
pull_request_targetでプルリクエストのコードを実行しないでください。 - クラウドへのデプロイは OIDC で長期の鍵を持たない構成を検討し、漏えい時は発行元で無効化します。