認証方式をscram-sha-256に変更する

## 環境

Ubuntu20.4
Postgresql12

## はじめに

**PostgreSQL10**より、ロールにつけるパスワードの暗号化方式で、従来の**md5**に加え、**scram-sha-256**を指定することができるようになりました。

md5にはいくつかの脆弱性が指摘されています。md5は強い意志をもった攻撃に対して安全でないとされています。md5では攻撃者がサーバからパスワードハッシュを盗むことに対して防ぐ手立てがありません。

scram-sha-256が、現在実装されている認証方式の中で、最も安全なアルゴリズムと言われています。セキュリティを強化させるために、md5のデフォルトの設定のままにしている場合は、scram-sha-256に変更した方がいいでしょう。

## 事前作業

設定ファイルを変更する前に、デフォルトの設定を残しておくために、バックアップを取っておいた方がいいでしょう。

```
sudo cp /etc/postgresql/12/main/postgresql.conf /etc/postgresql/12/main/postgresql.conf_bkup
sudo cp /etc/postgresql/12/main/pg_hba.conf /etc/postgresql/12/main/pg_hba.conf_bkup
```

## 暗号化のアルゴリズムを変更

パスワードの暗号化方式は、デフォルトではmd5になっています。
postgresql.confをみると、デフォルトではコメントになっています。コメントになっていれば、デフォルト値のmd5が有効になっています。

```/etc/postgresql/12/main/postgresql.conf
#password_encryption = md5              # md5 or scram-sha-256
```

コメントを外して設定を変更します。

```/etc/postgresql/12/main/postgresql.conf
password_encryption = scram-sha-256     # md5 or scram-sha-256
```
コメントには、md5またはscram-sha-256を設定するように記載されています。古いバージョンのPostgreSQLでは、passwordという値が設定できるようになっていました。passwordではパスワードは暗号化されずに、平文で保存されていました。現バージョンではpasswordを設定することはできません。あまりにもセキュリティ的に問題があるので外した模様です。

設定した内容を反映させるには、PostgreSQLのサービスを再起動させる必要があります。

```
$service postgresql restart
```

## 設定の確認

PostgreSQLにログインします。因みに、ここでのログインはmd5認証ではなくpeer認証によるログインです。

```
$sudo su - postgres
$psql
```
**postgresql.con**fの**password_encryption**で変更した値が、PostgreSQLに反映されていることを確認するには、つぎのコマンドで確認することができます。

```
postgres=#show password_encryption;
 password_encryption
---------------------
 scram-sha-256
(1 row)
```

## 動作確認（その１）

ここで問題です。

postgresql.confで、認証のアルゴリズムをmd5からscram-sha-256に変更しました。そして、pg_hba.confで認証タイプはつぎのように、md5のままになっています。

```/etc/postgresql/12/main/pg_hba.conf
host    all             all             127.0.0.1/32            md5
```

この状態で、host接続はできるでしょうか？

答えは「できます。」

ロールのパスワードはmd5でハッシュ化されたままの状態です。pg_hba.confの設定がmd5であれば、md5を使ってチャレンジパスワードがハッシュ化されるからです。

scram-sha-256の設定は、次にロールのパスワードを生成したときから有効になります。scram-sha-256に設定を変更しても、ロールのパスワードを再生成しない限り、md5でハッシュ化されたままの状態になっています。

## 動作確認（その２）

もうひとつ問題です。

pg_hba.confをmd5からscram-sha-256に変更して、サービスを再起動させて設定を反映させました。

```/etc/postgresql/12/main/pg_hba.conf
host    all             all             127.0.0.1/32            scram-sha-256
```

この状態で、host接続はできるでしょうか？

答えは「できません。」

チャレンジパスワードを入力するとつぎのエラーが表示されます。

```
$psql -U hoge -d hoge_db -h localhost
Password for user hoge:
psql: error: connection to server at "localhost" (127.0.0.1), port 5432 failed: FATAL:  password authentication failed for user "hoge"
connection to server at "localhost" (127.0.0.1), port 5432 failed: FATAL:  password authentication failed for user "hoge"
```

ロールのパスワードはmd5でハッシュ化されたままの状態です。host接続しようとすると、チャレンジパスワードはscram-sha-256でハッシュ化されます。md5でハッシュ化したものと、scram-sha-256でハッシュ化したものを比較して認証作業をするので、認証は失敗します。

因みにですが、local接続でpeer認証で接続する場合は、認証が行われないため、postgresql.confの設定がmd5であろうと、scram-sha-256であろうと、関係なく接続できます。

## パスワードを作り直す

postgresql.confの設定をmd5からscram-sha-256に変更して、pg_hba.confの認証タイプもmd5からscram-sha-256に変更しました。次にやることは、md5でハッシュ化されているロールを、すべて、scram-sha-256でハッシュ化させて作り直すことです。

PostgreSQLにログインします。

```
$sudo su - postgres
$psql
```
**pg_authid**のデータカタログで、パスワードが設定されているロールの一覧を確認することができます。ハッシュ化されたパスワードは**rolpassword**カラムに更新されています。

```
postgres=#select * from pg_authid;				        # すべてのカラムを表示
postgres=#select rolname, rolpassword from pg_authid;	# ロール名とパスワードを表示
```

すべてのロールに対して、scram-sha-256でハッシュ化させたパスワードに変更します。


```
postgres=#ALTER ROLE hoge_user1 WITH PASSWORD 'hoge_user1_password';
```

同じ長さのパスワードをmd5とscram-sha-256でそれぞれハッシュ化させた結果です。見ての通りハッシュの長さがこれだけ違います。やはりscram-sha-256の方がmd5と比べて圧倒的に強力です。

md5でハッシュ化した結果

```
hoge         | md5ce5f2d27bc6276a03b0328878c1dc0e2
```

scram-sha-256でハッシュ化した結果

```
hoge         | SCRAM-SHA-256$4096:855fLdHvpeSqPT/MaeoMGQ==$Mv4RTDu+WYy83hs48Zwr1cBtoobIU9EkFaPVaQK+QCg=:GrVQoMdFdaSGtsx7WF7M30Jm9npheaDYuIBNm7fabiA=
```

scram-sha-256でロールのパスワードを作り直した後、host接続をすると、今度はちゃんと認証に成功します。

## 動作確認（その３）

もうひとつ変なことをします。

postgresql.confでmd5ではなくscram-sha-256に設定されています。ロールのパスワードをscram-sha-256でハッシュ化して再設定をしました。そして、pg_hba.confで認証タイプを、scram-sha-256ではなくmd5で設定しました。

```/etc/postgresql/12/main/pg_hba.conf
host    all             all             127.0.0.1/32            md5
```

この状態でhost接続はできるでしょうか。

pg_hba.confで認証タイプをmd5にしているということは、チャレンジパスワードはmd5でハッシュ化されます。そして、scram-sha-256でハッシュ化されたパスワードと比較されるため、認証は成功するはずがない思われるでしょう。

しかし、この設定でもhost接続はできてしまいます。

なぜか？

ロールのパスワードがscram-sha-256でハッシュ化されていた場合、pg_hba.confの設定がmd5になっていても、実は、チャレンジパスワードは、md5ではなく、scram-sha-256でハッシュ化して認証するといった仕様になっているからです。

えーっ！、うそーっ！と思われるかも分かりませんが、現に公式ドキュメントにそのように書かれています。こちらのリンクがその証拠です。自分も実際に試してみて、その通りに動作しました。

https://www.postgresql.jp/document/12/html/auth-password.html

このような仕様になっているからといって、pg_hba.confの設定をmd5のままにしておくのではなく、明示的にscram-sha-256で設定してしておいた方が、後々、管理の面において混乱を起こさないことになるのでいいかとは思います。