Skip to main content

JavaScriptおよびNode.jsアプリケーションでログインジェクションの脆弱性を防ぐ方法

著者
feature open source

2024年9月26日

0 分で読めます

多くの一般的なエンタープライズアプリケーションでは、一貫したログ記録がさまざまな目的に役立ちます。エラーの特定と修正、貴重な分析情報の取得、新しいソリューションのテストに活用できます。しかしその一方で、ログインジェクションは、ハッカーがユーザーの機密情報を乗っ取ったり、取得したりするための最も一般的な手口の一つにもなっています。アプリケーションでデータを記録する方法に脆弱性があると、攻撃者がログに悪意のあるコードを挿入し、アプリケーションの完全性や機密性を損なうおそれがあります。

この記事では、Node.jsにおけるログインジェクションの脆弱性に焦点を当てます。ログインジェクションの仕組みと、効果的な防止方法を解説します。

ログインジェクションの脆弱性とは?

ログ記録では、外部ファイルに記録するアプリケーションや、ログの送信先として外部APIに依存するアプリケーションがあります。ログインジェクションとは、攻撃者が入力データを改ざんし、アプリケーションのログに悪意のあるコードを挿入する攻撃です。ログは通常、デバッグ、監視、監査に使用されます。

ログインジェクションが発生すると、攻撃者はログを大量の情報で埋め尽くしたり、アプリケーションのコンテキスト内でコードを実行したりできるため、さまざまなセキュリティリスクにつながります。

脆弱なNode.jsアプリの例

入力値の適切な検証やサニタイズを行わずに、ユーザーが入力したデータを記録するExpressウェブフレームワークを使ったシンプルなNode.jsアプリケーションを考えてみましょう。次のコードスニペットは、この脆弱性を示しています。

const express = require('express');
const app = express();

app.get('/log', (req, res) => {
  const userInput = req.query.data;
  console.log('INFO: ' + userInput);
  res.send('Data logged successfully.');
});

app.listen(3000, () => {
  console.log('Server started on port 3000');
});

このサーバーをローカルで起動したら、InsomniaなどのAPIテストツールでGETリクエストを送信し、想定どおりに動作するか確認できます。ここでは、フォームからユーザー名を記録して、サーバーのログがどのように見えるかをテストします。サーバーが想定する形式に合わせてデータを追加し、リクエストを送信します。すると200 OKが返され、「Data logged successfully.」というテキストが表示されます。

ログデータを送信するPOSTリクエストと、「データが正常に記録されました」という成功レスポンスを表示するAPIクライアント

サーバー側のログを確認すると、すべて想定どおりに記録されています。データパラメーターにcharlieを指定したため、サーバーのログにはINFO: charlieと記録されています。

ポート3000でNode.js Expressサーバーが起動し、「charlie」というINFOログが表示されたターミナル

このアプリケーションがデータを記録する仕組みを悪用する方法の一つは、dataパラメーターを使って偽のログを挿入することです。この例では、クエリにテキストを追加するだけで攻撃できます。

エンコードされたログインジェクションペイロードを含むlocalhostへのGETリクエストと、成功を示す200レスポンスを表示するAPIクライアント。
ポート3000で起動し、ユーザー削除のアクティビティをログに記録するNode.js Expressサーバーを表示したターミナル

たとえば、攻撃者は偽のログとしてINFO: User deletedを追加できます。開発チームがこのログを見つけると、ユーザーがユーザー名を送信した際にアプリケーションがユーザーを削除しているのはなぜかと誤解し、その原因を究明するために時間とリソースを費やす可能性があります。すべては、攻撃者がユーザー入力フォームに文字を追加したことが原因です。

この例は単純ですが、ログインジェクションの概要を理解するのに役立ちます。ログメッセージを変更して偽のエラーログやユーザー情報ログを追加するほか、さらに悪質なケースでは、攻撃者が内部データを直接操作できるコマンドインジェクションを挿入することもあります。

Node.jsでログインジェクションの脆弱性を防ぐ方法

Node.jsアプリケーションのログインジェクションの脆弱性を軽減するには、次のような一般的な対策を検討してください。

ユーザー入力をサニタイズする

多くの場合、ユーザー入力はログに記録する前に検証・サニタイズする必要があります。ログに記録するデータを十分に確認し、別のログ入力と誤認される文字を除外する、またはログに含めるべき文字だけを許可するということです。

先ほどの例では、アルファベットのみを許可する正規表現を使ってログをサニタイズする方法があります。これにより、特殊文字や数字がログに追加されるのを防げます。

app.get('/log', (req, res) => {
  const userInput = req.query.data
  const sanitizedInput = userInput.replace(/[^a-zA-Z]+/, '')
  console.log('INFO: Username: ', sanitizedInput);
  res.send('Data logged successfully.');
});
ユーザー名のエントリと「INFO: User deleted」というメッセージが表示された、Node.js Expressサーバーのログ画面

ただし、データをサニタイズする適切な方法を選ぶのは難しい場合があることに注意してください。この例では、ユーザー名に数字(例:123)が含まれていると、ログに記録されるユーザー名が変更されてしまいます。

入力のサニタイズは悪意のあるログの検出に役立ちますが、何をどのように除外するかについて、開発者は十分に慎重になる必要があります。たとえば、正規表現を誤って記述すると、Node.jsアプリケーションを停止させるReDOS脆弱性につながる可能性があります。ゼロから作る代わりに、validator.jsなどのサードパーティ製npmパッケージを使うのも有効ですが、依存関係を常に最新の状態に保つようにしてください。

ログに記録する情報に注意する

アプリケーションはそれぞれ異なります。すべてのアプリケーションで生じる可能性のあるニーズに対応できる、万能なログ記録の基準はありません。どの情報を、誰のために記録する必要があるのかを検討することが重要です。

DEBUGレベルのログは、開発者がアプリケーション内で何が起きているか、ユーザーがコンポーネント間をどのように移動しているか、情報がソース間をどのように流れているかを追跡するのに役立ちます。WARNまたはERRORレベルのログは、エンジニアリングチームが潜在的なバグを早期に発見し、アプリケーションのクラッシュや誤動作の原因を理解するのに役立ちます。重要なのは、必要な情報だけを記録し、攻撃者にログを侵害された場合に何が起こるかを考慮することです。

ログ記録の脆弱性に関するSnykの記事で説明されているように、外部のログサービスに送信した機密情報が、攻撃者によるデータ侵害につながる可能性があります。パスワード、APIキー、個人データなどの機密情報や秘密情報をログに記録しないのが基本です。記録する場合は、適切なアクセス制御を実装し、環境変数(GitHub Actionsなど)やキー管理システムを使って機密性の高い設定データを安全に管理することを検討してください。

ログに記録する情報だけでなく、記録方法も検討する必要があります。構造化ログを使うと、開発者が把握しやすい、より整理された方法でデータを記録できます。たとえば先ほどの例では、ユーザーデータを文字列として直接記録する代わりに、JSONオブジェクトを使うと、記録内容をより明確にラベル付けできます。

app.get('/log', (req, res) => {
  const userInput = req.query.data;
  const log = {username: userInput, 'action':'submitted username', 'time': new Date()}
  console.log(log)
  res.send('Data logged successfully.');
});

さらに、ログをこのように構造化すると、攻撃者がログを挿入するのが難しくなります。ログオブジェクトには、攻撃者が操作しなければならないプロパティ( actionやtimeなど)が複数あり、それらを適切に操作できなければ、攻撃者の意図が露呈する可能性があるためです。

ポート3000で稼働するNode.js Expressサーバーと、改行メッセージが挿入された名がログに記録されているターミナル出力

JSONオブジェクトをログに記録するのは単純な例ですが、次に紹介するログ記録ライブラリには、一般的なconsole.logに代わる便利な構造化ログ機能が用意されています。

console.logの代わりにログ記録ライブラリを使う

多くのログ記録ライブラリは、console.logよりもログ出力を細かく制御でき、セキュリティにも優れています。通常、ログインジェクション攻撃を防ぐサニタイズ機能やフィルタリング機能が組み込まれているためです。また、環境(テスト環境か本番環境か)ごとに表示するログを制御するフラグ、ログの送信先(ローカルファイル、データベース、サードパーティAPI)を柔軟に選ぶ機能、ログを追跡しやすくする設定機能も開発者に提供します。

おすすめのライブラリの一つはpinoです。適切にメンテナンスされており、既知のセキュリティ脆弱性もない、全体として健全なパッケージです。

Pinoはパフォーマンスが高度に最適化されており、各ログエントリーにタイムスタンプを含めるよう設定できます。構造化ログと標準的なシステムログレベルにも対応しています。こうした追加の複雑さと構造によってセキュリティが強化され、攻撃者にログを侵害されるリスクを抑えられます。

VS Code用のSnyk IDE拡張機能で脆弱性を検出する

Snykは、Visual Studio Code(VS Code)向けの無料IDE拡張機能を提供しています。Node.jsコード内のログインジェクションの脆弱性をはじめ、さまざまな脆弱性の検出に役立ちます。設定方法は次のとおりです。

VS CodeのExtensionsタブに移動し、「Snyk」と検索して、「Snyk Security - Code, Open Source Dependencies, Iac Configurations」拡張機能を見つけます。Installをクリックします。

Snyk Security拡張機能の掲載と[インストール]ボタンが表示されたVisual Studio Codeの拡張機能マーケットプレイス

インストール後、SnykからIDEに接続してアカウントを認証するよう求められます。Connect VS Code with Snykをクリックすると、接続の許可を求めるSnykの外部ウェブページが開きます。ページでAuthenticateをクリックします。

認証が完了したら、Scanをクリックしてコードの脆弱性をスキャンします。必要に応じて拡張機能を設定することもできます。

コードエディターに表示されたSnyk Codeのセキュリティパネル。JavaScriptファイルに6件の脆弱性と55件のコード品質問題が示されています

Snyk IDE拡張機能は、コード内のログインジェクションの脆弱性を自動的にスキャンします。さらに、スキャン中にハードコードされたシークレットやパストラバーサルの脆弱性など、ほかの潜在的なセキュリティ問題も検出します。アプリケーションのセキュリティを高めるための、実行可能な分析情報や提案も提供します。

Snykを無料で使い始める

クレジットカードは不要です。

Bitbucketなどを使ってアカウントを作成

Snykを利用することで、利用規約およびプライバシーポリシーを含む当社のポリシーに同意したものとみなされます。

続きを読む

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人のセキュリティリーダーが、機械の速度に対応する防御と迅速な修復が今不可欠である理由を語ります。