WordPressのDBに保存されているハッシュ化されたパスワードの値が気になったので調べた話

公開:2020/10/14
更新:2020/10/19
7 min読了の目安(約6800字IDEAアイデア記事

今までWordPressやPHPを触ったことは無く、お仕事で必要になりお勉強している初心者です。
初めての分野なので専門家の方なら当たり前かもしれませんが、WordPressが保存するパスワードの形式が気になったので調べたことを書きます。

気になったこと

WordPressのDBに存在するユーザーデータを検索したときのことでした。

$ mysql -udbadmin -p -e " SELECT user_login,user_pass FROM db.wp_users;"
+------------+------------------------------------+
| user_login | user_pass                          |
+------------+------------------------------------+
| wpadmin    | $P$BAK.qXtgo8dRgdlqdPLTBR28.2wKiM. |
+------------+------------------------------------+

このときパスワードはハッシュ化され $P$BAK.qXtgo8dRgdlqdPLTBR28.2wKiM. となっているのですが、この値が個人的にみたこと無い形式であり戸惑いました。
疑問を説明するまえにパスワードのハッシュ化について、個人的な知識を書きます。

Linuxなどでよく見るハッシュ化されたパスワードについて

パスワードのハッシュ化するライブラリで有名な?glibc2ではハッシュ化して生成される値は以下のようになっているらしいです。

$ man 3 crypt

もし salt の文字列が "$id$" で始まっていて、"$" で終わっている文字列が 続いている場合:

$id$salt$encrypted

DES を使う代わりに、 id で使用する暗号化手法を識別し、これがパスワード文字列の残りの部分を解釈する 方法を決定する。 id の値として、以下の値に対応している:

ID | Method
1 | MD5
2a | Blowfish (本流の glibc には入っていない;
| いくつかの Linux ディストリビューションで追加されている)
5 | SHA-256 (glibc 2.7 以降)
6 | SHA-512 (glibc 2.7 以降)

https://linuxjm.osdn.jp/html/LDP_man-pages/man3/crypt.3.html

これだけだとわかりづらいですがopennssl passwdを利用して実際に生成してみるとわかりやすいかもしれません。

## MD5

$ openssl passwd -1 password
$1$/rz43CxL$CZyL78Lvam6XLbjAKBHBU.

## SHA-256
$ openssl passwd -5 password
$5$LvIekIG3ERXIcRl0$pXpYX8CC3tm7dwllcdc3aAnOiyLwkCka4./0UMLOM9A

## SHA-512
$ openssl passwd -6 password
$6$K3JfSPZYVdNBrULa$st.ZwaBG2Bb3gXANewrEhGnDInPvLLrOZd6jpTDkYn/rUtxhTDFYvpGC8Vhcii8S5zGttfpIz3EL8cXHGrMCZ1

ハッシュ化された値から得られる情報

先程出力した各値を簡単に説明すると下記のようになると思います。

$1$/rz43CxL$CZyL78Lvam6XLbjAKBHBU.
 => Method   :MD5
 => salt     :/rz43CxL
 => encrypted:z43CxL$CZyL78Lvam6XLbjAKBHBU.

$5$LvIekIG3ERXIcRl0$pXpYX8CC3tm7dwllcdc3aAnOiyLwkCka4./0UMLOM9A
 => Method   :SHA-256
 => salt     :LvIekIG3ERXIcRl0
 => encrypted:pXpYX8CC3tm7dwllcdc3aAnOiyLwkCka4./0UMLOM9A

$6$K3JfSPZYVdNBrULa$st.ZwaBG2Bb3gXANewrEhGnDInPvLLrOZd6jpTDkYn/rUtxhTDFYvpGC8Vhcii8S5zGttfpIz3EL8cXHGrMCZ1
 => Method   :SHA-512
 => salt     :K3JfSPZYVdNBrULa
 => encrypted:st.ZwaBG2Bb3gXANewrEhGnDInPvLLrOZd6jpTDkYn/rUtxhTDFYvpGC8Vhcii8S5zGttfpIz3EL8cXHGrMCZ1

saltについて

生成される値に設定されるsaltは毎回適当なものを割り当てられるため、たとえ同じパスワードでもハッシュ化された後の値は毎回異なるものになります。

$ openssl passwd -1 password 
$1$6qIikTN6$gLHVtqnjov70CivrVFlOh1

## 生成するたびに値は異なる
$ openssl passwd -1 password 
$1$NHZCnQdQ$5NHv1R0ikBqVEpL1.epl4/

saltを固定すると同じハッシュ化された値を生成できます。

$ openssl passwd -salt saltsalt -1 password 
$1$saltsalt$qjXMvbEw8oaL.CzflDtaK/

## saltを指定すれば変わらない
$ openssl passwd -salt saltsalt -1 password 
$1$saltsalt$qjXMvbEw8oaL.CzflDtaK/

Linuxでよく見る場所

/etc/shadow などにあります。

## sample-userのパスワード情報などを表示

$ sudo cat /etc/shadow | grep sample-user
sample-user:$6$K3JfSPZYVdNBrULa$st.ZwaBG2Bb3gXANewrEhGnDInPvLLrOZd6jpTDkYn/rUtxhTDFYvpGC8Vhcii8S5zGttfpIz3EL8cXHGrMCZ1:00000:0:00000:0:::

Linuxでユーザー毎にパスワードのハッシュ方式が異なっていても特に問題なくログインできるのは、認証するためのプログラムがハッシュ化された値の先頭を確認してハッシュ方式を判断しているためなのでしょう。
LPICの過去問などで出題されてはずなので覚えておくと良いかもしれません。

PHPのハッシュ関数とハッシュ方式について

先程までの説明とは異なり、PHPで一般的なハッシュ方式は異なるようです。
以下の記事が参考になります。

https://medium-company.com/php-crypt/

PHPで有名な password_hash() では、デフォルト(PHPのバージョンに依存しますが…)でCRYPT_BLOWFISHが採用されており、これを利用してパスワードをハッシュ化すると下記のような状態になっているはずです。

$<Algorithm>$<Cost>$<Solt><Hashed Passwords>

先程までみたopennssl passwdとの違いは、Costが追加されたこと、また$2y$などのハッシュ方式の種類がふえたことのようです。
crypt()の説明によると使用できるハッシュ方式には下記があるようです。
https://www.php.net/manual/ja/function.crypt.php

CRYPT_STD_DES : DES ベースのアルゴリズム(可変長?)
CRYPT_EXT_DES : 先頭が_(アンダースコア)で始まるもの
CRYPT_MD5     : 先頭が $1$
CRYPT_BLOWFISH: 先頭が $2a$","$2x$","$2y$"のいずれかで次の$はコスト
CRYPT_SHA256  : 先頭が $5$"で次の$はコスト(rounds=<N>)
CRYPT_SHA512  : 先頭が $6$"で次の$はコスト(rounds=<N>)

実際に生成されるハッシュ値は下記の記事で確認でき参考になります。
https://medium-company.com/php-crypt/

WordPressのハッシュ化されたパスワードについての疑問

ここまで説明したなかでハッシュ化されたパスワードの先頭に存在するハッシュ方式の一覧は下記です。

$1$ , $2a$ , $2x$ , $2y$ , $5$ , $6$

そしてWordPressのハッシュ化されたパスワードを改めてみてみます。

+------------+------------------------------------+
| user_login | user_pass                          |
+------------+------------------------------------+
| wpadmin    | $P$BAK.qXtgo8dRgdlqdPLTBR28.2wKiM. |
+------------+------------------------------------+

WordPressのパスワードは、私にとっては未知のハッシュ方式 「$P$」 でハッシュ化されています。

「$P$」 とは何か?

結論から言うとWordPress独自のハッシュ方式を表す表記のようです。

ソースコードを見る限りこの部分でしょうか?
https://github.com/WordPress/WordPress/blob/6fe59c4bdc4c360c58fd950a495a920ce35e1159/wp-includes/class-phpass.php#L88-L164

ドキュメントなどは下記のようです。
https://developer.wordpress.org/reference/functions/wp_hash_password/

またソースの先頭に面白そうな記載も有ります。

# Please do not change the "private" password hashing method implemented in
# here, thereby making your hashes incompatible. However, if you must, please
# change the hash type identifier (the "$P$") to something different.
https://github.com/WordPress/WordPress/blob/6fe59c4bdc4c360c58fd950a495a920ce35e1159/wp-includes/class-phpass.php#L20-L22

別のハッシュ方式でハッシュ化したパスワードをDBへ直接設定してみる

wp_hash_password()crypt()と互換性があるっぽく、またチェックするためのwp_check_password()も対応しているようで、
MD5やSHA256やSHA512でハッシュ化されたパスワードをDBに保存しても問題なく動作します。

この方法はパスワードを忘れたときにDBから直接設定する方法として多くのサイトで紹介されていますね。

例えばopennssl passwdを利用し、wpuserのパスワードをnewpasswordに設定してみます

## passwordを変更
$ mysql -udbadmin -p -e "UPDATE db.wp_users SET user_pass = $(openssl passwd -6 newpassword) WHERE user_login = 'wpuser';"

## DBの値を確認
$ mysql -udbadmin -p -e  "SELECT user_login,user_pass FROM exampledb.wp_users;"
+------------+------------------------------------------------------------------------------------------------------------+
| user_login | user_pass                                                                                                  |
+------------+------------------------------------------------------------------------------------------------------------+
| wpuser     | $6$4ijm4QxWCWPns9ch$6tImoPgH9lo.7bYvK3PR1mZYHBkRnrzSpKZV7umSKJUc2C8cYPYmU3oz4A.fQZWGubS2g29tJvpC4wjxBp3.p. |
+------------+------------------------------------------------------------------------------------------------------------+

WP-CLIを利用して新しいパスワードを確認してみます。

$ wp user check-password wpuser newpassword && echo OK || echo NG
OK

そして実際にWordPressにアクセスすれば新しいパスワードでログインできます。

WordPressのハッシュ方式を変更したい時

SHA256のほうがハッシュ値が長いので採用したい!という場合は下記のようにして変更できるらしいです。
https://stackoverflow.com/questions/23949502/wordpress-sha-256-login#answers-header

WordPress詳しくないので運用上のセキュリティや互換性の問題はわからないですが…

あとがき

とりあえず自分の現時点での理解を健忘録としてまとめました……

しかし冒頭でも書いたとおり、PHPやWordPressには全く詳しくないので誤っている点などは多いと思います。また、パスワードのハッシュ化やLinuxについてもそれほど詳しくありません。

間違っている点があれが指摘していただけると幸いです。

そしてこの記事が誰かの参考になれば幸いです。