Skip to main content

npmパッケージマネージャーの安全でないデフォルト設定を悪用し、macOSのキーボードショートカットを盗む

feature open source

2023年6月28日

0 分で読めます

悪意のあるnpmパッケージとその危険性は、これまでたびたび話題になってきました。数百ものコマンド&コントロール型Cobalt Strikeマルウェアパッケージ、タイポスクワッティング、npmレジストリ(PyPIなどを含む)に公開された一般的なマルウェアなど、その例はさまざまです。こうしたセキュリティリスクから開発者やメンテナーを守るため、Snykはnpmセキュリティのベストプラクティスガイドを公開しました。

そうした中で、長年のメンテナーにYagiz Nizipliが注意を促した今回の攻撃範囲と、現実に起こり得るデータ侵害のリスクは、npmのpostinstallライフサイクルフックなど、パッケージマネージャーによる任意のコマンド実行リスクを最小限に抑えることがいかに重要かを示す好例です。

npmのライフサイクルスクリプト

Node Package Manager(npm)は、パッケージのライフサイクルイベントを管理するためのスクリプトを開発者やパッケージメンテナーに提供します。これらのスクリプトを使えば、パッケージのインストール時にさまざまなタスクや設定を実行できるため、開発者にとって大きな価値があります。たとえばpostinstallスクリプトを使って、アセットのビルド、環境変数の設定、マイグレーションの実行など、自動化可能なタスクを実行できます。

scriptsファイルのpackage.jsonプロパティには、パッケージのライフサイクルや開発中のパッケージの依存関係に応じて実行されるコマンドを定義します。現在、npmではpackage.jsonファイルのscriptsプロパティで限られた数のライフサイクルスクリプトを利用できます。

ここからは簡潔にするため、postinstallコマンドに焦点を当てます。ただし、この記事で説明する内容は、ほかのライフサイクル操作にも当てはまります。

過去のセキュリティインシデント

JavaScript開発者に現実の影響を及ぼした、大きく報道されたインシデントがいくつも発生しています。たとえば、次のようなものがあります。

そのほか多くのJavaScriptおよびNode.jsのセキュリティインシデントが、Awesome Node.js Securityリポジトリにまとめられています。

保存データのセキュリティ

セキュリティの専門家は、データの転送中や処理中ではなく、保存中の資産を保護することをData-at-restのセキュリティと定義しています。データベース、ファイルシステム、永続ストレージに保存された機密情報の保護に重点を置きます。保存データのセキュリティは、データが使われていない間の不正アクセス、漏えい、改ざんを防ぐことを目的としています。これを実現するため、次のようなさまざまな対策があります。

  • 必要なときだけ復号:現在のタスクに必要なデータのみを復号し、それ以外のデータは暗号化したまま保存して、不正アクセスを防ぎます。

  • アクセス制御ロジック:パスワード、二要素認証、OSが提供する生体認証(Face IDなど)といった仕組みでリクエスト元の身元を確認し、望ましくない相手にリソースがさらされるのを防ぎます。

開発者を取り巻く攻撃対象領域

業界のベストプラクティスに従ってアプリケーションを開発することが求められます。異なるチームや開発者と連携する際に多くのメリットをもたらす一方で、攻撃対象領域も広げてしまいます。

開発者のマシンには、どのようなデータが暗号化されずに残っているのでしょうか?

  • .envなどのプレーンテキストファイルに保存された環境変数(dotenvパッケージで利用可能)。

  • config.jsonなど、JSONファイルとして保存されたプロジェクトの設定ファイル。

  • Github/Gitlabへのアクセスに使うSSHキー(~/.sshフォルダー内に保存)。

  • そして……macOSのキーボードショートカットです!

macOSのテキスト置換

macOSには標準でText Replacementsという機能があり、システム設定アプリの中にあります。この機能を使うと、ある単語を別の単語にすばやく置き換えられます。つい最近、ある有名企業の開発者が、@cardというキーワードをクレジットカード情報に置き換えるテキスト置換を使っていると知りました。有効期限やCVVを含まないカード番号だけなら第三者に知られても金銭的な被害には直結しませんが、悪用される可能性のある攻撃対象領域を増やすことになります。

「@ssh-key」を「my-secret-password」にマッピングしたテキスト置換を表示するmacOSのキーボード設定
テキスト置換機能は、「システム環境設定」アプリケーションの「キーボード」メニュー項目から利用できます。

キーボードのテキスト置換データを外部に持ち出す

キーボードショートカットはdefaultsに保存されます。これはローカルフォルダー内のどこかにある、ファイルシステム上の.plistファイルに対応しています。次のコマンドを実行すると、設定済みのテキスト置換を取得できます。システム設定アプリからも確認できます。

次のコードの実行にsudo権限は必要ありません。コンピューター上のどのプロセスからでも実行できます。

> defaults read -g NSUserDictionaryReplacementItems
(
  {
    on = 1;
    replace = "@ssh-key";
    with = "my-secret-password";
  }
)

同じコマンドはNode.jsのexecSyncから実行して簡単に解析できます。また、npmパッケージマネージャーがサポートするpostinstallライフサイクル操作を通じても実行できます。

以下は、悪意のある攻撃者がmacOSのテキスト置換データにアクセスし、機密情報を外部に持ち出すために利用できるNode.jsスクリプトの例です。

import { execSync } from 'node:child_process'

const decoder = new TextDecoder()
const res = execSync('defaults read -g NSUserDictionaryReplacementItems')
const text_replacements = decoder.decode(res)

console.log(text_replacements)

パッケージのインストール時に上記のコードが実行されるよう、次のようにpackage.jsonのマニフェストファイルを更新します。

{
  "name": "my-useful-library",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "type": "module",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "postinstall": "node ./retrieve.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

npm経由で配布され、開発者がダウンロードすると、このライブラリはカスタムスクリプトを直接実行して、キーボードの置換データを取得・処理します。注意していないと、> node ./retrieve.jsの行を見落としがちです。

➜  vulnerable npm i

> my-useful-library@1.0.0 postinstall
> node ./retrieve.js

up to date, audited 1 package in 192ms

found 0 vulnerabilities
➜  vulnerable

対策

開発者として、悪意のあるnpmパッケージのセキュリティリスクや、依存関係ツリー内のパッケージによる任意のコマンド実行など、一般的なセキュリティ上の懸念を軽減するにはどうすればよいでしょうか?

npmパッケージのインストール時にスクリプトを無効化する

postinstallスクリプトを利用するパッケージから身を守る方法があります。npmでは、パッケージのインストール時に--ignore-scriptsを指定できます。

➜  npm i <package name> --ignore-scripts
up to date, audited 1 package in 124ms
found 0 vulnerabilities

npmの安全なデフォルト設定を使う

npmパッケージマネージャーには、.npmrcという設定ファイルもあります。安全なデフォルト設定を適用するには、npm CLIを使って既定の設定を変更できます。

➜  npm config set ignore-scripts true
➜  npm i
up to date, audited 1 package in 126ms
found 0 vulnerabilities

安全な保管

何より重要なのは、機密情報をプレーンテキストで保存しないことです。要件上、どうしてもプレーンテキストで保存する必要がある場合は、多要素認証を通じてのみアクセスできるようにしてください。

CTFを始めよう

オンデマンドのバーチャル入門ワークショップを視聴して、CTFの課題の解き方を学びましょう。

続きを読む

illustration hero ai
Blog

PHPが重大な未認証RCEの脆弱性2件を修正(CVE-2026-109618、CVE-2026-109619):今すぐアップデート

PHPのリクエスト処理に重大な脆弱性が2件あり、未認証のリモートコード実行につながるおそれがあります。修正版にアップグレードするか、旧バージョンにはベンダー提供のバックポートを適用してください。

blog feature ai lilac
Blog

エージェント型修正で脆弱性バックログをプルリクエストに変える

Evo Remediation Agentがオープンプレビューで利用可能になり、Evo Agentic AppSecに依存関係の自動解決機能が加わりました。AppSecチームは継続的な修正キャンペーンを実施できるようになりました

AI Background min crop
Blog

AIが、あなたが受け入れてきたあらゆるリスクを一気に顕在化させる:AI Security Panel: Unfilteredから得た7つの洞察

AIによって攻撃はより安価かつ高速になり、組織がすでに抱えているリスクが露呈しています。4人のセキュリティリーダーが、機械の速度に対応する防御と迅速な修復が今不可欠である理由を語ります。