🛡️

npmパッケージ/GitHub Actionsを利用する側/公開する側でサプライチェーン攻撃を防ぐためにやることメモ

に公開

パッケージを利用する側、パッケージを公開する側でサプライチェーン攻撃を防ぐためにできることのメモ書きです。

パッケージを利用する側

npmやGitHub Actionsなどを利用する側として、サプライチェーン攻撃を防ぐためにできることをまとめます。

ロックファイルを使う

  • npmやYarn、pnpmなどのパッケージマネージャーは、依存関係のバージョンを固定するためにロックファイル(例: package-lock.json, yarn.lock, pnpm-lock.yaml)を使用する

GitHub ActionsではSHA Pinを行う

GitHub Actionsには最小の権限を与える

依存のアップデート

サプライチェーン攻撃起きてからすぐパッケージをアップデートしてしまうと受動的に攻撃を受ける可能性がある。
そのため、パッケージが公開されてから1週間経ってからアップデートするPRを作るようなオプションをdependabotやrenovateでは有効にする。

renovatebot/dependabotを装ってlockfileを意図しない形でアップデートする攻撃も考えられる。この場合 dependabotなら手動でマージボタンは押さないで @dependabot merge コメントをすることで、本物のdependabotかを見分けることができる(権限がない偽物ならマージはできないため)
renovatebotだとこれに対応する方法はないけど、最近はpnpmを使っているのであまり気にしなくなった。この問題をチェックするツールも存在する。

pnpm catalogではlockfileのズレが pnpm install時にエラーにならないバグがあるけど、workaroundで検出はできる

スキャンを通してないパッケージをnpxで叩かない

  • MCP系で特に問題になる
  • npxは最新のパッケージを自動でダウンロードして実行してしまうので、パッケージが安全なのかを確認してから実行する必要がある
  • 繰り返し実行するなら、そもそもnpxでダウンロードしないで、devDependenciesに入れておくなどバージョンを固定する
  • npxで叩く場合は、バージョンを指定する e.g. npx some-package@1.2.3
  • azu/ni.zshryoppippi/bun-socket-scannerでは、Socket.devを使ったスキャンを行ってからパッケージを実行できる

AI AgentはSandboxで実行する

  • 勝手に外部パッケージを入れたり色々しちゃうため
  • サンドボックス環境で実行することで、ホストOSへの影響を最小限に抑える
    • 主にたどれるべきではないファイルやネットワークにアクセスされるのを防ぐ
  • macOSなら軽量なサンドボックスとしてsandbox-execを使う
  • DevContainersなどDockerコンテナで実行する

パッケージを公開する側

npm registryにパッケージを公開する側として、サプライチェーン攻撃を防ぐためにできることをまとめます。
攻撃者に認証情報を盗まれないようにする方法、アカウントを乗っ取られた場合の被害を軽減する方法などについて

コミットはsecretlintを通す

  • secretlintを使って、コミットに秘密情報が含まれていないかをチェックする
  • 機密情報はprivate repositoryであっても漏洩する可能性があるので、コミットに含めない

ローカルに生tokenを置かない

TokenのScopeを最小にする

  • npmはGranular access tokens
  • GitHubはGitHub AppsFine-grained personal access tokens
  • それぞれ最小のスコープを設定したtokenを利用する
  • npmはほぼtokenが不要になってきているので、できる限りtokenを発行しないようにする
  • GitHubは最小のスコープ(リポジトリと権限)のtokenを発行する
  • 広いスコープ(リポジトリ)のtokenはghコマンドぐらいになるイメージで、このtokenも1password連携を使うことでローカルに生のtokenとして存在しない状態にできる

GitHub ActionsはSecurity Checkを行なってからマージする

  • GitHub Actionsには典型的なScript Injectionの問題がある
  • - run: echo "${{ github.event.issue.title }}" のように書いたStepがあると、Issueのタイトルに悪意のあるコードを書かれると、そのコードが実行されてしまう
  • https://securitylab.github.com/resources/github-actions-untrusted-input/
  • そのため、GitHub ActionsのWorkflowを変更するPRは、Security Checkを行なってからマージする
  • 背景としては、AdnaneKhan/gato-xのようなツールで、このような脆弱性があるGitHub Actionsは自動的に見つけることができる
    • 実際にnxの攻撃の起因となったGitHub Actionsの問題も、実際にインシデント起きる1週間前には発見されている
    • これらのツールを回して脆弱なPublicリポジトリを見つけて、攻撃を仕掛ける攻撃者がいる

npmのTrusted PublishingとOIDC連携を使ってトークンレスでCIからnpmパッケージを公開する

npmは"Require two-factor authentication and disallow tokens"を設定する

Require two-factor authentication and disallow tokens
With this option, a maintainer must have two-factor authentication enabled for their account, and they must publish interactively. Maintainers will be required to enter 2FA credentials when they perform the publish. Automation tokens and granular access tokens cannot be used to publish packages.
https://docs.npmjs.com/requiring-2fa-for-package-publishing-and-settings-modification

MFAはフィッシング耐性の高いものを使う

  • SMS/TOTPはフィッシング耐性が低いので、できる限りフィッシング耐性の高いMFAを使う
  • WebAuthn/FIDO2などのセキュリティキー/Passkeyを使う
  • パスワード管理/MFA管理の戦略 | Web Scratch
  • OIDCなどでnpm token自体を減らせたので、自分の場合はnpmにはセキュリティキーのみ(+バックコードを保存)をMFAとして登録している
  • GitHubもセキュリティキーのみをMFAにしたいが、昔登録した Authenticator app が消せないというバグがある

Discussion