ユーザーの認証情報はコンピューター内で、どのように管理されているのでしょうか?
ユーザーを追加してパスワードを設定すると、それはどこに保存されるのか。
ユーザーが入力した認証情報はどのように処理されて、コンピューターはログインを許可するのか。
Linuxが認証情報を処理する方法について解説します。
ここで使用するLinuxディストリビューションは「Debian12 Bookwarm」です。
ユーザー追加する
ユーザー名「yamada」、パスワード「Yama5Da」でユーザーを追加します。
adduser yamada
adduserは対話的にユーザーを追加するコマンドです。
root@debian:/# adduser yamada
Adding user `yamada' ...
Adding new group `yamada' (1001) ...
Adding new user `yamada' (1001) with group `yamada (1001)' ...
Creating home directory `/home/yamada' ...
Copying files from `/etc/skel' ...
新しい パスワード: Yama5Da⏎キー
新しい パスワードを再入力してください: Yama5Da⏎キー
passwd: パスワードは正しく更新されました
yamada のユーザ情報を変更中
新しい値を入力してください。標準設定値を使うならリターンを押してください
フルネーム []: ⏎キー
部屋番号 []: ⏎キー
職場電話番号 []: ⏎キー
自宅電話番号 []: ⏎キー
その他 []: ⏎キー
Is the information correct? [Y/n]
⏎キー
Adding new user `yamada' to supplemental / extra groups `users' ...
Adding user `yamada' to group `users' ...
id コマンドで確認します。
root@debian:/# id yamada
uid=1001(yamada) gid=1001(yamada) groups=1001(yamada),100(users)
ホームディレクトリyamadaも生成されています。
root@debian:/# ls /home/
tanaka yamada yoshida
間違いやすいのですが、「useradd」というコマンドもユーザーを追加します。
「useradd」は「adduser」のように親切ではないのでパスワードやホームディレクトリは作ってくれません。
ユーザー情報はどこに保存されているのか?
ユーザー情報は次のファイルに保存されています。
/etc/passwd
/etc/shadow
「/etc/passwd」にはID番号や使用するシェルなどのユーザー情報が保存されています。
root@debian:/# cat /etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
...
yamada:x:1001:1001:,,,:/home/yamada:/bin/bash
yoshida:x:1002:1002:,,,:/home/yoshida:/bin/bash
tanaka:x:1003:1003:,,,:/home/tanaka:/bin/bash
root@debian:/#
/etc/shadow にはユーザーごとの認証情報(パスワード)が保存されています。
root@debian:/# cat /etc/shadow
root:*:19541:0:99999:7:::
daemon:*:19541:0:99999:7:::
bin:*:19541:0:99999:7:::
sys:*:19541:0:99999:7:::
...
yamada:$y$j9T$b8bD5o1nZrTL4lwrcq2Pk0$Br.AW5RCWz8qTYVxp.H5fg2X2CFfgPXjpEwCEo5FJG3:19571:0:99999:7:::
yoshida:$y$j9T$Ahmnwtq6va8KRxB8Z8HVa/$TLA8aBQACZ7XfxaYKhvlI9il1aOu1fCdBj1tTjlTteB:19571:0:99999:7:::
tanaka:$y$j9T$2jHKWPz312SeBb9cVPy9.1$eR96VUv6oET4QotW32ymTtoF00ajfQt0.w.KrJDpf/1:19571:0:99999:7:::
root@debian:/#
このほかにも次のファイルにユーザー情報が保存されています。
保存されているのはグループ情報などです。
/etc/group
/etc/gshadow
/etc/subgid
/etc/subuid
次のように後ろに-(ハイフン)があるものはバックアップファイルです。
/etc/group-
/etc/gshadow-
/etc/passwd-
/etc/shadow-
/etc/subgid-
/etc/subuid-
ユーザー名とパスワード
ログインに必要な認証情報は「/etc/shadow」に保存されています。
先ほど追加したyamadaの情報を切り出してみます。
yamada:$y$j9T$b8bD5o1nZrTL4lwrcq2Pk0$Br.AW5RCWz8qTYVxp.H5fg2X2CFfgPXjpEwCEo5FJG3:19571:0:99999:7:::
情報は「:(コロン)」に区切られて1行にまとめられています。
8個の「:(コロン)」で区切った9個の情報を含んでいます。
前から順番に以下の情報が設定されます。
情報 | ここでの値 |
---|---|
ユーザー名 | yamada |
パスワード | 下記で解説 |
パスワードを更新した日を1970年1月1日からの経過日数で表示 | 19571 |
パスワード更新が可能になるまでの日数 | 0 |
パスワードの有効期限までの日数 | 99999 |
パスワードの期限までの日数が何日になったら警告するか | 7 |
有効期限が過ぎて何日目で無効になるか | 未設定 |
IDが無効になるまでの日数 | 未設定 |
予約フィールド | 未設定 |
前から2番目の「パスワード」フィールドを見ていきます。
パスワード フィールド
ユーザー名「yamada」のパスワード フィールドは以下になります。
$y$j9T$b8bD5o1nZrTL4lwrcq2Pk0$Br.AW5RCWz8qTYVxp.H5fg2X2CFfgPXjpEwCEo5FJG3
4個の「$(ドル)」でフィールドが区切られているように見えます。
DESで暗号化されていた時代は次の形式でした。
<ソルト値><ハッシュ値>
DESのソルト値は2文字に決まっていたので区切り文字の「$(ドル)」は必要ありませんでした。
パスワード フィールドは暗号化アルゴリズムの遷移とともに変化します。
歴史的には暗号強度を高めるために新しいハッシュアルゴリズムが採用されていきます。
ハッシュアルゴリズムがMD5になった時に下のようなフォーマットになりました。
ソルト部分に「ハッシュ アルゴリズム」を示す「オプション」が追加さました。
$<ハッシュアルゴリズム>$<ソルト値>$<パスワードのハッシュ値>
Cライブラリのマニュアル(man crypt)からの抜粋です。
ID | Method
───────────────────────────────────────────────────────────────────
1 | MD5
2a | Blowfish (本流の glibc には入っていない;
| いくつかの Linux ディストリビューションで追加されている)
5 | SHA-256 (glibc 2.7 以降)
6 | SHA-512 (glibc 2.7 以降)
MD5はかなり前に非推奨になったので検証環境がありませんが、コマンドでハッシュを算出しました。
$ mkpasswd -m md5
Password: YourPasswordHere
$1$1Zva.MK1$2Uosb0/ep4E2zjSuoCIeU1
パスワード フィールドは最後の行です。
$1$1Zva.MK1$2Uosb0/ep4E2zjSuoCIeU1
「$1$」はアルゴリズムがMD5であることを表し、続く0~8文字がソルト値、最後の$(ドル)より後がパスワードをハッシュした値です。
パスワード フィールドで「ソルト(salt)」は「$<ハッシュ アルゴリズム>$<ソルト値>$」の部分です。
ソルト (salt) | パスワードのハッシュ値 |
---|---|
$1$1Zva.MK1$ | 2Uosb0/ep4E2zjSuoCIeU1 |
「ハッシュされたパスワード」の長さは以下のように固定値になっています。
MD5 | 22 characters
SHA-256 | 43 characters
SHA-512 | 86 characters
下は Debian11 Bullseye の /etc/shadow 内のパスワード フィールドです。
$6$u2nMcFZd$4YLz4.a9KkVJMjP8rCiTU0mBzxiAvFiYSPzuw./d9CJYa.YyVrDCP.0NX7eSoCW9.xQEdSuvbws092q91I0a10
SHA-512 でハッシュされていることがわかります。
「86文字」のハッシュ値が圧巻です。
次は Debian12 Bookworm のパスワード フィールド。
$y$j9T$b8bD5o1nZrTL4lwrcq2Pk0$Br.AW5RCWz8qTYVxp.H5fg2X2CFfgPXjpEwCEo5FJG3
この文字列には4個の「$(ドル)」があります。
ハッシュアルゴリズム「y」
ソルトのアルゴリズムを表す部分がマニュアルにはない「y」になっています。
Debianでは$y$になっているんだけどこれは何?
Chat-GPTに尋ねてみると次の回答が返ってきました。
`$y$`はDebianにおいて、yescryptハッシュ関数を使用することを示します。yescryptは、パスワー
ドのハッシュ化において強力なセキュリティを提供することを目的として開発されたハッシュアルゴ
リズムです。
新しいハッシュ関数 「yescrypt」
私にも理解できる説明はネット上に見つかりませんでした。
それにもかかわらず世の暗号アルゴリズムは「yescrypt」に向かっているようです。
暗号資産(ブロックチェーン)に使われています。
「yescrypt」は以下の特徴を持ったハッシュアルゴリズムです。
- 専用の装置(ASICマシン)を使用しても解析を高速化することができない
- メモリとCPUの利用効率が高い
- 計算コストを調整することが可能でプロセスにかかる時間を調整できる
Debian の「crypt(5)」にある説明を抜粋します。
yescrypt
yescryptは、Solar Designerによって設計されたスケーラブルなパスフレーズハッシュスキームで
す。 Colin Percival の scrypt をベースにしています。新しいハッシュアルゴリズムとして推奨
されています。
プレフィックス
"$y$"
ハッシュパスフレーズ フォーマット
\$y\$[./A-Za-z0-9]+\$[./A-Za-z0-9]{,86}\$[./A-Za-z0-9]{43}
最大パスフレーズ長さ
無制限
ハッシュサイズ
256 ビット
ソルトの長さ
512 ビットまで
CPU時間コスト パラメータ
1 ~ 11 (対数)
フォーマットは以下の通りです。
$y$CPU_TIME_COST$ソルト値$パスワードのハッシュ値
ソルトに2つ目のオプション「CPT_TIME_COST」があります。
これを踏まえてDebian12 Bookworm のパスワード フィールドを分解してみます。
$y$j9T$b8bD5o1nZrTL4lwrcq2Pk0$Br.AW5RCWz8qTYVxp.H5fg2X2CFfgPXjpEwCEo5FJG3
ソルト | パスワードのハッシュ値 |
---|---|
$y$j9T$b8bD5o1nZrTL4lwrcq2Pk0$ | Br.AW5RCWz8qTYVxp.H5fg2X2CFfgPXjpEwCEo5FJG3 |
ソルトを分解すると以下のようになります。
ここでの値 | 意味 |
---|---|
y | ハッシュ関数「yescrypt」を使う |
j9T | CPU時間コスト パラメータ |
b8bD5o1nZrTL4lwrcq2Pk0 | ソルト値 |
「CPU時間コスト パラメータ」はCPU実行時間と使用メモリのサイズ、並列処理の有無などを表しています。
「j9T」はパスワードのハッシュを最適化する「おまじない」と考えておきます。
追求したい方はこのあたりを参照してみてください。
文字列はSHA-512に比べて格段に「短く」なりました。
「yescrypt」のハッシュ値は固定長で43文字です。
パスワードの検証
3台のDenbianパソコンを用意して、それぞれにユーザー名「yamada」とパスワード「Yama5Da」でユーザーを作ります。
各パソコンの /etc/shadow から yamada の行の「パスワードのハッシュ値」を取り出して並べました。
pc0: Br.AW5RCWz8qTYVxp.H5fg2X2CFfgPXjpEwCEo5FJG3
pc1: 7UbdAib0/gUFab6NrZgGi96C3j1riUR1Q/P7Izw25/4
pc2: i/z5QibiA2gXIep1EhaSJ42/D.E1ssvtPJyWdmuwra/
同じ長さの「似ても似つかない」文字列です。
なぜかというと、「ソルト(値)」が違うからです。
ソルトはユーザーが追加されるときにランダムな値が割り当てられます。
「異なるソルト」を使って算出されたハッシュ値は「全く違ったもの」になります。
これはパスワードの安全性を確保する手段の1つです。
このことからパスワードを検証するには、「事前にソルトを知る必要がある」ことがわかります。
パスワードの検証には次の手順が必要です。
- 「(ハッシュされる前の)パスワード」を知っている
- /etc/shadow から「ソルト」を取り出す
- 「ソルト」と「ソルト内に示されたアルゴリズム」を使って「パスワードのハッシュ値」を算出する
- 「算出されたハッシュ値」と「/etc/shadow の yamada 行のパスワードのハッシュ値」とを比較する
検証してみましょう。
mkpasswd を使う
apt install whois
mkpasswd --method=yescrypt --salt='$y$j9T$b8bD5o1nZrTL4lwrcq2Pk0$' 'Yama5Da'
$y$j9T$b8bD5o1nZrTL4lwrcq2Pk0$Br.AW5RCWz8qTYVxp.H5fg2X2CFfgPXjpEwCEo5FJG3
アルゴリズムは「ソルトから判断できる」ので「–method=yescrypt」は無くしても同じ結果が出ます。
mkpasswd --salt='$y$j9T$b8bD5o1nZrTL4lwrcq2Pk0$' 'Yama5Da'
$y$j9T$b8bD5o1nZrTL4lwrcq2Pk0$Br.AW5RCWz8qTYVxp.H5fg2X2CFfgPXjpEwCEo5FJG3
perl を使う
export PASS=Yama5Da SALT='$y$j9T$b8bD5o1nZrTL4lwrcq2Pk0$'
perl -le 'print crypt($ENV{PASS}, $ENV{SALT})'
$y$j9T$b8bD5o1nZrTL4lwrcq2Pk0$Br.AW5RCWz8qTYVxp.H5fg2X2CFfgPXjpEwCEo5FJG3
環境変数を使うのは、ワンライナー コマンドで展開とクオートのトラブルを回避するためです。
「パスワードのハッシュ値が一致」すれば正当なユーザーであることを検証できます。
まとめ
Linuxのログインパスワードについて調べてみました。
暗号化アルゴリズム/ハッシュ アルゴリズムは通信を支える重要な技術ですが、気づかぬうちに新しくなっていることに驚きました。