本記事はPostgreSQL Advent Calendar 2021の第15日目の記事です。
テーマはタイトルどおり、PostgreSQLのログインユーザに設定するパスワードに、最小文字数や最大文字数、使用できる文字種などの制限は存在するのか?というテーマです。
第18回PostgreSQLアンカンファレンスでも同じテーマで発表したのですが、そういえば記事としてまとめていなかったことを思い出し、Advent Calendarもあるしいい機会なのでまとめてみることにしました。
ちなみにアンカンファレンスでの発表資料はこちら。
PostgreSQLのパスワードの謎を追え!
結論
結論から言いましょう。PostgreSQLのユーザのパスワードには、実質的に制限はありません。
しかし、マルチバイト文字を使ったり異常に長い文字列にしたりなど、あまりにも奇抜なパスワードは実用的ではありません。
常識的な文字種・文字数のパスワードを使用しましょう。
なぜこのテーマで記事を書いたか
筆者はPostgreSQLの導入や設計を行っていますが、PostgreSQLにユーザを作成する際に、顧客に「パスワードはどうしますか?プロジェクトで適用しているルール等あれば教えて下さい」と確認します。
ほとんどの場合はプロジェクトで統一されたパスワードのルールがあるのですが、たまにルールが決まっておらず、逆に「PostgreSQLで使えるパスワードの文字や長さを教えて下さい」と聞かれてしまうことがあります。
さて困りました。だいたいどんなパスワードでもユーザは作れるので、パスワードの制限なんて気にしたことがありませんでした。MySQLではパスワードポリシーが設定できるのですが。
「制限はありませんよ」と言ってしまってもいいのですが、根拠がありません。「今までどんなパスワードでもOKだったから」では根拠としてはちょっと弱いです。せっかくなので、パスワードの制限について調べてみました。
ネットで検索してみる
「PostgreSQL パスワード 制限」等のキーワードで探してみますが、PostgreSQLをDBとして使用するツールのパスワードの制限はあるのですが、特にこれと言ってPostgreSQL本体のパスワードの制限について記述しているサイトは見つかりませんでした。(アンカンファレンスでの発表当時。)
マニュアルを読んでみる
PostgreSQLは有志の方々の翻訳のおかげで日本語のマニュアルも充実しています(感謝)。CREATE ROLE
やALTER ROLE
などのユーザのパスワードが関わってくるコマンドのマニュアルを確認したのですが、やはりパスワードの制限については何も書かれていません。
普通に考えて、パスワードに制限があればマニュアルに記載するはず。それがないということは、もしかして制限はないのでは?
しかし、そんなことある?
ソースを読んでみる
最終的に、最も信頼できるドキュメントであるソースを読むことにしました。
しかし、PostgreSQLはC言語で記述されているプログラムです。筆者は残念ながらC言語のプログラミング経験はありません。それでも全体ではなくある一部分だけなら読めるだろうと覚悟を決めて読んでみることにしました。
こうやって、直接ソースに当たれるのがOSSであるPostgreSQLの利点です。
ソースのダウンロードと展開
ソースは次のURLからダウンロードしましょう。
お使いのバージョンがあればそのフォルダから、今回は本記事執筆時の最新版である14.1のソースをダウンロードします。
バージョンによる差異があるかもしれませんが、ソースはtar.bz2
かtar.gz
で圧縮されているはずです。Windowsをお使いの方は対応している解凍ツールで解凍してください。どちらの圧縮ファイルも中身は同じです。
展開すると次のようなフォルダとファイルが解凍されます。PostgreSQLのソースはsrc
フォルダに格納されています。
しかしsrc
フォルダの中身が複雑で、一見しただけでは何がどこにあるのかわかりません。
詳しいフォルダの説明は他に譲るとして、backend
フォルダの中身がサーバサイドで動くPostgreSQLのコアな部分ということを覚えてください。今回確認するユーザのパスワードに関するソースもこの中にあります。
ユーザ関連のソースを探る
ユーザの作成や変更に関するソースはbackend/commands
フォルダの中の、user.cというソースファイルに記述されています。
その中のCreateRole
関数を追って、パスワードに関する記述がないか確認してみましょう。
CreateRole
関数は最初に変数を宣言しています。この段階ではまだパスワードの制限は関係ありません。
次にCREATE ROLE
を実行した際のオプションの組み合わせや、指定したオプションが実行可能な権限を持っているかをチェックし、実行できなければエラーメッセージを返しています。
この時点でも、オプションのチェックだけで、パスワードそのもののチェックはしていません。
様々なチェックが終わると、check_password_hook
という変数を参照し、パスワードチェックをしてそうなif文が出てきます。
「お!やっぱり何かしらチェックしてる!」と思いましたが、check_password_hook
変数はuser.cの最初の方で宣言されている変数で、NULLで初期化されており、これまで異なる値を入れた行はありませんでした。
つまり、通常このif文には入らないということになります。結局パスワードのチェックはしていません。
このあとは、pg_authid
というユーザやロールに関する情報を保持するシステムカタログにレコードを追加する処理となり、最後までパスワードのチェックは行いません。
本当に制限はないの?
本当にパスワードのチェックはしていないのか?本当になんでもいいのか?試しにやってみました。
マルチバイト文字のパスワード
次のSQLを実行してみました。
CREATE ROLE multibyte_user LOGIN PASSWORD 'パスワードはマルチバイトで';
しっかりとユーザが作成できます。
長いパスワード
1024文字のパスワードでユーザを作成してみました。
特に問題なく作成できます。どうやら本当に、SQLで文字列として扱えればなんでもいいようです。
もう一度結論
PostgreSQLのユーザのパスワードには、実質的に制限はありません。
しかし、マルチバイト文字を使ったり異常に長い文字列にしたりなど、あまりにも奇抜なパスワードは実用的ではありません。入力も大変ですし、環境によってはそもそも入力を受け付けない場合もあります。
たとえばPostgreSQLを操作するツールにパスワードを入力するとき、おそらくマルチバイト文字は入力できないでしょう。逆に言えば、PostgreSQLを使うソフトやツールの制限に合わせるために、PostgreSQL自身には特に制限がないとも考えられます。
常識的な文字種・文字数のパスワードを使用しましょう。
補足
最後に補足です。
check_password_hook
user.cに出てきたcheck_password_hookとは何者でしょうか?
PostgreSQLにはhookという仕組みがあり、自作の処理をPostgreSQLの処理の途中に割り込ませることができます。hookは決まったところにあるのですが、check_password_hookもその一つです。
片手間にホイホイと作るわけにはいきませんが、パスワードに制限を与え、なおかつ柔軟にパスワードの条件を指定できる拡張機能があれば便利……かもしれません。
pg_authid
pg_authidはユーザやロールの情報を保存するシステムカタログです。CREATE ROLE
コマンドで作成されたロールはこのテーブルに格納されます。
pg_authidのrolpassword
列がパスワードを格納する列であり、text型で作成されています。
text型は最大1GBなので、パスワードの上限も1GBではと思われたのですが、CREATE ROLE
やALTER ROLE
でユーザを作成すると、通常はmd5かSCRAM-SHA-256でハッシュ化された値がrolpasswordに格納されます。
md5もSCRAM-SHA-256も桁数が決まっているため、1GBになることはありません。
やはり、通常の操作の範疇では、パスワードの制限は実質ないと考えて差し支えないでしょう。