Skip to main content

バックエンド開発者が知っておくべきNode.jsセキュリティコードスニペット5選

著者
feature nodejs security snippets

2024年2月28日

0 分で読めます

バックエンド開発者には、アプリケーションのセキュリティを確保するという重要な役割があります。Node.jsも例外ではありません。人気の高まりとともにハッカーにとって格好の標的となっているため、Node.jsを使う際はセキュリティのベストプラクティスに従うことが不可欠です。

このブログでは、2024年にバックエンド開発者が知っておくべきNode.jsの重要なセキュリティコードスニペットをご紹介します。Node.jsソフトウェアの脆弱性は、あらゆるアプリケーションにとって重大な脅威となり得ます。不正アクセスやデータ漏えいを招き、最悪の場合はシステム全体が侵害される可能性があります。そのため、セキュリティのベストプラクティスに従うことは単なる推奨事項ではなく、必須事項です。

Node.jsでは、悪用可能な抜け穴をコードに作らないようにすることが重要です。インジェクション攻撃を防ぐためにユーザー入力をサニタイズする、パスワードを機密データとして適切に扱う、サードパーティ製の脆弱性を防ぐために依存関係を管理するなど、さまざまな対策が必要です。

ここでは、一般的なセキュリティ脆弱性の防止効果が高く、専門的なセキュリティ知識がなくても開発者が手軽に実践できるNode.jsのセキュリティ概念とコードスニペットを紹介します。

  1. Node.jsの権限モデルを使って、実行時のリソースへのアクセスを制限する

  2. FastifyのJSONスキーマで入力検証を実装する

  3. Bcryptでパスワードのハッシュ化を安全に行う

  4. Knex.jsでSQLインジェクション攻撃を防ぐ

  5. fastify-rate-limitでレート制限を実装する

1. Node.jsの権限モデルを使って、実行時のリソースへのアクセスを制限する

Node.jsの権限モデルは、アプリケーションのセキュリティ確保に重要な役割を果たします。Node.jsのコアセキュリティモデルの重要な要素であり、攻撃や悪意ある活動からアプリケーションを守ります。Denoがプロセス単位のリソース制限によって実現したセキュリティと同様に、Node.jsにも(一定の範囲で)同様の権限モデルが導入されています。

Node.jsアプリケーションでPDFファイルをPNG画像に変換するケースを考えてみましょう。この処理にはnpmパッケージのpdf-imageを使用できます。pdf-imageパッケージは変換に子プロセスを使用します。これを有効にするには、Node.jsランタイムで権限モデルが提供する--allow-child-processフラグを使う必要があります。

以下のコードスニペットをご覧ください。

const { PDFImage } = require('pdf-image');
const path = require('path');

const pdfPath = path.resolve(__dirname, 'sample.pdf');
const pdfImage = new PDFImage(pdfPath, {
  convertOptions: {
    '-density': '300',
    '-quality': '80'
  },
  combinedImage: true
});
pdfImage.convertFile().then(() => {
  console.log('PDF converted to PNG successfully');
}).catch((err) => {
  console.error(`Failed to convert PDF to PNG: ${err}`);
});

このスニペットでは、PDFファイルのパスと変換オプションを指定してPDFImageの新しいインスタンスを作成しています。次にconvertFile()を呼び出し、PDFをPNG画像に変換します。

Node.jsで実験的な権限モデルを有効にする場合は、前述の--allow-child-processコマンドラインフラグを付けてNode.jsランタイムを明示的に実行する必要があります。内部ではpdf-imageライブラリが子プロセスを起動してPDFを変換するためです。

Node.jsの権限モデルはシステムリソースへのアクセスを制限する優れた方法ですが、使用するパッケージに潜むセキュリティ脆弱性にも注意が必要です。そこで役立つのがVisual Studio Code用Snyk拡張機能です。Snyk拡張機能は、Node.jsアプリケーション内の安全でないコードや脆弱な依存関係を検出できます。たとえば、先ほど紹介したpdf-imageパッケージは、子プロセスを使用することによるコマンドインジェクションの脆弱性が知られています。この脆弱性は2018年から報告されていますが、残念ながら現時点で修正プログラムはありません。

このことから、Snykのようなツールを使って依存パッケージのセキュリティ脆弱性を把握しておくことの重要性がわかります。また、子プロセスのように、適切に扱わなければセキュリティリスクとなる機能の使い方にも注意が必要です。

Node.js開発者には、機能するだけでなく安全なコードを書く責任があります。Node.jsアプリケーションのセキュリティを高める重要な方法の一つは、組み込みの権限モデルを使って実行時のシステムリソースへのアクセスを制限することです。アプリケーションで子プロセス機能が不要だとわかっている場合は、攻撃対象領域を広げないよう、このリソースを有効にしないでください。

JavaScriptアプリのセキュリティ対策を始めましょう

Snykを無料で利用して、JavaScriptの脆弱性を検出・修正しましょう。

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

またはAzure AD Docker ID Bitbucketで登録

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

Node.jsの権限モデルに関するFAQ

Node.js Permissions Modelは、どのようなセキュリティ脅威を防ぎますか?

Node.js Permissions Modelは、不正なファイルアクセス、コマンドインジェクション攻撃、権限昇格など、さまざまなセキュリティ脅威を防ぐように設計されています。

Node.js Permissions Modelはカスタマイズできますか?

はい。Node.js Permissions Modelで制御されるリソースの中には、高度にカスタマイズできるものがあります。たとえば、ファイルリソースへのアクセスを制限する場合、--allow-fs-read=*のように、対象のファイルやファイルパスを指定できます。

Node.jsの権限モデルだけでアプリケーションを安全に保護できますか?

Node.jsの権限モデルはアプリケーションセキュリティにおいて重要な要素ですが、それだけでは十分ではありません。セキュアコーディングを実践し、コードに脆弱性がないか定期的に監査するとともに、サードパーティ製の依存関係もチェックする必要があります。また、Snykのようなツールを使って、潜在的なセキュリティ上の問題を特定し、修正しましょう。

2. FastifyのJSONスキーマで入力検証を実装する

入力検証は、バックエンド開発に欠かせない要素です。ExpressやFastifyなどのフレームワークでAPIを構築したことがあれば、その重要性はすでにご存じでしょう。入力検証は、適切な形式のデータだけがシステムに渡るようにするセキュリティ対策であり、潜在的なリスクを防ぎます。Node.jsアプリケーションで入力検証を実装する方法の一つが、FastifyのWebアプリケーションでFastifyスキーマを使用することです。

Fastifyのスキーマベースのアプローチ

Fastifyは、入力検証にスキーマベースのアプローチを採用しています。必須ではありませんが、JSONスキーマを活用してルートを検証し、出力をシリアライズすることが推奨されます。JSONスキーマはデータの契約として、想定されるデータ形式、データ型、必須フィールド、その他の制約を定義します。FastifyのルートでJSONスキーマを使うのは、ビルド時にTypeScriptでコードの型安全性を確保するのと似ています。

新しいユーザーを作成するシンプルなFastifyアプリケーションを考えてみましょう。Fastifyスキーマを使って入力データを検証できます。

const fastify = require('fastify')({ logger: true });

const UserSchema = {
  body: {
    type: 'object',
    properties: {
      name: { type: 'string' },
      email: { type: 'string', format: 'email' },
      password: { type: 'string', minLength: 8 }
    },
    required: ['name', 'email', 'password']
  }
};

// User creation route
fastify.post('/users', { schema: UserSchema }, async (request, reply) => {
  // Example user creation logic
  // In a real application, you would replace this with actual database logic
  const user = request.body;

  // Simulating user creation
  console.log("Creating user:", user);

  // Responding with the created user (in real applications, never send the password back)
  return reply
    .code(201)
    .send({ success: true, message: "User created", user: { name: user.name, email: user.email } });
});

// Server startup
const start = async () => {
  try {
    await fastify.listen({ port: 6000, host: 'localhost' });
    console.log(`Server running at http://localhost:3000/`);
  } catch (err) {
    fastify.log.error(err);
    process.exit(1);
  }
};

start();

上記のコードでは、名前、メールアドレス、パスワードを必須とするUserSchemaを定義しています。メールアドレスは有効な形式である必要があり、パスワードは8文字以上でなければなりません。

入力検証を怠ると、サーバーサイドリクエストフォージェリ(SSRF)やHTTPパラメータ汚染攻撃など、さまざまなセキュリティリスクにアプリケーションがさらされます。SSRF攻撃では、サーバーをだまして不正なリクエストを送信させ、データが漏えいする可能性があります。一方、HTTPパラメータ汚染ではリクエストが操作または破損され、予期しない動作につながる恐れがあります。

Snykなどのツールは、コードをスキャンしてセキュリティ上の問題を検出し、こうした脆弱性の発見を支援します。また、修正に役立つアドバイスも提供し、アプリケーション全体のセキュリティ向上をサポートします。

Fastifyでの入力検証に関するFAQ

入力検証は必要ですか?

はい。入力検証は、形式が不適切なデータがシステムに取り込まれるのを防ぐ、基本的なセキュリティ対策です。

Fastifyで他のバリデーションライブラリを使用できますか?

はい。Fastifyは柔軟性が高く、Joi、yup、ajvなどの他のバリデーションライブラリも使用できます。

Fastifyのスキーマ検証はパフォーマンスに影響しますか?

Fastifyは高いパフォーマンスを実現するように設計されており、スキーマ検証がパフォーマンスに与える影響は最小限です。

4. FastifyのJSONスキーマとは何ですか?

Fastifyスキーマは、ルートでリクエストデータとレスポンスデータを検証し、指定された条件を満たすデータのみが処理されるようにするために使用されます。これにより、セキュリティ上の脆弱性やアプリケーションエラーにつながる可能性のある無効なデータを処理するリスクを軽減できます。

3. Bcryptでパスワードのハッシュ化を安全に行う

ソフトウェア開発では、ユーザーのパスワードを安全に保存することが不可欠です。データ侵害が発生した場合でも、ユーザーのパスワード情報を簡単に解読されないようにする必要があります。そこで使われるのがパスワードのハッシュ化です。ハッシュ化とは、与えられたキーを別の値に変換する処理です。ハッシュ関数で新しい値を生成し、理想的には入力値ごとに固有の出力(ハッシュ値)を得ます。

サービスや別のライブラリに頼らず、自分で認証を実装する場合は、パスワードのセキュリティをどのように扱うかが、アプリケーション全体の安全性に直接影響することを理解しておくことが重要です。

Node.jsでのパスワードハッシュ化に使うBcryptの概要

Bcryptは、安全性が非常に高いとされるパスワードハッシュ関数を提供するアルゴリズム兼npmパッケージです。レインボーテーブル攻撃を防ぐソルト(ランダムデータ)を組み込み、処理負荷を調整できるコスト係数も設定できます。これにより、ハッシュ化に必要なCPU負荷を指定でき、ブルートフォース攻撃の防止に役立ちます。

シンプルな使用例を見てみましょう。

const bcrypt = require('bcrypt');
const saltRounds = 10;
const myPlaintextPassword = 'my_password';

// Define an async function
async function hashPassword(plaintextPassword) {
  try {
    const hash = await bcrypt.hash(plaintextPassword, saltRounds);
    // Store hash in your password DB.
    console.log(hash); // Example of how to use the hash
  } catch (err) {
    // Handle error
    console.error(err);
  }
}

// Call the async function
hashPassword(myPlaintextPassword);

saltRoundsパラメーターは、ハッシュ化処理の複雑さを決定します。値が大きいほど処理時間が長くなり、ブルートフォース攻撃への対策になります。hash関数は、平文のパスワードに自動でソルトを加えてハッシュ化します。生成されたハッシュ値はデータベースに保存できます。

Node.jsのパスワードハッシュ化と認証管理に関するFAQ

Bcryptで使用するソルトの適切な長さは?

Bcryptは16バイトのソルトを自動的に生成します。

ハッシュアルゴリズムや戦略はどのくらいの頻度で更新すべきですか?

ハッシュ技術に大きな進歩があった場合や、現在使用しているアルゴリズムに脆弱性が見つかった場合は、ハッシュ戦略を更新することをおすすめします。Node.jsでは、コアのnode:cryptoモジュールの一部として、scryptアルゴリズムが標準でサポートされています。

Bcryptのワークファクターとは何ですか?

ワークファクターは、ハッシュ処理に必要なCPU負荷を決定します。ワークファクターが高いほどハッシュの安全性は高まりますが、計算にも時間がかかります。Node.jsでは、ハッシュの生成方法によっては、イベントループやアプリケーションの応答性にも直接影響することがあります。

4. Knex.jsでSQLインジェクション攻撃を防ぐ

SQLインジェクションは、アプリケーションのデータに大きなリスクをもたらす、広く見られるセキュリティ脆弱性です。Node.js開発者は、有望なSQLクエリビルダーであるKnex.jsを使って安全なSQLクエリを作成し、このリスクを軽減できます。ただし、Knex.jsでは生のSQLクエリを作成して実行することもできます。その場合、ユーザー入力を連結するとクエリに混入する可能性があるため、SQLインジェクションは依然としてセキュリティ上の問題となります。

Knex.jsは、Node.js向けの強力なSQLクエリビルダーです。トランザクション、コネクションプーリング、マイグレーション、シードに対応しているため、安全で堅牢かつスケーラブルなSQLクエリを作成したい開発者に選ばれています。Knex.jsは、パラメーター化クエリを使用し、SQL文に入力される値をエスケープすることで、SQLインジェクション攻撃を防ぎます。

以下のコードスニペットは、Knex.jsがSQLインジェクション攻撃を防ぐ方法を示しています。

const knex = require('knex')({
  client: 'pg',
  connection: {
    host : '127.0.0.1',
    user : 'your_database_user',
    password : 'your_database_password',
    database : 'myapp_test'
  }
});

// This value is provided by the user and could be malicious
// such as applying an OR 1=1 SQL injection or other
// techniques that escape the original context of the query
// and create a new one
let userProvidedValue = 'maliciousValue';

knex('users')
  .where('id', '=', userProvidedValue)
  .select()
  .then(rows => {
    // process result
  })
  .catch(err => {
    // handle error
  });

この例では、userProvidedValueがKnex.jsによって自動的にエスケープされ、SQLインジェクション攻撃の可能性を防ぎます。

先ほど説明したように、Knex.jsを使うだけでSQLインジェクションに対する完全なサンドボックスやセキュリティ対策が実現するわけではありません。SQLインジェクションにつながる可能性のある、安全でないKnex.jsの使用例を見てみましょう。

knex.raw(`SELECT * FROM users WHERE id = ${userProvidedValue}`)

この場合、userProvidedValueはエスケープもパラメーター化もされません。そのため、userProvidedValueに悪意のあるSQLコードが含まれていると、クエリはSQLインジェクションの影響を受けやすくなります。Snykは、コードベースにあるこのような脆弱性の検出を開発者に支援します。Snykはコードをスキャンし、SQLインジェクション攻撃の可能性を含むセキュリティ脆弱性の修正に役立つ、実行可能な情報を提供します。

JavaScriptの脆弱性を検出して修正

Snykの脆弱性スキャンと修正アドバイスで、アプリケーションを保護しましょう。

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

またはAzure AD、Docker ID、Bitbucketで登録

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

Knex.jsでのSQLインジェクション対策に関するFAQ

SQLインジェクションとは何ですか?

SQLインジェクションとは、攻撃者がクエリに悪意のあるSQLコードを挿入する手法です。この侵入により、機密データへの不正アクセスやデータの破損、さらにはデータの消失につながる可能性があります。そのため、アプリケーションのセキュリティを確保するには、SQLインジェクション攻撃の防止が不可欠です。

Knex.jsはSQLインジェクションの影響を受けませんか?

Knex.jsは、パラメーター化クエリや値のエスケープによってSQLインジェクションのリスクを効果的に軽減しますが、完全に防げるわけではありません。開発者はKnex.jsを正しく使用し、脆弱性を作り込まないようにする必要があります。

Knex.jsを使えばSQLインジェクションを防ぐのに十分ですか?

Knex.jsはSQLインジェクションの防止に役立つ強力なツールですが、それだけで対策が完結するわけではありません。開発者は、入力値の検証や最小権限の原則など、ほかのセキュリティのベストプラクティスも取り入れる必要があります。

5. fastify-rate-limitでレート制限を実装する

レート制限は、Webアプリケーションをサービス拒否(DoS)攻撃から守る重要なセキュリティ対策です。一定時間内にクライアントがアプリケーションへ送信できるリクエスト数を制御することで、不正なクライアントによる大量のリクエストでサーバーが過負荷になるのを防ぎ、正規ユーザーが引き続きアプリケーションを利用できるようにします。

fastify-rate-limitは、Fastify Webフレームワーク用のプラグインで、Node.jsアプリケーションに簡単にレート制限を実装できます。一定時間内にクライアントが送信できるリクエスト数の上限と、その上限を超えたときに返すレスポンスを指定できます。

fastify-rate-limitを使ってNode.jsアプリケーションにレート制限を実装する基本的な例を紹介します。

const fastify = require('fastify')()

fastify.register(require('@fastify/rate-limit'), {
  // max number of connections during windowMs milliseconds before sending a 429 response
  max: 100,
  timeWindow: '1 minute'
})

fastify.get('/', (req, reply) => {
  reply.send({ hello: 'world' })
})

fastify.listen(3000, err => {
  if (err) throw err
  console.log('Server listening at http://localhost:3000')
})

このコードスニペットでは、Fastifyのレート制限パッケージを設定し、各クライアントのリクエストを1分あたり100件に制限しています。上限を超えると、サーバーは429(「リクエストが多すぎます」)ステータスコードを返します。

レート制限に関するFAQ

レート制限によってアプリケーションのパフォーマンスが低下することはありますか?

いいえ。レート制限は、大量のリクエストによる過負荷を防ぐことで、アプリケーションのパフォーマンスと可用性を向上させます。

正規のユーザーがレート制限を超えた場合、どうすればよいですか?

アプリケーションのニーズに応じて、レート制限を引き上げる、特定のIPアドレスをレート制限の対象外にする、またはユーザーの行動や評判を考慮した、より高度なレート制限戦略を導入するといった方法を選べます。

レート制限ですべての種類のサービス拒否(DoS)攻撃を防げますか?

レート制限は、大量のリクエストでサーバーを過負荷にするDoS攻撃に効果的です。ただし、アプリケーションやサーバーの特定の脆弱性を悪用する他の種類のDoS攻撃は防げません。そのため、レート制限だけでなく、ほかのセキュリティ対策も含めた包括的なセキュリティ戦略を実施することが重要です。

開発者がJavaScriptのセキュリティにSnykを使うべき理由

このブログでは、すべてのバックエンド開発者が知っておくべきNode.jsの重要なセキュリティコードスニペットを5つ紹介しました。SQLインジェクションの回避など、安全なコーディング手法の重要性と、アプリケーションのセキュリティ向上に果たす役割についても説明しました。テクノロジーの進化とサイバー脅威の複雑化が進む今、安全なコーディングの重要性はいくら強調してもしすぎることはありません。

開発者は、コードに潜む脆弱性を特定して軽減できる強力なセキュリティツールを活用することが重要です。その一つが、開発者向けの強力なセキュリティプラットフォームSnykです。コード、依存関係、コンテナなどの脆弱性を検出して修正できます。Snykを使えば、アプリケーションのセキュリティ脆弱性を継続的に監視でき、新たな脆弱性が見つかった際には自動修正PRを受け取れます。

// Install Snyk CLI
npm install -g snyk

// Then run Snyk to find vulnerabilities
snyk test

SnykはIDEやCI/CDパイプラインにシームレスに統合でき、アプリケーション開発ライフサイクル全体を通じて継続的なセキュリティを維持できます。Node.jsをはじめ、幅広いプログラミング言語に対応しているため、さまざまなプラットフォームで開発する人にとって汎用性の高いツールです。

セキュアコーディングはソフトウェア開発の基本であり、すべてのバックエンド開発者が優先すべき実践です。Snykのようなセキュリティツールを活用すれば、脆弱性の自動検出と修正機能によって、セキュアコーディングをさらに強化できます。絶えず変化するサイバー脅威に対応していくうえで、こうした実践とツールは、アプリケーションのセキュリティと完全性を維持するために欠かせません。

JavaScriptのセキュリティを守るSnyk

最初のコードから最後のnpm依存関係まで、SnykはIDE、CLI、GitのワークフローからJavaScriptアプリケーションを安全に保ちます。

続きを読む

Blog

フロンティアモデルは脆弱性を発見した。攻撃者だけがエクスプロイトチェーンを見つけた。

静的解析で欠陥は見つかりましたが、ライブ攻撃テストで侵害につながる連鎖を実証できたのは唯一でした。Evo COS、Claude Security、Claude Code Securityを比較します。

feature insights context
Blog

自律型攻撃はすでに始まっている。防御もそのスピードに追いつかなければならない。

自律型攻撃者によって、防御に使える時間は短くなっています。継続的な検出、修復、検証、予防で、セキュリティチームが攻撃に歩調を合わせる方法をご紹介します。

Blog

AIコーディングエージェントが不適切なアクセス制御を繰り返し実装する理由

AIコーディングエージェントは、コンパイルが通りレビューも通過する一方で、あるテナントのデータを別のテナントに公開してしまう認可ロジックを生成することがあります。不適切なアクセス制御が検出しにくい理由と、その防止策をご紹介します。