GitHub Actions secrets の設定方法|環境変数を安全に渡す手順

※本記事にはプロモーション(広告・アフィリエイトリンク)を含みます。
GitHub Actions でワークフローを組むとき、APIキーやパスワードを YAML にそのまま書いてリポジトリに上げてしまうミスが起きやすい。secrets を使えばコードから機密情報を切り離して安全に渡せる。この記事では、secrets の登録からワークフローでの参照まで、つまずきやすい点を含めて手順を整理する。
secrets の種類と使い分け
GitHub Actions の secrets には3つのスコープがある。
| スコープ | 登録場所 | 適用範囲 |
|---|---|---|
| Repository secrets | リポジトリ設定 | そのリポジトリのワークフロー全体 |
| Environment secrets | リポジトリ → Environments | 指定 environment を使うジョブのみ |
| Organization secrets | Organization 設定 | 組織内の指定リポジトリ |
個人プロジェクトでは Repository secrets が最もシンプルに使える。ステージング・本番で異なる APIキーを使い分けたい場合は Environment secrets が適している。Organization secrets は複数リポジトリで共通のキーを一元管理したいときに使う。
Repository secrets の登録手順
UI から登録する(基本手順)
- リポジトリの Settings タブを開く
- 左メニューから Secrets and variables → Actions を選択
- New repository secret をクリック
- Name に識別子を入力(例:
PAYJP_SECRET_KEY) - Secret に値を貼り付けて Add secret で保存する
登録後は値の確認・コピーができない。更新(上書き)のみ可能なので、登録直後にパスワードマネージャーなどにも保存しておくと安全だ。
シークレット名のルール
公式ドキュメントによると、執筆時点では以下の制約がある。
- 英字・数字・アンダースコア(
_)のみ使用可 - 先頭は英字または
_にする GITHUB_で始まる名前は予約済みで使用不可- 大文字小文字は区別されない(
api_keyとAPI_KEYは同一扱い)
慣習として大文字スネークケース(DATABASE_URL)で統一しておくと、ワークフロー YAML を読んだときにシークレットだと一目でわかる。
GitHub CLI から登録する
複数の secrets をまとめて登録したい場合や、スクリプトで自動化したい場合は gh コマンドが使える。
# 対話プロンプトで入力する場合
gh secret set PAYJP_SECRET_KEY --repo owner/repo-name
# 標準入力から渡す場合
echo "sk_live_xxxx" | gh secret set PAYJP_SECRET_KEY --repo owner/repo-name
# .env ファイルを一括登録する場合
gh secret set --env-file .env.production --repo owner/repo-name
# 登録済みの一覧を確認(値は表示されない)
gh secret list --repo owner/repo-name
gh secret list は名前と更新日時のみを返す。値は一切表示されない。
ワークフロー YAML での参照方法
登録した secrets は ${{ secrets.シークレット名 }} の構文で参照する。一般的なパターンは env: ブロックで環境変数として渡す方法だ。
name: Deploy
on:
push:
branches:
- main
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: npm ci
- name: Build
run: npm run build
- name: Deploy to server
env:
PAYJP_SECRET_KEY: ${{ secrets.PAYJP_SECRET_KEY }}
DATABASE_URL: ${{ secrets.DATABASE_URL }}
run: ./scripts/deploy.sh
ジョブ全体で同じ secrets を複数ステップにわたって使う場合は、ジョブレベルの env: に書くとすっきりする。
jobs:
deploy:
runs-on: ubuntu-latest
env:
PAYJP_SECRET_KEY: ${{ secrets.PAYJP_SECRET_KEY }}
steps:
- name: Check payment config
run: node scripts/check-payment.js
- name: Deploy
run: node scripts/deploy.js
ログのマスキング
ワークフローのログでシークレットの値が含まれる出力は自動的に *** に置き換えられる。ただし公式ドキュメントでは「シークレットを意図的にエコー・出力しないこと」が推奨されている。base64 エンコードや URL エンコードなど変換を経由した値はマスクが効かない場合があるためだ。
Environment secrets の設定手順
デプロイ先ごとに異なる値を使いたい場合は Environment secrets を使う。ステージング環境と本番環境で別々の APIキーを割り当てたいケースが典型的だ。
1. Environment を作成する
- Settings → Environments → New environment
- 名前を入力(例:
production、staging) - 必要に応じて Required reviewers や Deployment branches などの protection rules を設定する
2. Environment secrets を登録する
Environment 詳細画面の Add secret から登録する。操作は Repository secrets と同じ手順だ。
3. ワークフロー側で environment を指定する
jobs:
deploy-production:
runs-on: ubuntu-latest
environment: production # ← ここで Environment 名を指定
steps:
- name: Deploy
env:
PAYJP_SECRET_KEY: ${{ secrets.PAYJP_SECRET_KEY }}
run: ./scripts/deploy.sh
environment: を指定したジョブでは Environment secrets が Repository secrets より優先される。同名のシークレットが両方にある場合は Environment secrets の値が使われる。
つまずきやすいポイント
フォーク PR では secrets が渡されない
フォークされたリポジトリからの pull_request トリガーでは、セキュリティ上の理由から secrets がワークフローに渡されない。これは仕様だ。pull_request_target トリガーで対応できる場合もあるが、権限昇格のリスクがあるため公式ドキュメントをよく確認してから使うべきだ。
secrets の上限
公式ドキュメントによると、執筆時点の上限は以下の通り。
| スコープ | 上限 |
|---|---|
| リポジトリ | 100個 |
| 環境(Environment) | 100個 |
| Organization | 1,000個 |
| 1件のサイズ | 64 KB |
JSON キーファイルや PEM 証明書のような大きなファイルを格納するときは、base64 エンコードして登録するパターンが使われる。
# ファイルを base64 エンコードして登録
base64 -w 0 service-account.json | gh secret set GCP_SA_KEY --repo owner/repo-name
# ワークフロー内でデコードして使う
- name: Decode service account key
run: echo "${{ secrets.GCP_SA_KEY }}" | base64 -d > /tmp/sa-key.json
vars コンテキストと使い分ける
GitHub Actions には secrets のほかに Variables(vars コンテキスト)がある。Variables は値が暗号化されず、UI 上から確認・編集できる。
| secrets | vars(Variables) | |
|---|---|---|
| 暗号化 | される | されない |
| UI での値確認 | 不可 | 可 |
| 主な用途 | APIキー・パスワード | 非機密の設定値 |
env:
NEXT_PUBLIC_SITE_URL: ${{ vars.NEXT_PUBLIC_SITE_URL }} # 非機密 → vars
PAYJP_SECRET_KEY: ${{ secrets.PAYJP_SECRET_KEY }} # 機密 → secrets
「非公開にしたいが暗号化は不要な設定値」(サイト URL、ビルドオプションなど)は vars を使うとワークフローが整理しやすい。機密情報は必ず secrets に入れる、という区分けを徹底するだけで管理コストが下がる。
まとめ
- secrets は Repository / Environment / Organization の3スコープで管理する
- 登録は Settings の UI または
gh secret setで行い、登録後は値を確認できない - ワークフローからは
${{ secrets.NAME }}で参照し、env:ブロックで環境変数に割り当てる - フォーク PR では secrets が渡されない仕様に注意する
- 非機密の設定値は
varsコンテキストと使い分けるとワークフローがすっきりする
まずリポジトリの Settings → Secrets and variables → Actions を開き、テスト用に1つ登録してみるところから始めてほしい。.github/workflows/test.yml に最小構成のワークフローを作り、run: echo "Value=${{ secrets.TEST_SECRET }}" のログが Value=*** と表示されれば、マスキングを含む基本の流れが確認できる。
よくある質問
GitHub Actions の secrets に登録した値は後から確認できますか?
確認・コピーはできません。更新(上書き)のみ可能です。登録後に値が必要になるケースに備え、パスワードマネージャーや安全な社内ストレージにも同じ値を保存しておくことをおすすめします。
Repository secrets と Environment secrets が同じ名前の場合、どちらが優先されますか?
ジョブに `environment:` を指定している場合は Environment secrets が優先されます。`environment:` を指定していないジョブでは Repository secrets が使われます。同名のシークレットを両方に登録する場合は、この優先順位を意識して設計してください。
secrets に 64 KB を超えるファイルを登録したい場合はどうすればよいですか?
ファイルを base64 エンコードした文字列として登録する方法がよく使われます。ただし元ファイルが大きい場合はエンコード後も上限を超える可能性があります。その場合は外部のシークレット管理サービス(AWS Secrets Manager や HashiCorp Vault など)を組み合わせる設計を検討してください。
secrets をワークフローで参照したのに空文字になる場合、何を確認すればよいですか?
シークレット名のスペルミスが最もよくある原因です。次に、フォーク PR から実行していないか、Environment secrets を使っている場合はジョブに `environment:` が正しく指定されているかを確認してください。`gh secret list` でリポジトリに登録されている名前を確認するのが確実です。