🔐
[基本情報の問題] パスワードのハッシュとは?
なぜパスワードをハッシュ値にするのか?
ユーザーのパスワードを平文の状態で保存して置くことは大変危険で、サービスを運営する側としても漏洩などのリスクが伴います。
サービスを運営する側はパスワード管理を行わずに認証を行いたい・・・
ユーザー側もサービスを運営する人にパスワードを見られたくない・・・
そこで考え出されたのがパスワードの代わりにハッシュ値を保存して認証する考え方です。
ハッシュ値とは
ハッシュは、パスワードなどを特定のアルゴリズムに基づいて固定長の値(ハッシュ値)に変換することです。
- ハッシュ値から元のパスワードに戻せない
- 同じ入力は同じハッシュ値になる
- 異なる入力は異なるハッシュ値になる(衝突回避)
問題
基本情報技術者 平成26年春期 午前問42
基本情報技術者 平成23年秋期 午前問42
基本情報技術者 平成30年春期 午後問1
ハッシュ値での認証の考え方
パスワードの登録
- パスワードを入力
- 入力されたパスワードをハッシュ値にして保存する
- 入力されたパスワードを消す
パスワードの認証
- パスワードを入力
- 入力されたパスワードをハッシュ値にして保存する
- 入力されたパスワードを消す
- 保存されたハッシュ値(登録時のハッシュ値と認証時のハッシュ値)が一致しているか確認する
プログラムで動的に確認
環境
MacBook M4
- Homebrew
- VSCode
- Apple clang version 16.0.0
- OpenSSL 3.4.0 22 Oct 2024
OpenSSLのインストール
brew install openssl
プログラム
hashPassword関数でハッシュ値を生成します。
(ハッシュ値の生成はOpenSSLのSHA256を使用しています。)
https://docs.openssl.org/3.4/man3/SHA256_Init/
OpenSSL Documentation SHA256_Init
main関数は「ハッシュ値での認証の考え方」を実行します。
以下にプログラムを示します。
hash_password.cpp
#include <iostream>
#include <iomanip>
#include <string>
#include <sstream>
#include <openssl/sha.h>
// パスワードをSHA-256でハッシュ化する関数
std::string hashPassword(const std::string &password)
{
// SHA256の出力サイズは32バイト
unsigned char hash[SHA256_DIGEST_LENGTH];
// ハッシュ化
SHA256(reinterpret_cast<const unsigned char *>(password.c_str()), password.size(), hash);
// ハッシュ値を16進数文字列に変換
std::ostringstream oss;
for (int i = 0; i < SHA256_DIGEST_LENGTH; ++i)
{
oss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(hash[i]);
}
return oss.str();
}
int main()
{
// ユーザーからパスワードを入力
std::string password;
std::cout << "Enter password: ";
std::cin >> password;
// パスワードをハッシュ化
std::string hashedPassword = hashPassword(password);
std::cout << "Hashed password (SHA-256): " << hashedPassword << std::endl;
// パスワードをメモリクリア
std::fill(password.begin(), password.end(), '\0');
std::cout << "Password clear: " << password << std::endl;
// パスワード認証を入力
std::string tryPassword;
std::cout << "Enter tryPassword: ";
std::cin >> tryPassword;
// パスワード認証をハッシュ化
std::string tryHashedPassword = hashPassword(tryPassword);
std::cout << "tryHashedPassword (SHA-256): " << tryHashedPassword << std::endl;
// パスワード認証をメモリクリア
std::fill(tryPassword.begin(), tryPassword.end(), '\0');
std::cout << "Password clear: " << tryPassword << std::endl;
// パスワード認証
if (hashedPassword == tryHashedPassword)
std::cout << "Successful" << std::endl;
else
std::cout << "Failed" << std::endl;
return 0;
}
VSCodeにパスを設定する
#include <openssl/sha.h>
に赤波線が出る場合はC/C++ 構成に
/opt/homebrew/opt/openssl/include
を設定する。
以下に設定の画像を示します。
実行ファイルの作成
g++ -o hash_password hash_password.cpp -I/opt/homebrew/Cellar/openssl@3/3.4.0/include -L/opt/homebrew/Cellar/openssl@3/3.4.0/lib -lssl -lcrypto
実行結果
認証成功
Enter password: 123
Hashed password (SHA-256): a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3
Password clear:
Enter tryPassword: 123
tryHashedPassword (SHA-256): a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3
Password clear:
Successful
認証失敗
Enter password: ひらけごま
Hashed password (SHA-256): ad86666a45fd1319752263c9981520a5f7a2d20ee0207d36bc634c53a4251b1d
Password clear:
Enter tryPassword: ひらけとびら
tryHashedPassword (SHA-256): 6470c90d412baa83cededcdb9f297bb72048970428f94e9b3bbd91bdc1f1de38
Password clear:
Failed
解説
以下に解説画像を示します。
このように、パスワードそのものを保存せずにハッシュ値を用いて認証を行うことで、データベースなどの漏洩リスクを軽減できます。ただし、さらに安全性を高めるためには、ソルトを加えてハッシュ化を行ったり、ストレッチングを用いて計算コストを増大させることも重要です。これにより、総当たり攻撃や辞書攻撃への耐性を強化できます。
Discussion