PostgreSQLのユーザのパスワードに制限はあるのか？

本記事は[PostgreSQL Advent Calendar 2021](https://qiita.com/advent-calendar/2021/postgresql)の第15日目の記事です。

テーマはタイトルどおり、PostgreSQLのログインユーザに設定するパスワードに、最小文字数や最大文字数、使用できる文字種などの制限は存在するのか？というテーマです。

[第18回PostgreSQLアンカンファレンス](https://pgunconf.connpass.com/event/192959/)でも同じテーマで発表したのですが、そういえば記事としてまとめていなかったことを思い出し、Advent Calendarもあるしいい機会なのでまとめてみることにしました。

ちなみにアンカンファレンスでの発表資料はこちら。
[PostgreSQLのパスワードの謎を追え！](https://www.slideshare.net/TakashiMeguro/postgresql-239052721)

# 結論

結論から言いましょう。PostgreSQLのユーザのパスワードには、実質的に制限はありません。
しかし、マルチバイト文字を使ったり異常に長い文字列にしたりなど、あまりにも奇抜なパスワードは実用的ではありません。
常識的な文字種・文字数のパスワードを使用しましょう。

# なぜこのテーマで記事を書いたか

筆者はPostgreSQLの導入や設計を行っていますが、PostgreSQLにユーザを作成する際に、顧客に「パスワードはどうしますか？プロジェクトで適用しているルール等あれば教えて下さい」と確認します。
ほとんどの場合はプロジェクトで統一されたパスワードのルールがあるのですが、たまにルールが決まっておらず、逆に「PostgreSQLで使えるパスワードの文字や長さを教えて下さい」と聞かれてしまうことがあります。

さて困りました。だいたいどんなパスワードでもユーザは作れるので、パスワードの制限なんて気にしたことがありませんでした。MySQLではパスワードポリシーが設定できるのですが。
「制限はありませんよ」と言ってしまってもいいのですが、根拠がありません。「今までどんなパスワードでもOKだったから」では根拠としてはちょっと弱いです。せっかくなので、パスワードの制限について調べてみました。

# ネットで検索してみる

「PostgreSQL パスワード 制限」等のキーワードで探してみますが、PostgreSQLをDBとして使用するツールのパスワードの制限はあるのですが、特にこれと言ってPostgreSQL本体のパスワードの制限について記述しているサイトは見つかりませんでした。（アンカンファレンスでの発表当時。）

# マニュアルを読んでみる

PostgreSQLは有志の方々の翻訳のおかげで日本語のマニュアルも充実しています（感謝）。`CREATE ROLE`や`ALTER ROLE`などのユーザのパスワードが関わってくるコマンドのマニュアルを確認したのですが、やはりパスワードの制限については何も書かれていません。

普通に考えて、パスワードに制限があればマニュアルに記載するはず。それがないということは、もしかして制限はないのでは？

しかし、***そんなことある？***

# ソースを読んでみる

最終的に、最も信頼できるドキュメントであるソースを読むことにしました。

しかし、PostgreSQLはC言語で記述されているプログラムです。筆者は残念ながらC言語のプログラミング経験はありません。それでも全体ではなくある一部分だけなら読めるだろうと覚悟を決めて読んでみることにしました。
こうやって、直接ソースに当たれるのがOSSであるPostgreSQLの利点です。

## ソースのダウンロードと展開

ソースは次のURLからダウンロードしましょう。

https://www.postgresql.org/ftp/source/

お使いのバージョンがあればそのフォルダから、今回は本記事執筆時の最新版である14.1のソースをダウンロードします。

![スクリーンショット 2021-12-06 224054.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70076/d49d66d7-a17d-0d2f-178a-94102e75e65f.jpeg)


バージョンによる差異があるかもしれませんが、ソースは`tar.bz2`か`tar.gz`で圧縮されているはずです。Windowsをお使いの方は対応している解凍ツールで解凍してください。どちらの圧縮ファイルも中身は同じです。

![スクリーンショット 2021-12-06 224152.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70076/ba337ac3-399b-14af-5de9-f87bec50d83a.jpeg)

展開すると次のようなフォルダとファイルが解凍されます。PostgreSQLのソースは`src`フォルダに格納されています。

![スクリーンショット 2021-12-06 224351.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70076/930edea8-63c7-0fce-05d7-e16dbc672db2.jpeg)

しかし`src`フォルダの中身が複雑で、一見しただけでは何がどこにあるのかわかりません。
詳しいフォルダの説明は他に譲るとして、`backend`フォルダの中身がサーバサイドで動くPostgreSQLのコアな部分ということを覚えてください。今回確認するユーザのパスワードに関するソースもこの中にあります。

![スクリーンショット 2021-12-06 224705.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70076/587d6043-0c1d-5200-438d-4bbd228ff270.jpeg)

## ユーザ関連のソースを探る

ユーザの作成や変更に関するソースは`backend/commands`フォルダの中の、***user.c***というソースファイルに記述されています。
その中の`CreateRole`関数を追って、パスワードに関する記述がないか確認してみましょう。

`CreateRole`関数は最初に変数を宣言しています。この段階ではまだパスワードの制限は関係ありません。

次に`CREATE ROLE`を実行した際のオプションの組み合わせや、指定したオプションが実行可能な権限を持っているかをチェックし、実行できなければエラーメッセージを返しています。
この時点でも、オプションのチェックだけで、パスワードそのもののチェックはしていません。

様々なチェックが終わると、`check_password_hook`という変数を参照し、パスワードチェックをしてそうなif文が出てきます。

![スクリーンショット 2021-12-06 230249.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70076/73560517-210a-db4f-b522-b7f6926d5fd8.jpeg)

「お！やっぱり何かしらチェックしてる！」と思いましたが、`check_password_hook`変数はuser.cの最初の方で宣言されている変数で、NULLで初期化されており、これまで異なる値を入れた行はありませんでした。
つまり、通常このif文には入らないということになります。***結局パスワードのチェックはしていません。***

このあとは、`pg_authid`というユーザやロールに関する情報を保持するシステムカタログにレコードを追加する処理となり、最後までパスワードのチェックは行いません。

# 本当に制限はないの？

本当にパスワードのチェックはしていないのか？本当になんでもいいのか？試しにやってみました。

### マルチバイト文字のパスワード

次のSQLを実行してみました。

```sql
CREATE ROLE multibyte_user LOGIN PASSWORD 'パスワードはマルチバイトで';
```

しっかりとユーザが作成できます。

![スクリーンショット 2021-12-06 232214.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70076/d9271a37-4b7f-66e4-1e18-0a575da8eb98.jpeg)

### 長いパスワード

1024文字のパスワードでユーザを作成してみました。

![スクリーンショット 2021-12-08 212845.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/70076/05c90576-d964-c24b-6e3d-f44f054aeca1.jpeg)


特に問題なく作成できます。どうやら本当に、SQLで文字列として扱えればなんでもいいようです。

# もう一度結論

PostgreSQLのユーザのパスワードには、実質的に制限はありません。
しかし、マルチバイト文字を使ったり異常に長い文字列にしたりなど、あまりにも奇抜なパスワードは実用的ではありません。入力も大変ですし、環境によってはそもそも入力を受け付けない場合もあります。
たとえばPostgreSQLを操作するツールにパスワードを入力するとき、おそらくマルチバイト文字は入力できないでしょう。逆に言えば、PostgreSQLを使うソフトやツールの制限に合わせるために、PostgreSQL自身には特に制限がないとも考えられます。
常識的な文字種・文字数のパスワードを使用しましょう。

# 補足

最後に補足です。

### check_password_hook

user.cに出てきたcheck_password_hookとは何者でしょうか？

PostgreSQLには***hook***という仕組みがあり、自作の処理をPostgreSQLの処理の途中に割り込ませることができます。hookは決まったところにあるのですが、check_password_hookもその一つです。

片手間にホイホイと作るわけにはいきませんが、パスワードに制限を与え、なおかつ柔軟にパスワードの条件を指定できる拡張機能があれば便利……かもしれません。

### pg_authid

[pg_authid](https://www.postgresql.jp/document/13/html/catalog-pg-authid.html)はユーザやロールの情報を保存するシステムカタログです。`CREATE ROLE`コマンドで作成されたロールはこのテーブルに格納されます。

pg_authidの`rolpassword`列がパスワードを格納する列であり、text型で作成されています。
text型は最大1GBなので、パスワードの上限も1GBではと思われたのですが、`CREATE ROLE`や`ALTER ROLE`でユーザを作成すると、通常はmd5かSCRAM-SHA-256でハッシュ化された値がrolpasswordに格納されます。
md5もSCRAM-SHA-256も桁数が決まっているため、1GBになることはありません。
やはり、通常の操作の範疇では、パスワードの制限は実質ないと考えて差し支えないでしょう。