Linux におけるパスワードハッシュ

最終更新: 2025-08-08 11:46:18 +0800 +0800
原文は英語で書かれています。あなたはAI翻訳版を読んでいます。

パスワードの保存には、暗号化ではなく一方向のパスワードハッシュ関数を使用します。検証側は、保存値に埋め込まれた設定を使用して入力されたパスワードをハッシュし、エンコード結果を比較します。平文パスワードの復元は設計に含まれません。データベースを入手した攻撃者は、代わりに候補パスワードをオフラインでテストします。したがって、現代のパスワードハッシュには、パスワードごとのランダムなソルトと、意図的に高コストでサポート対象のアルゴリズムが必要です。

crypt のエンコード済みハッシュ #

歴史的な crypt(3) インターフェースは、パスワードと設定を受け取ります。そのモジュラー形式は一般に次のようになります。

text
$id$parameters$salt$checksum

識別子とパラメーター構文によってアルゴリズムを選択します。ソルトにより、同じパスワードでも異なる保存値が生成されます。チェックサムは検証値です。検証時には保存された文字列全体を設定として渡すため、アプリケーションはその文字列を正確に保持する必要があります。一方、従来の DES crypt は 2 文字のソルトを使用するため、新しいパスワードハッシュには適していません。

glibc の旧 libcrypt は $1$(MD5)、$5$(SHA-256)、$6$(SHA-512)を文書化しており、SHA 形式は rounds=N を受け付けます。歴史的な互換性があっても、MD5 や DES が新規導入に適するわけではありません。利用可能な方式と受け付ける識別子は、移植可能な POSIX パスワード形式ではなく、実装とビルド設定の詳細です。

crypt は静的ストレージを返すため、並行呼び出しに対して安全ではありません。crypt_r は呼び出し側が提供する struct crypt_data を使用します。この構造体は実装の要件に従って初期化し、スレッド間で共有してはなりません。比較前に失敗を確認してください。実装によっては NULL または無効な値を返す場合があります。アプリケーション自身がハッシュを比較する場合は定数時間比較を使用し、API が許す限り早くパスワード情報を消去してください。

c
struct crypt_data data = {0};
char *candidate = crypt_r(password, stored_hash, &data);
if (candidate != NULL && constant_time_equal(candidate, stored_hash)) {
    /* authenticated */
}

この例が示すのは検証の仕組みであり、ログインアーキテクチャではありません。PAM 対応プログラムは、アカウントデータベースを読み取ったり、プロンプトやスロットリングを独自に実装したりせず、認証を PAM に委任すべきです。

glibc と libxcrypt の互換性 #

crypt はオプションの POSIX XSI インターフェースです。glibc はバージョン 2.39 でツリー内の libcrypt を削除しました。Linux ディストリビューションでは、代わりに libxcrypt が一般的に提供されています。libxcrypt は、glibc と互換性のあるシンボルバージョンを持つ libcrypt.so.1 を提供できるため、glibc の歴史的なライブラリにリンクされた既存の Linux バイナリは引き続きロードできます。その互換性なしで設定されたシステムでは異なる ABI 名がインストールされるため、ビルド時と実行時の互換性をそれぞれ確認する必要があります。

libxcrypt は crypt と crypt_r に加えて、Openwall 由来のインターフェースを提供します。

  • crypt_rn は、明示的にサイズを指定した呼び出し側バッファーを受け取ります。
  • crypt_ra は、作業用ストレージを割り当てるかサイズ変更します。
  • crypt_gensalt、crypt_gensalt_rn、crypt_gensalt_ra は設定とソルトを生成します。

libxcrypt は、有効化されたビルドでの yescrypt を含め、設定可能な一連の歴史的方式と現代的方式をサポートします。識別子だけからサポート状況を推測してはなりません。ディストリビューションがサポートするパスワードポリシーを選択し、OS の暗号学的乱数ソースでソルトを生成し、その方式について文書化されたコストパラメーターを使用してください。コストを上げるとすべての対話的ログインに影響するため、キャパシティーテストが必要です。移行では、認証成功後に各アカウントの再ハッシュが完了するまで、既存ハッシュの検証サポートを維持する必要があります。

shadow の所有権 #

プロセスが数値 ID をアカウント名に対応付けられるよう、/etc/passwd は読み取り可能でなければなりません。shadow を使用するシステムでは、そのパスワードフィールドは一般に x であり、パスワードハッシュと有効期限情報は /etc/shadow に格納されます。shadow レコードには、アカウント名、エンコード済みハッシュまたはロックマーカー、最終変更日、最小および最大有効日数、警告および無効期間、失効日、予約フィールドが含まれます。

shadow ファイルは特権アカウントデータです。その所有者とモードはディストリビューションのポリシーによって異なりますが、一般ユーザーがハッシュを読み取れる状態にしてはなりません。shadow suite は passwd、chpasswd、pwconv、pwunconv などのユーティリティを管理しています。コードで /etc/shadow を直接解析して書き換えるべきではありません。NSS ルックアップ API は shadow データを読み取る場合がありますが、アクセス制御は引き続き適用されます。

認証と移行 #

ログイン時、PAM 対応サービスは PAM トランザクションを開始します。pam_unix などのモジュールは、アプリケーションの対話コールバックを通じて資格情報を取得し、必要な権限で設定済みアカウントデータを読み取り、設定済みのハッシュ実装を呼び出して、PAM スタックに成功または失敗を報告します。アカウントの有効期限、ロック状態、多要素方式、ディレクトリ認証、およびレート制限ポリシーは、そのスタックと支援サービスで扱います。

移行時には、次の手順を実行します。

  1. アカウント管理ポリシーで、サポート対象の現行アルゴリズムとそのコストを設定する。
  2. 移行期間中に限り、旧方式の検証を有効に保つ。
  3. 次回のパスワード変更成功時またはログイン検証成功時に再ハッシュする。
  4. 移行を完了できないアカウントを失効またはリセットする。
  5. 旧方式を必要とする保存済みハッシュがなくなった後、旧アルゴリズムを削除する。

libxcrypt のインターフェース文書、crypt(3)、および shadow(5) では、プラットフォーム固有の契約について説明しています。

コメント

GitHub で閲覧・コメントする(投稿にはログインが必要です)