Skip to content

Instantly share code, notes, and snippets.

@azu
Last active September 21, 2025 01:16
Show Gist options
  • Save azu/52b40f6afa7cc7b2c6377933461f28c6 to your computer and use it in GitHub Desktop.
Save azu/52b40f6afa7cc7b2c6377933461f28c6 to your computer and use it in GitHub Desktop.
npm/GitHubパッケージのセキュリティ設定のリスクアセスメント

npm/GitHubパッケージのセキュリティ

パッケージを公開するリポジトリにセキュリティ管理について扱う

npm

  • OIDC
  • Trusted Publisher
  • Enviroment: npm を指定する
  • Require two-factor authentication and disallow tokens

で、2FA or OIDCのどちらかでのみpublish可能にできる。

GitHubのアカウントが乗っ取られた場合、npmにpublishされてしまう。

GitHub

Workflow ファイルの方針

  • できるだけ外部依存を減らす
    • actions/* と ビルトインコマンドのみにする
  • 依存は必ずSHA PINする Require actions to be pinned to a full-length commit SHAを有効化
  • CODEOWNERでworkflowの変更にオーナーをつける
  • RulesetでRequire review from Code Ownersを有効化
  • Release Workflowの実行にはEnviroment Protection RulesでOwnerの承認を必須にする
  • Enviroment: npmrelease/*ブランチのみで実行できるようにする

攻撃のリスク

npm

  1. npmアカウントの乗っ取り
    • MFAがセキュリティキーのみなので難しい
  2. npm tokenの漏洩
    • tokenのみではpublishできない
  3. OIDCのtokenの漏洩
  • これは防げないが永続的にするにはworkflow自体を乗っ取る必要がある

GitHub

  1. GitHubアカウントの乗っ取り
    • MFAがセキュリティキーのみなので難しい
  2. GitHub Actionsの脆弱性 = GITHUB_TOKENの漏洩
    • workflowファイルは外部依存を減らす = ghコマンドぐらいにしたい
    • github/workflows/create-release-pr.yml
    • .github/workflows/release.yml
  3. Workflowファイルの改ざん
    • 攻撃パターン
      1. GITHUB_TOKENの漏洩
        • -> RulesetでのProteection BranchでCODEOWNERSの承認を必須にすることで防げる
      2. 管理者アカウントの侵害
      • MFAのチェックなしにリポジトリの設定を変更できないようにしたい
  4. リポジトリOwnerアカウントの乗っ取り
    • MFAがセキュリティキーのみなので難しい
  5. リポジトリOwnerのバスファクタ
    • -> npmの権限移譲、GitHubの権限移譲と基本的には変わらないのでバスファクタ係数自体は変化がない
  6. GitHub Actionsの実行権限の乗っ取り
    • -> Enviroment Protection Rulesで実行にはOwnerの承認を必須にする
  7. GitHubのTokenの漏洩

課題

  • GitHubリポジトリのProtection RulesでMFAのチェックを必須にする方法がわからない
  • GitHub Tokenが漏洩した場合の対策が難しい
    • ghコマンドを使う場合、短期的にTokenが存在してしまう
    • ghコマンドのtokenはclassicで権限が広すぎる - admin権限を含んでいる
    • 短期的にローテーションされていないため、長期的な問題がある
    • 1passwordの10分セッションで、10分ごとにローテーションしたい

GitHub Tokenの対応方針

  1. ghコマンドのScopeをread-onlyにする
  2. Short-lived tokenを使う

1. ghコマンドのScopeをread-onlyにする

ghコマンドのデフォルトの権限はとても広い。

そのため、ghコマンドの権限を最小限にするのが、攻撃のリスクを減らす上で重要。

普段のghコマンドの利用では、read-onlyで十分なことが多いので、ghコマンドのデフォルトの権限をread-onlyにできると良い。

GitHubのPersonal Access Token (PAT)には、2種類ある。

  • classic personal access tokens: ユーザーに紐づく権限
  • fine-grained personal access tokens: リソースに紐づく権限

GitHubのClassic personal access tokensは read-only tokenのような細かい設定はできない。一方で、fine-grained personal access tokensは、read-only tokenは作成できるが、OrganizationごとにTokenを作る必要がある。

Open Sourceとかは基本Public Repositoryなので、fine-grained personal access tokensで"Public repositories"だけを選択して、何も権限を付与しないことで、read-only tokenを作成できる。

  • Repository access: Public repositories
  • Permissions
    • なし

一方で、Private Repositoryを扱う場合は、Organizationごとにfine-grained personal access tokensで次のようなread権限を持ったTokenを作成する必要がある。

  • Repository access: All repositories
  • Permissions
    • Contents: Read-only
    • Pull requests: Read-only
    • Issues: Read-only
    • Metadata: Read-only
    • Actions: Read-only

これで、PublicリポジトリとPrivateリポジトリ用の2種類のTokenを用意する。

1PasswordのGitHub Pluginではディレクトリごとに、GitHub Tokenを切り替えることができる。

  1. Public Repository用のTokenを"Use as global default on my system"として設定する
$ op plugin init gh
# PUblic Repository用のTokenを選択
# Use as global default on my system? を選択
  1. Private Repository用のTokenをPrivate Repositoryのディレクトリで設定する
$ cd private-org
$ op plugin init gh
# Private Repository用のTokenを選択
# "Use automatically when in this directory or subdirectories"を選択

これで、Public RepositoryではPublic Repository用のTokenが、Private RepositoryではPrivate Repository用のTokenが使われるようになる。

どちらのGitHub Tokenもread-onlyなので、Tokenが漏洩しても攻撃のリスクはかなり減る(基本的にリポジトリには、機密情報をそのまま置くことはないので、Privateのコードが漏れるぐらいで済む)

いくつかWrite権限が欲しい操作もある。

  1. Pull Requestの作成 → gh pr create --webで代用
  2. Pull Request Bodyの更新 → 更新内容をクリップボードにコピペして、Webで更新
  3. リポジトリの設定更新 → これだけはWrite権限が必要

3のリポジトリの設定更新は、リポジトリ作成時とかメンテナンス時とか特定のタイミングのみでやる操作なので、別途、Write権限を持ったTokenを1Passwordに保存しておいて、必要なときにだけ使うようにする。

function gh-write-task() {
  op signin
  # GitHubのWrite権限を持ったTokenを使う処理
  GITHUB_TOKEN="op://Private/GitHubWriteToken/token" op run -- gh repo edit azu/example --enable-discussions
  # 終わったら1passwordからサインアウト
  op signout
}

command gh auth login --git-protocol ssh --skip-ssh-key --clipboard --webを使うことで、GitHub CLIのOAuthのtokenを使う方法も考えたけど、OAuth AppのtokenはrevokeするのがWeb画面のみとなっている(gh auth logoutしても、古いtokenはrevokeされない形になっていた)。

GitHub Appのtokenを使う方法はもあるけど、秘密鍵の管理が必要になるのと、複数人で扱うとGitHub Appとしての管理の仕組みが必要になる。

Security Log的には、GitHub App + userとなるので追跡はできそう。

@ghtest-testtestbot @azu
ghtest-testtestbot[bot] – repo.change_merge_setting
Unblocked a merge setting on the azu/example-example repository
1 minute ago
@timestamp 	2025-09-21 09:25:47 +0900
_document_id 	xxx
action 	repo.change_merge_setting
actor 	ghtest-testtestbot[bot]
actor_id 	233508683
created_at 	2025-09-21 09:25:47 +0900
hashed_token 	xxx=
operation_type 	modify
programmatic_access_type 	GitHub App server-to-server token
public_repo 	true
repo 	azu/example-example
repo_id 	123432456
request_access_security_header 	nil
request_id 	077E:1111:7022A1:9B0DE5:68CF460B
user 	azu
user_agent 	GitHub CLI 2.79.0
user_id 	19714
visibility 	public 

GitHub Appのinstallation_idが、Organizationごとに異なるIDとなる。 OrganizationごとにGitHub Appをインストールする必要がある。

Organizationごと異なるなら、共通のGitHub Appを使う必要性があまりないような気がする。 共通のGitHub Appにすると、生成されたPrivate Keyの権限が強くなるだけだから、OrganizationごとにGitHub Appを作成した方が良い気もする。

2. Short-lived tokenを使う

Tokenの有効期限を短くすることでTokenが漏洩した場合のリスクの長期化が減る。

GitHub Personal Access Token (PAT)やOAuthのTokenには、有効期限がとても短いようなToken(10分とか)を扱う実用的な方法が用意されていない。

GitHub AppのTokenを使うことで、Tokenのローテーションと短い時間のTokenを扱うことはできる。

ただし、GitHub Appを管理する必要がある問題と少しハック的な方法になるので、今のところやってない。寿命の短いTokenを作れるという点はGitHub Appの方が良いが、GitHub Appの秘密鍵を持つリスクがあると感じている。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment