ハッシュ化で用いる「ソルト(Salt)」や「ペッパー(Pepper)」とは?わかりやすく解説!

みなさんは「ソルト(Salt)」や「ペッパー(Pepper)」というIT用語を聞いたことがありますか?

料理の「塩」や「胡椒」と同じ単語ですが、ITの世界では、これらの用語はパスワードの安全性を高めるために使う重要な技術を指します。

「パスワードに塩や胡椒をかけるってどういうことでしょうか?」

この記事では、「ソルト(Salt)」や「ペッパー(Pepper)」について、以下の内容をサンプルコードを用いてわかりやすく解説します。

  • 【結論】パスワードを安全に守る方法
  • そもそもハッシュ化とは?
    • ハッシュ化のメリットと課題
  • ソルト(Salt)とは?
    • ソルトの使い方
    • ソルトの課題
  • ペッパー(Pepper)とは?

【結論】パスワードを安全に守る方法

【結論】パスワードを安全に守る方法

パスワードを安全に守るには、ハッシュ化だけでは不十分です。ハッシュ化だけでなく、「ソルト(Salt)」や「ペッパー(Pepper)」など複数の対策を組み合わせるのが、今やセキュリティの常識です。

セキュリティレベル方法
弱いパスワードのみ」をハッシュ化する
普通パスワード + ソルト(Salt)」をハッシュ化する
強いハッシュ化 + ソルト(Salt) + ペッパー(Pepper)」をハッシュ化する

補足

「ストレッチング(Stretching)」という技術も使うと、セキュリティをさらに強力にすることができます。ストレッチングは、ハッシュ化の際にハッシュ処理を何千回、何万回と繰り返して時間をかけることで、攻撃者による攻撃を困難にする技術です。通常のハッシュは一瞬で計算できるため、短時間で大量に試されてしまいますが、ストレッチングを使えば1回の計算コストが高くなり、攻撃者にとって膨大な時間とリソースが必要になります。

そもそもハッシュ化とは?

ハッシュ化は、元データ(例:パスワード等の文字列)をハッシュ値(文字列)に不可逆変換することです。不可逆変換なので、ハッシュ値から元データには戻すことができません。

なお、ハッシュ化を実行する関数のことをハッシュ関数と呼びます。ハッシュ関数は様々な種類があり、MD5やSHA-256などが有名ですが、今回はMD5関数を使用したサンプルコードを以下に示します。

const crypto = require('crypto');

const password = '123456';
const hashedOnly = crypto.createHash('md5').update(password).digest('hex');

// --- ログ出力 ---
console.log('ハッシュ結果:', hashedOnly);
// ハッシュ結果: e10adc3949ba59abbe56e057f20f883e

あわせて読みたい

ハッシュ関数』については下記の記事で詳しく説明しています。興味のある方は下記のリンクからぜひチェックをしてみてください。

ハッシュ化のメリットと課題

ユーザーIDに対するパスワードをそのままデータベースに保存している場合、SQLインジェクションなどでデータベースの内容が漏洩すると、非常に危険です。そこで、例えば、パスワードそのものではなく、パスワードのハッシュ値を保存することで、漏洩時のリスクを減らすことができます。

しかし、ハッシュ化にも課題があります。それは、不可逆変換とはいえ、同じ入力値なら必ず同じハッシュ値になることです。そのため、「レインボーテーブル攻撃(逆引き辞書攻撃)」と呼ばれる攻撃によってパスワードを逆引きすることができてしまいます。ユーザーがよく使うパスワード(例:"123456"や"password")を使用していたら、一瞬で逆引きされてしまうリスクがあるのです。

レインボーテーブル攻撃とは

レインボーテーブル攻撃とは、「よく使われるパスワードとそのハッシュ値の大量の対応表(レインボーテーブル)」を事前に用意し、漏洩したパスワードのハッシュ値から元のパスワードを高速に逆引きする攻撃手法です。

ソルト(Salt)とは?

そこで登場するのが、ソルト(Salt)です。ソルト(Salt)を用いると、ハッシュ化の課題(レインボーテーブル攻撃)を回避することができます。

ソルト(Salt)は元データ(例:パスワード等の文字列)に付け加える「ランダムな文字列」です。パスワードにソルト(Salt)を加えてからハッシュ化することで、同じパスワードでも、違うハッシュ値を生成することができます。サンプルコードを以下に示します。

const crypto = require('crypto');

const password = '123456';

// ソルトをランダムに生成(例:16バイトのランダム値)
const salt = crypto.randomBytes(16).toString('hex');

// --- ハッシュ化のみ ---
const hashedOnly = crypto.createHash('md5').update(password).digest('hex');

// --- ハッシュ化 + ソルト ---
const saltedPassword = password + salt;
const hashedWithSalt = crypto.createHash('md5').update(saltedPassword).digest('hex');

// --- ログ出力 ---
console.log('生成されたソルト:', salt);
console.log('ハッシュ化のみ:', hashedOnly);
console.log('ハッシュ化 + ソルト:', hashedWithSalt);
// 生成されたソルト: fc5192646d56998f73dfa01330a68613
// ハッシュ化のみ: e10adc3949ba59abbe56e057f20f883e
// ハッシュ化 + ソルト: 9a3ca6189453e1ad1273be3b19980767

レインボーテーブル攻撃を防ぐことができる理由

攻撃者は「よく使われるパスワードとそのハッシュ値の大量の対応表(レインボーテーブル)」を使って、元のパスワードを特定しようとします。"123456"のようなよく使われるパスワードでもソルト(Salt)を使用することで、異なるハッシュ値に変換されるので、レインボーテーブル攻撃を回避することができます。

ソルトの使い方(基本の流れ)

ソルト(Salt)の使い方を以下にまとめます。

  • ソルト(Salt)をランダムに生成する
  • パスワードにソルト(Salt)を付けてハッシュ化する
  • ハッシュ値とソルト(Salt)をセットで保存する
  • ログイン時は、入力パスワードに保存されたソルト(Salt)を付けてハッシュ化し、保存ハッシュと比較する

ソルトの課題

一見万能そうに見えるソルト(Salt)にも課題があります。それは「ソルト(Salt)もパスワードのハッシュ値と一緒にデータベースに保存されることが多い」ということです。そのため、データベースが漏洩したら、ハッシュ値とセットでソルト(Salt)も漏洩してしまっています。漏洩したソルト(Salt)を使って簡単にハッシュ値から元のパスワードを特定できてしまうのです。

ペッパー(Pepper)とは?

そこで登場するさらに強力な手段がペッパー(Pepper)です。

ペッパー(Pepper)はパスワードハッシュ値やソルト(Salt)と同時に漏洩しないように、データベースとは別の安全な場所(環境変数など)に保管して使いますペッパー(Pepper)は秘密に管理するソルトのようなものなので、シークレットソルト(Secret salt)と呼ばれることもあります。

サンプルコードを以下に示します。

const crypto = require('crypto');
require('dotenv').config(); // .envファイルを読み込む

const password = '123456';
const pepper = process.env.PEPPER_SECRET; // 環境変数から取得

// ソルトをランダムに生成(例:16バイトのランダム値)
const salt = crypto.randomBytes(16).toString('hex');

// --- ハッシュ化のみ ---
const hashedOnly = crypto.createHash('md5').update(password).digest('hex');

// --- ハッシュ化 + ソルト ---
const saltedPassword = password + salt;
const hashedWithSalt = crypto.createHash('md5').update(saltedPassword).digest('hex');

// --- ハッシュ化 + ソルト + ペッパー ---
const saltedAndPepperedPassword = password + salt + pepper;
const hashedWithSaltAndPepper = crypto
  .createHash('md5')
  .update(saltedAndPepperedPassword)
  .digest('hex');

// --- ログ出力 ---
console.log('生成されたソルト:', salt);
console.log('ハッシュ化のみ:', hashedOnly);
console.log('ハッシュ化 + ソルト:', hashedWithSalt);
console.log('ハッシュ化 + ソルト + ペッパー:', hashedWithSaltAndPepper);
// 生成されたソルト: b222e0b0504f1f1e40dcebe6f02f496d
// ハッシュ化のみ: e10adc3949ba59abbe56e057f20f883e
// ハッシュ化 + ソルト: 5190e23375046dcdec35d0d627152bbc
// ハッシュ化 + ソルト + ペッパー: f316f156fe2c443c5d3873b290e0e871

データベースが丸ごと流出しても、ペッパー(Pepper)が分からないと、元のパスワードが分からないため、セキュリティを強固にすることができます。

本記事のまとめ

この記事では「ソルト(Salt)」や「ペッパー(Pepper)」について、以下の内容を説明しました。

  • ハッシュ化だけではパスワード漏洩リスクがある。
  • ソルトを加えることでハッシュ値をバラバラにでき、レインボーテーブル攻撃を防ぐことができる。
  • ペッパーを使えば、さらにセキュリティレベルが上がる。
  • 実運用ではストレッチング(ハッシュ繰り返し)も取り入れるとセキュリティがより強固になる。

お読み頂きありがとうございました。