1. 平文は保存されず、応答にハッシュも含まれない
  2. 同じパスワードでも salt が違えば結果が変わる
コース一覧
Node.js入門
パスワードハッシュ

Node.js入門

Node.jsの実行モデル、npm、Expressによるルーティング、REST API設計、データベース接続、トランザクションを使った購入処理、トークン認証まで、バックエンド開発の基礎を、毎レッスン実際にコードを書き、フリマアプリのAPIを自分の手で作りながら学びます。

1
Node.jsとは
01. Node.jsとは15分
02. スクリプトを実行する15分
03. npmとは15分
04. package.json15分
05. npmスクリプト15分
06. つくる:プロジェクト始動20分
07. 第1章クイズ10分
2
モジュールと非同期の復習
01. import/export15分
02. ファイルを読み書きする15分
03. async/await復習15分
04. JSONの取り回し15分
05. つくる:データ層20分
06. 第2章クイズ10分
3
はじめてのサーバー
01. サーバーとリクエスト15分
02. Expressを導入する15分
03. ルーティング15分
04. JSONを返す15分
05. 開発の回し方15分
06. つくる:サーバー始動20分
07. 第3章クイズ10分
4
REST APIのきほん
01. RESTの考え方15分
02. 一覧を返す15分
03. 1件を返す15分
04. 登録を受ける15分
05. 更新と削除15分
06. ステータスコード15分
07. つくる:商品CRUD完成20分
08. 第4章クイズ10分
5
リクエスト処理と検証
01. クエリパラメータ15分
02. 並び替えとページング15分
03. 入力検証15分
04. ミドルウェア15分
05. エラーハンドリング15分
06. つくる:検索API完成20分
07. 第5章クイズ10分
6
データベース接続
01. DBにつなぐ15分
02. 一覧・詳細をDB化15分
03. 出品をDB化15分
04. SQLインジェクション20分
05. 更新・削除をDB化15分
06. 購入トランザクション20分
07. つくる:DB版fleama20分
08. 第6章クイズ10分
7
認証のきほん
01. 認証と認可15分
02. パスワードハッシュ15分
03. ログインとトークン15分
04. 保護ルート15分
05. 本人のものだけ15分
06. つくる:認証完備20分
07. 第7章クイズ10分
8
総合制作
01. fleama API完成20分
02. Reactと接続する20分
03. サーバーで常駐させる20分
04. 自由拡張20分
05. 完成と次のステップ15分

パスワードハッシュ

そのまま保存してはいけない

会員登録を作るとき、いちばんやってはいけないのがこれです。

JavaScript

db.prepare("INSERT INTO users (name, password) VALUES (?, ?)").run(name, password);

プレースホルダは正しく使っています。ですがパスワードがそのまま保存されています。

データベースが漏れたとき、全員のパスワードがそのまま読まれます。しかも多くの人は同じパスワードを他のサービスでも使っているので、被害は fleama の中では終わりません。

元に戻せない形にする

ハッシュ化は、値を元に戻せない形に変える処理です。

JavaScript

const crypto = require("crypto"); const hashed = crypto.scryptSync(password, salt, 32).toString("hex");

同じ入力からは必ず同じ結果が出ます。逆に、結果から元のパスワードを求めることはできません。

保存するのはこの結果だけです。ログインのときは、送られてきたパスワードを同じ手順でハッシュ化して、保存してあるものと一致するかを見ます。元に戻す必要はありません。

salt を混ぜる

salt は、ハッシュ化のときに一緒に混ぜる文字列です。

これが無いと、同じパスワードを使っている人は同じ結果になります。漏れたときに「この 2 人は同じパスワードだ」と分かってしまいますし、よくあるパスワードの結果を並べた表と照合されると元が割れます。

利用者ごとに違う salt を混ぜれば、同じパスワードでも結果が変わります。salt 自体は秘密ではないので、ハッシュと一緒に保存して構いません。

比べ方にも作法がある

JavaScript

if (hashed === stored) { ... }

動きますが、より安全な比べ方があります。

JavaScript

crypto.timingSafeEqual(Buffer.from(hashed), Buffer.from(stored));

=== は違いが見つかった時点で比較をやめるので、一致した文字数によって処理時間がわずかに変わります。その差を測って 1 文字ずつ当てていく攻撃があります。timingSafeEqual は最後まで比べるので、時間から情報が漏れません。

返すときも気をつける

登録の応答に、ハッシュを含めてはいけません。

JavaScript

res.status(201).json({ id: user.id, name: user.name });

第3章から繰り返してきた「返すものは選ぶ」が、いちばん効く場面です。

手を動かす

演習では、会員登録の処理を書きます。保存する形と、返す形の両方に気を配ってください。

要件

  1. パスワードをハッシュ化して password_hash 列に保存する(平文は保存しない)
  2. stored は保存された {id, name, password_hash} を返す
  3. response は {id, name} だけにして、ハッシュも平文も含めない

入出力例

register("田中", "himitsu", "salt-a") → {"response":{"id":1,"name":"田中"},"stored":{"id":1,"name":"田中","password_hash":"704b7637ecc20719df70b70e9fae8b384de6f9c0281ec1823b72c090c46ce731"}} register("鈴木", "himitsu", "salt-b") → {"response":{"id":1,"name":"鈴木"},"stored":{"id":1,"name":"鈴木","password_hash":"1ea22361123475abc9f1c31a29a8bebe87b3d69c7752eda0152985e7c3d05bff"}}

ヒント

初期コードは `password` をそのまま保存しています。ここが直すところです

ハッシュ化は `crypto.scryptSync(password, salt, 32).toString("hex")` です

`response` は `stored` をそのまま渡さず、`id` と `name` だけの新しいオブジェクトにします

生田 陸人
監修生田 陸人
ゆめさくエンジニア / 現役ソフトウェアエンジニア監修者プロフィールを見る →
編集 ゆめさく編集部·公開 2026/08/10

関連レッスン

  • ログインとトークン

    ログインAPIがトークンを返すようにし、トークンを発行できるようになります。

  • 保護ルート

    出品と購入をログイン必須にし、トークンが要るAPIを作れるようになります。

  • 本人のものだけ

    自分の出品だけ編集できるようにし、認可チェックを書けるようになります。

  • つくる:認証完備

    登録からログイン・出品・編集までを通し、認証を備えたAPIを完成できるようになります。

このレッスンに出てくる用語

意味があいまいなまま進んだ語は、ここから読み直せます。

  • プレースホルダSQL の `?` 部分にあとから値を入れる仕組み
  • パスワードDBが漏洩しても元のパスワードがわからないようにするため。
  • データベースデータを構造化して保存する仕組み
  • ハッシュ化元に戻せない一方向変換でパスワードを保管
  • 処理計算や代入を表す長方形
main.js
学習モード
エディタを読み込んでいます

メモ

パスワードハッシュ

⌘S で保存