🔐

[基本情報の問題] パスワードのハッシュとは?

2025/01/05に公開

なぜパスワードをハッシュ値にするのか?

ユーザーのパスワードを平文の状態で保存して置くことは大変危険で、サービスを運営する側としても漏洩などのリスクが伴います。
サービスを運営する側はパスワード管理を行わずに認証を行いたい・・・
ユーザー側もサービスを運営する人にパスワードを見られたくない・・・
そこで考え出されたのがパスワードの代わりにハッシュ値を保存して認証する考え方です。

ハッシュ値とは

ハッシュは、パスワードなどを特定のアルゴリズムに基づいて固定長の値(ハッシュ値)に変換することです。

  1. ハッシュ値から元のパスワードに戻せない
  2. 同じ入力は同じハッシュ値になる
  3. 異なる入力は異なるハッシュ値になる(衝突回避)

問題

基本情報技術者 平成26年春期 午前問42
https://www.fe-siken.com/kakomon/26_haru/q42.html

基本情報技術者 平成23年秋期 午前問42
https://www.fe-siken.com/kakomon/23_aki/q42.html

基本情報技術者 平成30年春期 午後問1
https://www.fe-siken.com/kakomon/30_haru/pm01.html

ハッシュ値での認証の考え方

パスワードの登録

  1. パスワードを入力
  2. 入力されたパスワードをハッシュ値にして保存する
  3. 入力されたパスワードを消す

パスワードの認証

  1. パスワードを入力
  2. 入力されたパスワードをハッシュ値にして保存する
  3. 入力されたパスワードを消す
  4. 保存されたハッシュ値(登録時のハッシュ値と認証時のハッシュ値)が一致しているか確認する

プログラムで動的に確認

環境

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