言語を切り替える
テーマを切り替える

GitHub Actions Secrets 管理:漏洩リスクから OIDC による秘密鍵レスデプロイまで

2025年3月のある週末、GitHub セキュリティチームは 23,000 以上のリポジトリの所有者に緊急メールを送信しました。

彼らの secrets が漏洩した可能性があるという内容です。

犯人は tj-actions/changed-files という action でした。広く使われていたこのツールが攻撃者に侵害され、ワークフロー内のすべての環境変数と secrets を密かに窃取していました。「自分のプロジェクトでもこの action を使っているけど、大丈夫だろうか?」と思われた方もいるかもしれません。

正直なところ、この事件は多くの人が見過ごしていた問題を浮き彫りにしました。GitHub Actions 内の secrets は、一体どう管理すべきなのか?

この記事では、3つのことをお話しします。secrets の3層アーキテクチャの選び方、8つの安全鉄則の守り方、そして OIDC を使って静的クレデンシャル漏洩の悪夢から完全に解放される方法です。

一、GitHub Actions Secrets の3層アーキテクチャ

GitHub は secrets を保存するために3つのレベルを提供しています:Repository、Environment、Organization。どれを選ぶべきか?答えは「あなたのユースケース次第」です。

Repository Secrets:個人プロジェクトの首选

最もシンプルなレベルです。secrets はリポジトリレベルで保存され、すべてのワークフローがアクセスできます。個人プロジェクト、モノリポジトリ、複数環境デプロイの要件がない場合は、これだけで十分です。

唯一の欠点は、staging と production の同名 secret を区別できないことです。例えば DATABASE_URL という secret がある場合、staging と production で値が異なります。どうすればよいのでしょうか?ここで Environment Secrets が必要になります。

Environment Secrets:複数環境デプロイの必需品

Environment secrets は環境ごとに分離でき、承認フローもサポートしています。GitHub リポジトリの設定で stagingproduction の2つの環境を作成し、それぞれ異なる secrets を設定できます。

重要な特徴は、その環境を参照するジョブだけが対応する secrets にアクセスできることです。これにより、セキュリティ境界がより明確になります。

jobs:
  deploy-staging:
    runs-on: ubuntu-latest
    environment: staging  # staging 環境を参照
    steps:
      - run: echo "Deploying to staging..."
      - env:
          API_KEY: ${{ secrets.API_KEY }}  # staging 環境の secret

  deploy-production:
    runs-on: ubuntu-latest
    environment: production  # production 環境を参照(承認設定可能)
    steps:
      - run: echo "Deploying to production..."
      - env:
          API_KEY: ${{ secrets.API_KEY }}  # production 環境の secret

上記の設定では、deploy-stagingstaging 環境の secrets にしかアクセスできず、deploy-productionproduction 環境の secrets にしかアクセスできません。両者は互いに干渉しません。

また、Environment は「保護ルール」もサポートしています。例えば production 環境では、人的承認を経ないと実行できないように設定できます。これはチームでのコラボレーション時に特に有用です。

Organization Secrets:チーム共有、統一管理

もしチームに数十のリポジトリがあり、各リポジトリに同じ AWS_ACCESS_KEY を設定する必要があるとします。何十回もコピペして、更新時はまた何十回も修正する——考えるだけで頭が痛くなります。

Organization secrets はこの問題を解決するために存在します。組織レベルで一度設定すれば、すべてのリポジトリで使用できます。また、どのリポジトリがアクセスできるかを制御できます:すべてのリポジトリ、または指定したリポジトリリスト。

# リポジトリのワークフローでは、repository secrets と全く同じように使用
steps:
  - env:
      AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
      AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

どう選ぶ?

シンプルに言うと:

シナリオ推奨レベル
個人プロジェクト、単一環境Repository Secrets
複数環境デプロイ(staging/production)Environment Secrets
チーム複数リポジトリ、共有クレデンシャルOrganization Secrets

私の経験では、多くのプロジェクトは最初 Repository Secrets を使用し、複数環境の要件が出てから Environment Secrets に移行します。移行コストは高くありませんが、事前に計画することをお勧めします。

二、Secrets セキュリティベストプラクティス — 8つの鉄則

ここまで secrets の保存方法を説明しました。ここからは使い方についてお話しします。実践から学んだ8つの鉄則をまとめました。それぞれに教訓があります。

1. 命名規則を守る

すべて大文字 + アンダースコア区切り。例:AWS_ACCESS_KEY_ID。小文字やキャメルケースは使わないでください。理由はシンプルです。一目で secret とわかり、普通の変数と混同しません。

2. JSON を1つの secret に保存しない

これは典型的な落とし穴です。設定ファイル全体を1つの secret に詰め込む人がいます:

{"api_key": "xxx", "db_url": "yyy", "token": "zzz"}

そしてワークフロー内で fromJson で解析します。問題は、この secret が漏洩すると、すべての機密情報が一緒に漏洩することです。正しい方法は、各値を独立した secret として保存することです。

3. 明示的に受け渡す、インラインにしない

# 悪い例 ❌
- run: my-cli --token ${{ secrets.MY_TOKEN }}

# 良い例 ✅
- env:
    MY_TOKEN: ${{ secrets.MY_TOKEN }}
  run: my-cli --token $MY_TOKEN

なぜでしょうか?GitGuardian の研究によると、コマンドライン引数は同じマシン上の他のプロセスから ps x -w で見ることができます。環境変数の方がはるかに安全です。

4. 定期的にローテーションする

30〜90日ごとに変更します。ローテーションは面倒だとわかっています。しかし、漏洩後の対応と比べれば、この手間は本当に大したことありません。Blacksmith チームの提案では、クラウドサービス(AWS/GCP)を使用している場合、OIDC と組み合わせることでこの手順を完全にスキップできます。

5. Actions を SHA に Pin する

サプライチェーン攻撃の最初の防衛ラインです。

# 悪い例 ❌
- uses: tj-actions/changed-files@v45

# 良い例 ✅
- uses: tj-actions/changed-files@b827595e0a7e97537d7c7a2f458b5a8e6d5c8e39

バージョン番号タグではなく、commit SHA を使用してください。タグは攻撃者によって改ざんされる可能性がありますが、SHA は不変です。

6. GITHUB_TOKEN には最小権限のみを付与する

GitHub は各ワークフローに自動的に GITHUB_TOKEN を提供します。デフォルトの権限は高すぎます——コードを書ける、issue を変更できる。ワークフローまたはリポジトリ設定で read-only に変更することをお勧めします:

permissions:
  contents: read

7. ログで secrets が正しくマスキングされているか確認する

GitHub は自動的に ${{ secrets.XXX }} をログ内で *** に置換します。しかし、このように書くと:

- run: echo "Token is $MY_TOKEN"

ログに実際の token 値が表示されます。ワークフローをテストして、予期しない露出がないことを確認してください。

8. 派生した機密値を登録する

ワークフローが1つの secret から新しい機密値を生成した場合(例:API key で JWT を生成)、その新しい値も secret として登録してください。メモリ内でのみ受け渡さないでください。


この8つの鉄則は単なる理論ではありません。tj-actions 事件後、StepSecurity チームが数千の公開リポジトリを監査したところ、これらのルールに違反しているリポジトリが相当な割合を占めていました。修正は難しくありませんが、時間をかけて1つずつ確認する必要があります。

三、OIDC — 秘密鍵レス時代のクラウドデプロイ認証

前の2つの鉄則で「secrets の定期的なローテーション」に触れました。正直なところ、ローテーションは本当に面倒です——毎回 AWS コンソールで手動変更、GitHub secrets の更新、チームメンバーへの通知が必要です。

OIDC(OpenID Connect)は1つの解決策を提供します:そもそも secrets を保存しない

OIDC はどう動作するのか?

従来の方法:AWS で IAM ユーザーを作成し、access key を生成し、その key を GitHub secrets に保存します。ワークフローが実行されるたびに、この静的 key を使って AWS リソースにアクセスします。

OIDC の方法:GitHub がアイデンティティプロバイダーとして、AWS に「このワークフローは eastondev/my-repo リポジトリから来ている」ことを証明します。AWS は検証後、短期 JWT トークン(有効期限は数分から数時間)を発行します。ワークフローはこの一時トークンを使ってタスクを完了し、トークンは期限切れ後に自動的に無効化されます。

長期クレデンシャルを保存する必要がなく、ローテーションも不要で、漏洩リスクもありません。

AWS OIDC 設定例

手順は2つの部分に分かれます:AWS 側で信頼関係を設定し、GitHub 側でトークンをリクエストします。

AWS 側(コンソール操作):

  1. IAM Identity Provider を作成、URL を https://token.actions.githubusercontent.com に設定
  2. IAM Role を作成、信頼ポリシーをリポジトリに制限:
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {
      "Federated": "arn:aws:iam::123456789:oidc-provider/token.actions.githubusercontent.com"
    },
    "Action": "sts:AssumeRoleWithWebIdentity",
    "Condition": {
      "StringEquals": {
        "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
        "token.actions.githubusercontent.com:sub": "repo:eastondev/my-repo:ref:refs/heads/main"
      }
    }
  }]
}

この設定の意味は、eastondev/my-repo リポジトリの main ブランチだけがこの role を assume できるということです。

GitHub 側(ワークフロー設定):

jobs:
  deploy:
    runs-on: ubuntu-latest
    permissions:
      id-token: write  # 必須:OIDC トークンをリクエスト
      contents: read
    steps:
      - uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: arn:aws:iam::123456789:role/GitHubActionsRole
          aws-region: us-east-1
      - run: aws s3 sync ./dist s3://my-bucket

ここには secrets.AWS_ACCESS_KEY が一切ないことに注目してください。直接 role で認証しています。

GCP と Azure

3大クラウドはすべて OIDC をサポートしており、設定は似ています:

クラウドプラットフォームGitHub Action公式ドキュメントのキーワード
AWSaws-actions/configure-aws-credentialsOIDC federation
GCPgoogle-github-actions/authWorkload Identity Federation
Azureazure/loginFederated Identity Credentials

OIDC の意外なメリット

johal.in のテストによると、OIDC 認証のレイテンシーは従来の secrets 方式より約 87% 低減されました。理由はシンプルです。GitHub secrets API からクレデンシャルを読み取る必要がなく、ローカル JWT から直接一時トークンを取得できます。

私個人の使用経験から言うと、OIDC はクラウドデプロイの首选ソリューションです。唯一のハードルは設定が少し複雑なことですが、一度設定すれば後は完全に放置できます。

四、サプライチェーン攻撃対策 — tj-actions 事件の振り返り

冒頭で触れた tj-actions/changed-files 事件に戻りましょう。どのように発生したのか?何を学べるのか?

事件の経緯

2025年3月、攻撃者が tj-actions リポジトリの maintainer 権限を取得しました(具体的な経路は調査中ですが、クレデンシャル漏洩またはアカウント侵害の可能性があります)。彼らは v45 バージョンのコードに悪意のあるスクリプトを挿入し、このスクリプトはワークフロー実行時に密かにすべての環境変数と secrets を読み取り、攻撃者が管理するサーバーに送信しました。

Semgrep の分析によると、tj-actions/changed-files@v45 を使用しているすべてのワークフローが影響を受けました——GitHub secrets でも OIDC でも、この action が環境変数にアクセスできれば窃取されます。

Unit42 Palo Alto のレポートでは、23,000 以上のリポジトリがこの action を使用していたことが指摘されています。有名なプロジェクトの fork も含まれていました。

教訓

この事件はいくつかの問題を露呈しました:

  1. Action のバージョン番号タグは信頼できない——攻撃者はタグを改ざんして悪意のある commit を指すことができる
  2. サードパーティ action はあなたの secrets にアクセスできる——侵害されると、すべての secrets が露出する
  3. 過去の実行ログは機密情報を漏洩している可能性がある——現在修正しても、過去の実行記録には痕跡が残っている可能性がある

チェックリスト

tj-actions/changed-files を使用したことがある場合、以下の項目を1つずつ確認することをお勧めします:

□ ワークフローログを確認し、secrets が出力に漏洩していないことを確認
□ 漏洩した可能性のある secrets をすべてローテーション(API keys、tokens など)
□ action を commit SHA に pin し、バージョン番号ではなく
□ 他のサードパーティ action の maintainer の出所を監査
□ Dependabot または Renovate で action バージョンの自動チェックを検討

まだ侵害されていないプロジェクトにとって、Pin SHA が最も重要な防御策です。SHA はバージョン番号より読みにくいですが、タグ改ざんを防ぐ唯一の方法です。

また、GitHub は2026年のセキュリティロードマップで重要な方向性を言及しています:コード貢献権限とクレデンシャル管理権限の分離。将来的には、より詳細なアクセス制御が導入され、サードパーティ action が必要な secrets にしかアクセスできないようになる可能性があります。これは良いニュースですが、現時点では自分で防衛線を守る必要があります。

結論

GitHub Actions Secrets 管理は複雑な技術問題ではなく、継続的な注意が必要なセキュリティプラクティスです。まとめると:

3層アーキテクチャの選び方:個人プロジェクトは Repository Secrets、複数環境デプロイは Environment Secrets、チーム共有は Organization Secrets。

安全鉄則の守り方:Pin SHA、明示的な受け渡し、定期的なローテーション——この3つが最も重要です。

クラウドデプロイの認証方法:OIDC が首选、secrets 保存なし、ローテーションの悩みなし。

サプライチェーン攻撃の防ぎ方:サードパーティ action の secrets アクセスを制限、maintainer の出所を監査。

tj-actions 事件から教訓を得た後、私の実践はこうです:すべての action を SHA に pin、クラウドデプロイはすべて OIDC、毎月1回 secrets 監査。このプロセスの構築には約2週間かかりましたが、その後のメンテナンスコストは非常に低いです。

まだこれらのチェックを始めていない方は、今日この記事のチェックリストを実行することをお勧めします。事後対応と比べれば、事前予防のコストははるかに低いです。

GitHub Actions OIDC 秘密鍵レスデプロイの設定

AWS クラウドサービスに OIDC 認証を設定し、静的クレデンシャルを保存せずに安全なデプロイを実現

⏱️ 目安時間: 30 分

  1. 1

    ステップ1: IAM Identity Provider を作成

    AWS IAM コンソールでアイデンティティプロバイダーを作成:

    • Provider URL: https://token.actions.githubusercontent.com
    • Audience: sts.amazonaws.com
    • 生成された Provider ARN を記録
  2. 2

    ステップ2: IAM Role を作成し信頼ポリシーを設定

    IAM Role を作成し、信頼ポリシーを GitHub リポジトリに制限:

    ```json
    {
    "Version": "2012-10-17",
    "Statement": [{
    "Effect": "Allow",
    "Principal": {
    "Federated": "arn:aws:iam::ACCOUNT_ID:oidc-provider/token.actions.githubusercontent.com"
    },
    "Action": "sts:AssumeRoleWithWebIdentity",
    "Condition": {
    "StringEquals": {
    "token.actions.githubusercontent.com:aud": "sts.amazonaws.com",
    "token.actions.githubusercontent.com:sub": "repo:OWNER/REPO:ref:refs/heads/main"
    }
    }
    }]
    }
    ```

    • ACCOUNT_ID を自分の AWS アカウント ID に置換
    • OWNER/REPO を自分の GitHub リポジトリに置換
    • :ref:refs/heads/main を削除してすべてのブランチを許可可能
  3. 3

    ステップ3: Workflow で OIDC を使用するように設定

    GitHub Actions ワークフローで OIDC トークンをリクエスト:

    ```yaml
    jobs:
    deploy:
    runs-on: ubuntu-latest
    permissions:
    id-token: write # 必須:OIDC トークンをリクエスト
    contents: read
    steps:
    - uses: aws-actions/configure-aws-credentials@v4
    with:
    role-to-assume: arn:aws:iam::ACCOUNT_ID:role/GitHubActionsRole
    aws-region: us-east-1
    - run: aws s3 sync ./dist s3://my-bucket
    ```

    • id-token: write は必須の権限宣言
    • secrets.AWS_ACCESS_KEY_ID の設定は不要
  4. 4

    ステップ4: テストと検証

    ワークフローを実行して OIDC 設定を検証:

    • ワークフローログを確認し、認証成功を確認
    • role-to-assume が正しいことを確認
    • 静的クレデンシャルなしで AWS リソースにアクセスできることを確認
    • ターゲット操作(S3 同期、ECR プッシュなど)をテスト

FAQ

Repository Secrets と Environment Secrets の違いは何ですか?
Repository Secrets はリポジトリレベルで保存され、すべてのワークフローがアクセスできます。Environment Secrets は環境ごとに分離され、その環境を参照するジョブだけが対応する secrets にアクセスでき、承認フローもサポートしています。複数環境デプロイには Environment Secrets の使用をお勧めします。
ワークフローで安全に secrets を使用するにはどうすればよいですか?
3つの原則に従ってください:

• 明示的な受け渡し:env フィールドで注入し、インラインで ${{ secrets.XXX }} を使用しない
• 最小権限:必要なステップにのみ受け渡し、GITHUB_TOKEN 使用時は permissions: contents: read を設定
• 定期的なローテーション:30〜90日ごとのローテーションを推奨、クラウドデプロイでは OIDC を使用してローテーションを完全にスキップ可能
OIDC とは何ですか?従来の secrets より安全な理由は?
OIDC(OpenID Connect)は GitHub をアイデンティティプロバイダーとしてクラウドプラットフォームにワークフローの身元を証明させ、クラウドプラットフォームが短期 JWT トークンを発行します。利点:静的クレデンシャルの保存不要、ローテーション不要、漏洩リスクなし。認証レイテンシーは従来の secrets 方式より約 87% 低減されます。
サプライチェーン攻撃(tj-actions 事件など)を防ぐにはどうすればよいですか?
核心的な防御策:

• Actions を commit SHA に pin し、バージョン番号タグを使用しない
• secrets を信頼できる actions にのみ受け渡すよう制限
• サードパーティ action の maintainer の出所を監査
• 定期的に Dependabot または Renovate でバージョン更新をチェック
secrets はログに漏洩しますか?
GitHub は自動的に ${{ secrets.XXX }} をログ内で *** に置換します。しかし、環境変数を直接使用した場合(例:echo ${MY_TOKEN})、ログに実際の値が表示されます。ワークフローをテストして、予期しない露出がないことを確認することをお勧めします。
Organization Secrets はどのようなシナリオに適していますか?
チームの複数リポジトリ、共有クレデンシャルのシナリオに適しています。組織レベルで一度設定すれば、すべてのリポジトリで使用できます。アクセス範囲を制御可能:すべてのリポジトリまたは指定したリポジトリリスト。例えば、複数のプロジェクトで AWS_ACCESS_KEY を共有する場合、Organization Secrets を使用して各リポジトリでの重複設定を回避できます。

6 min read · 公開日: 2026年4月18日 · 更新日: 2026年4月20日

関連記事

コメント

GitHubアカウントでログインしてコメントできます