Node.jsのcryptoを使用した暗号・複合とハッシュ化方法

Node.jsのcryptoモジュールは、暗号化、復号化、ハッシュ、署名などの暗号学的な操作を実行するためのツールセットを提供します。

cryptoモジュールを使用して解決できる課題は以下のものがあります。

  • データの暗号化、複合化による整合性の検証
  • パスワードのハッシュ化と比較
  • デジタル署名を生成および検証

今回の記事では暗号・複合とハッシュ化の方法について取り上げていきます。

暗号・複合

以下は、Node.jsのcryptoモジュールを使用して文字列を暗号化し、その後復号化するサンプルコードです。

const crypto = require("crypto");

// 鍵を作成
function generateKey() {
  const password = "passwordpassword";
  const salt = "Aeyee7haegeasheZ"; // 16バイトの長さであることが推奨
  const key = crypto.scryptSync(password, salt, 32);
  return key;
}

// 暗号化
function encrypt(text) {
  const key = generateKey();
  const iv = crypto.randomBytes(16);
  const cipher = crypto.createCipheriv("aes-256-cbc", key, iv);
  let encryptedText = cipher.update(text, "utf8", "hex");
  encryptedText += cipher.final("hex");
  return { iv, encryptedText };
}

// 復号化
function decrypt(text, iv) {
  const key = generateKey();
  const decipher = crypto.createDecipheriv("aes-256-cbc", key, iv);
  let decrypted = decipher.update(text, "hex", "utf8");
  decrypted += decipher.final("utf8");
  return decrypted;
}

const originalText = "これはテストメッセージです。";
console.log("元のテキスト:", originalText);

const {iv, encryptedText} = encrypt(originalText);
console.log("暗号化されたテキスト:", encryptedText);
console.log("iv:", iv);

const decryptedText = decrypt(encryptedText, iv);
console.log("復号化されたテキスト:", decryptedText);
% node sample.js
元のテキスト: これはテストメッセージです。
暗号化されたテキスト: 85db00d665c2f23ec4e55394cd85e89c6be373aa7f59c0f2bcb49c98f7a67b75512d6cef980b9dc4f189eb6399296d35
iv: <Buffer 97 90 02 56 0c 15 91 f2 77 98 0f a2 cc f2 ab 8a>
復号化されたテキスト: これはテストメッセージです。
% node sample.js
元のテキスト: これはテストメッセージです。
暗号化されたテキスト: 0a7c4e97e298837726def8c4ca48cfe5ebb9b2868f580937abf7fb615ea701a75cb723a004c693828a93a51f7b8d1c93
iv: <Buffer 19 43 dc f1 be 0e 51 e9 36 d2 2e 40 31 ca ea 3a>
復号化されたテキスト: これはテストメッセージです。

このコードでは、AES-256-CBC暗号化アルゴリズムを使用してテキストを暗号化し、同じ鍵と初期化ベクトルを使用して復号化しています。

注意点として、実際のアプリケーションでは

  • 暗号を行う側と複合を行う側で passwordsalt を事前に共有して秘密にする必要があります
  • iv(初期化ベクトル)は毎回変更される必要があります
  • ivと暗号化テキストは複合側に同時に渡します
  • ivと暗号化テキストは秘密にする必要はありません

AES-256-CBC暗号化アルゴリズムは、データの保護とプライバシーを強化するために広く使用されています。

ただし、注意すべきポイントもあります。

例えば、同じ鍵とivを再利用するとセキュリティが低下する可能性があるため、ランダムで予測困難なivを使用することが重要です。また、正確な鍵管理とセキュリティプラクティスの遵守も不可欠です。

例えば、鍵(コード内だとkey)の指定は文字列でも可能ですが、passwordsaltを使用して、リポジトリ内にハードコーディングしない等の鍵管理が必要です。

AES-256-CBCは、暗号通信やデータの保護など、多くのセキュリティ関連のシナリオで信頼性の高い選択肢となります。

ハッシュ化

ハッシュ化とは暗号・複合とは違い、一方向性を持つので元のデータを逆算する事が出来ないものを指します。主にパスワードの平文保存を回避するために使用されます。

以下は、Node.jsのcryptoモジュールを使用してソルト付きハッシュを生成するサンプルコードです。

const crypto = require("crypto");

function generateSalt() {
  return crypto.randomBytes(16).toString("hex"); // ランダムなソルト生成
}

function hashWithSalt(data, salt) {
  const algorithm = "sha256";
  const hash = crypto.createHash(algorithm);

  // ソルトとデータを結合してハッシュに追加
  const saltedData = salt + data;
  hash.update(saltedData);

  // ハッシュ値を取得
  return hash.digest("hex");
}

// パスワード
const password = "password123";

// ソルトの生成
const salt = generateSalt();

// ソルト付きハッシュの生成
const saltedHash = hashWithSalt(password, salt);

console.log("パスワード:", password);
console.log("生成されたソルト:", salt);
console.log("ソルト付きハッシュ:", saltedHash);

このサンプルコードでは、ランダムなソルトを生成して、ソルト付きのパスワードをハッシュ化しています。

ユーザーごとに異なるソルトを使用することで、セキュリティを向上させることができます。

実際のアプリケーションではソルトと生成されたハッシュの両方を保存して、検証する必要があります。

ソルト値をハッシュに含めるbcrypt

パスワードの保存にソルト値を含めるのはセキュリティの向上に役立ちますが、cryptoモジュールを使う場合ではソルト値とハッシュ値を別々に保存する必要があります。

ハッシュ値の中にソルト値等を含める事ができるパスワードハッシュ化関数として bcrypt を使用する方法があります。

https://ja.wikipedia.org/wiki/Bcrypt

bcryptライブラリは、ハッシュ化する際にソルトを自動的に生成してくれるなど、セキュリティを高めるための機能が組み込まれています。

一方、cryptoモジュールはハッシュ関数を提供していますが、ソルトの生成や適切なセキュリティプラクティスの実装に関しては、自分で行う必要があります。

以下は、bcryptを使用してパスワードをハッシュ化するサンプルコードです。

$ npm install --save bcrypt
const bcrypt = require("bcrypt");

// パスワード
const password = "password123";

// パスワードをハッシュ化
bcrypt.hash(password, 10, (err, hash) => {
  if (err) {
    console.error("ハッシュ化エラー:", err);
    return;
  }
  console.log("生成されたハッシュ:", hash);

  console.log("ハッシュの照合:", bcrypt.compareSync(password, hash));
});
 % node sample.js
生成されたハッシュ: $2b$10$9KHWQQo.wlwu4E21ypTu7eZt.ChZDbzqIXByGuZ8lgtZW9TMgYMRO
ハッシュの照合: true
 % node sample.js
生成されたハッシュ: $2b$10$6jDAOfP03ey0tktsnuB4b.EHEv66ZqrpxkCAABDOnttbElqy0L3du
ハッシュの照合: true

このサンプルコードでは、bcryptモジュールのhash関数を使用してパスワードをハッシュ化しています。

第2引数にはストレッチングの回数を指定しており、回数が多いほど計算が時間がかかり、攻撃者が総当たり攻撃をしにくくなります。

bcryptライブラリを使用することで、適切なソルト生成やセキュリティプラクティスを実装する手間を減らすことができ、セキュリティを強化することができます。

最後に

以上、Node.jsのcryptoを使用した暗号・複合とハッシュ化について紹介していきました。

詳細な使用方法については公式のドキュメントも参照することをオススメします

https://nodejs.org/api/crypto.html