2026年10月29日、PostgreSQLの管理ツールpgAdmin 4のv9.19が公開されました。修正された脆弱性は15件です。最も深刻なCVE-2026-116241(CVSS 9.9)は、データの書き出し機能を通じて、一般の利用者がpgAdminを動かすサーバー上でOSコマンドを実行できるというものでした。7月に直したはずの欠陥の抜け道です。今回の更新には、初回起動の前に設定を済ませないと既存の設定が消える変更も含まれます。15件の中身、影響する構成の見分け方、更新前の設定とDockerでの手順、更新までの緩和策を、2026年11月2日時点の公開情報で整理しました。
まとめ:pgAdmin 4を9.19へ上げる前にサーバーモードの設定を決める
対応の本線は、pgAdmin 4を9.19へ上げることです。15件のCVEはどれも9.19より前の全版が対象で、9.18以前に回避用の設定はありません。Dockerならdpage/pgadmin4:9.19、pipならpgadmin4==9.19が修正版です。
急ぐべきなのは、複数の利用者でpgAdminを共有するサーバーモードの環境です。15件の多くは「ある利用者が、別の利用者やpgAdminを動かすOSアカウントの権限・資格情報を使えてしまう」種類の欠陥で、1人で使うデスクトップ版では前提が成り立ちにくいものが中心です。
サーバーモードで更新するときは、起動の前にSERVER_PASSEXEC_COMMANDSを書いてください。9.19の初回起動で、この設定と一致しない既存のPassword exec commandは消え、元に戻せません。社外からアクセスできる場所にpgAdminを置いているなら、更新に合わせて公開範囲も見直します。DB管理ツールを含めた公開面をまとめて点検したい場合は、脆弱性診断・セキュリティ診断で範囲を決めると漏れが出にくくなります。
pgAdmin 4 v9.19で修正された15件のCVEとサーバーモードへの影響範囲
9.19の公式リリースノートは、CVE-2026-116238から116252までの15件を挙げています。採番は10月30日で、どのCVEも影響範囲は「9.19より前」です。
CVE-2026-116241:書き出し機能のTO PROGRAMでOSコマンド実行
Import/Export Dataは、利用者が書いたクエリの結果をファイルへ書き出す機能です。pgAdminは内部でそのクエリをpsqlの\copy (クエリ) TO ...で包んで実行します。包みの外へ抜け出されないよう、pgAdminは事前に括弧の対応を検査していました。
7月に採番されたCVE-2026-17566は、単一引用符の中のバックスラッシュの扱いがpgAdminとpsqlで食い違い、検査をすり抜けた閉じ括弧の後ろにTO PROGRAMを書けるという欠陥でした。9.17の修正は、単一引用符の中のバックスラッシュを拒否する形でした。
CVE-2026-116241は、その修正が二重引用符で囲んだ識別子の側に及んでいなかった抜け道です。standard_conforming_stringsをoffにすると、psqlはバックスラッシュ付きの二重引用符で識別子を閉じません。この設定は一般のDBロールが自分のセッションで切り替えられます。結果としてTO PROGRAMのコマンドが、pgAdminを動かすOSアカウントの権限で実行されました。CVSS 3.1は9.9、分類はCWE-78(OSコマンドインジェクション)です。
9.19の修正コミットは、二重引用符の中のバックスラッシュも一律に拒否します。サーバー設定によって意味が変わる文字は、検査時点で正しく解釈できないので受け付けない、という考え方です。外部入力をコマンドラインへ組み込むと何が起きるかは、OSコマンドインジェクションの仕組みと引数配列で防ぐ実装で詳しく扱っています。
15件をCVSS 3.1の値と攻撃の前提で並べた一覧表と読み方
CVE公式記録のCVSS 3.1の値で、高い順に並べました。「前提」の列は、攻撃者に何が要るかを示します。
| CVE | CVSS 3.1 | 中身 | 前提 |
|---|---|---|---|
| 116241 | 9.9 | Import/ExportでOSコマンド実行 | ログイン済み |
| 116239 | 8.5 | LDAP等で同名の別アカウントに入る | ログイン済み |
| 116249 | 8.4 | 接続ファイルのパスが保存先の外へ出る | ログイン済み |
| 116242 | 8.2 | SSHトンネルがOSアカウントの鍵を使う | ログイン済み |
| 116244 | 8.2 | OSアカウントのKerberos資格情報を使う | ログイン済み |
| 116245 | 8.2 | libpqのサービス定義を誰でも使える | ログイン済み |
| 116247 | 8.2 | OSアカウントの.pgpassと証明書を使う | ログイン済み |
| 116238 | 8.1 | ログイン試行回数の制限を回避できる | なし |
| 116243 | 7.5 | 共有ストレージのファイルを実行できる | ログイン済み |
| 116248 | 6.8 | 管理者でなくてもサーバーを全員に共有 | ログイン済み |
| 116240 | 6.5 | 検査したパスと別のパスを操作する | ログイン済み |
| 116246 | 6.5 | パスワードがプロセスの環境変数に残る | ログイン済み |
| 116250 | 6.5 | シンボリックリンクの差し替えで競合 | ホスト上の操作 |
| 116252 | 6.5 | マテビュー更新で接続先を乗っ取られる | CREATEDB権限 |
| 116251 | 4.2 | パスワード変更後も他のセッションが残る | 旧パスワード等 |
CVE番号は「CVE-2026-」を省いています。ログインが不要なのは116238の1件だけです。ただしこれは、総当たりの回数制限が外れて管理者アカウントのパスワードを推測し続けられる欠陥で、インターネットから届くpgAdminでは入口になります。残りの14件はログイン済みの利用者が起点で、いずれもサーバーモードでの権限の境界を越える話です。スコアの各指標の読み方はCVSSv3のスコアの目安と計算式で確認できます。
サービスアカウントの資格情報を他の利用者が使えた5件の共通点
116242・116244・116245・116247・116246の5件は、根が同じです。pgAdminが接続に使うlibpqやSSHは、指定が無いとき、実行中のOSアカウントの~/.pgpass、~/.sshの鍵、Kerberosのチケット、~/.pg_service.confを自動で拾います。
デスクトップ版なら本人の資格情報なので問題になりません。サーバーモードでは、pgAdminのOSアカウントに置いた資格情報を、ログインした誰もが自分の接続に使えました。運用担当がサービスアカウントに.pgpassを置いた構成は、9.18以前では全利用者への開放と同じです。9.19は、サーバーモードでこの自動の引き継ぎを止めました。
ログイン試行制限の回避とLDAP・Kerberos認証での取り違え
116238は、pgAdminが組み込んでいるFlask-Securityの画面経由でログインすると、pgAdmin側の失敗回数の数え上げとロックを通らなかった欠陥です。既定のMAX_LOGIN_ATTEMPTS = 3が実質的に効いていませんでした。
116239は、LDAPやKerberosで認証した利用者を、ユーザー名だけで内部アカウントと照合していた欠陥です。最初のセットアップで作った管理者と同じユーザー名をLDAP側に持つ人は、管理者としてログインできました。AUTHENTICATION_SOURCESにldapやkerberosを入れている環境では、9.19へ上げた後、内部アカウントとLDAP側のユーザー名が重なっていないかも確かめてください。LDAPの認証の流れはLDAPの仕組みと認証実装、回数制限だけに頼らない守り方は多要素認証(MFA)の実装方式で整理しています。
影響を受ける構成の見分け方:サーバーモードかデスクトップかと版の確認
同じpgAdmin 4でも、動かし方で危険の大きさは変わります。
サーバーモードとデスクトップモードで急ぎ度が変わるCVEの区別
サーバーモードは、Webサーバーとして立てたpgAdminに複数の利用者がブラウザでログインする形です。Dockerのdpage/pgadmin4イメージは既定でこちらになります。デスクトップモードは、各自のPCにインストールして1人で使う形です。
私たちの判断はこうです。サーバーモードで2人以上が使っている環境は、15件すべてが当てはまるものとして、今週中に9.19へ上げます。デスクトップモードで1人だけが使うPCは、権限の境界を越える攻撃者が同じpgAdminの中にいない前提なので、次の定期更新で構いません。ただし116241は、手元のPCで自分がOSコマンドを実行できるだけになるとはいえ、他人から渡されたクエリをそのまま書き出し機能へ貼る運用があるなら早めに上げます。
Dockerやpipで動くpgAdminの版を確かめるコマンドと判定
pgAdminの版は、画面の「Help」から「About」でも確かめられます。台数が多い場合は、次のコマンドでまとめて確かめてください。Docker版はイメージ内の/pgadmin4/version.pyに版が書かれています。
# Docker(compose)で動かしている場合:イメージ内の版の定義を表示する
docker compose exec pgadmin grep -E "^APP_(RELEASE|REVISION) =" /pgadmin4/version.py
# 出力例:APP_RELEASE = 9 と APP_REVISION = 19 なら修正版
# どのタグのイメージで起動しているかも見る(latest のままなら固定する)
docker compose images pgadmin
# pip で入れている場合
pip show pgadmin4 | grep -i ^version
上のコマンドはpgAdminのソースの構成から書いたもので、私たちは未実行です。APP_REVISIONが18以下なら未修正です。Docker Hubでは10月29日に9.19と9.19.0のタグが公開され、PyPIにも同日に9.19が載っています。PostgreSQL本体の版の確かめ方はPostgreSQLのバージョン確認方法で扱っています。
更新前に済ませる設定:SERVER_PASSEXEC_COMMANDSほか3項目
9.19では、サーバーモードの一部の接続が更新しただけで通らなくなります。起動の前に次の3項目を決めます。
Password exec commandの許可リスト化と初回起動で消える既存コマンド
Password exec commandは、DBのパスワードを保存せず、接続のたびにコマンドを実行して取り出す機能です。9.18までは、管理者がENABLE_SERVER_PASS_EXEC_CMD = Trueにすると、利用者がサーバーの設定画面に任意のコマンドを書けました。9.19ではこの設定は無視され、管理者がSERVER_PASSEXEC_COMMANDSに名前付きで定義したコマンドだけを、利用者が名前で選ぶ形に変わっています。
注意すべきは移行の規則です。config.pyの説明によると、9.19の初回起動で、既存の自由記述のコマンドのうち、定義した引数を半角スペース1つで連結した文字列と完全に一致するものだけが名前に置き換わります。それ以外のコマンドは消去され、元に戻せません。設定が空のまま起動した場合は変換が保留され、起動のたびにログへ警告が出ます。
そこで、更新の前に既存のコマンドを棚卸しします。pgAdminの設定DB(Docker版は/var/lib/pgadmin/pgadmin4.db)を手元へ複製し、次のスクリプトで一覧にします。
# list_passexec.py:pgAdmin の設定DBから、Password exec command が入ったサーバーを一覧にする
import sqlite3, sys
db = sys.argv[1] if len(sys.argv) > 1 else "pgadmin4.db"
con = sqlite3.connect(f"file:{db}?mode=ro", uri=True) # 読み取り専用で開く
for table in ("server", "sharedserver"):
rows = con.execute(
f"SELECT id, user_id, name, host, passexec_cmd FROM {table} "
"WHERE passexec_cmd IS NOT NULL AND passexec_cmd <> ''"
).fetchall()
for sid, uid, name, host, cmd in rows:
print(f"{table}\tid={sid}\tuser_id={uid}\t{name}\t{host}\t{cmd}")
con.close()
テーブル名と列名は、pgAdminのモデル定義(serverとsharedserverのpassexec_cmd)に合わせました。2026年11月2日に、同じ列構成のダミーDBで実行し、コマンドが入った2行だけが出ることを確かめています。出てきたコマンドを、引数の区切りが一致するように次の形で定義します。
# config_local.py(サーバーモード)
# 既存の "/usr/local/bin/get-pg-pass --ttl 300" をそのまま名前 vault に変換させる
SERVER_PASSEXEC_COMMANDS = {
'vault': ['/usr/local/bin/get-pg-pass', '--ttl', '300'],
}
# サービス定義は使わない(既定の空のまま。使う場合だけ名前を並べる)
SERVER_MODE_ALLOWED_SERVICES = []
9.19のコマンドはシェルを通さず直接実行され、接続先の情報はPGADMIN_PASSEXEC_HOSTなどの環境変数で渡されます。config.pyの説明は、ホスト・ポート・ユーザー名・DB名の4つはサーバー設定を編集した人が決める値なので、信用できない入力として扱うよう求めています。コマンド側で認可の判断に使ってよいのは、pgAdminの利用者を示すPGADMIN_PASSEXEC_PGADMIN_USERとPGADMIN_PASSEXEC_AUTH_SOURCEだけです。
サービス定義・SSHトンネル・.pgpassの挙動の変化と移行時の確認
前の章の5件の修正で、サーバーモードの接続まわりは次のように変わりました。どれも、OSアカウントの資格情報に頼っていた接続が通らなくなる変更です。
SERVER_MODE_ALLOWED_SERVICESは既定で空になり、管理者が並べたサービス名以外は保存も接続もできない- SSHトンネルは、pgAdminのOSアカウントの
~/.sshの鍵・設定・エージェントを使わず、利用者が設定した識別ファイルかパスワードだけを使う - 共有サーバーの所有者以外は、所有者のパスワードファイルを引き継がない
- 保存先(
STORAGE_DIR)の外を指す接続ファイルを持つサーバーは、ファイルを保存先の中へ移すまで接続できない
SERVER_MODE_ALLOWED_SERVICESに名前を並べるのは慎重に判断してください。config.pyは、並べたサービスの資格情報は全利用者で共有され、接続先のホストを書き換えてパスワードを別のサーバーへ送らせることもできると警告しています。私たちは、サーバーモードではこの設定を空のまま使い、各利用者が自分の資格情報で接続する形を基本にします。
Dockerのcompose定義で設定を渡し、9.19へ上げる手順
コンテナ配置の公式ドキュメントによると、コンテナ内の/pgadmin4/config_local.pyは最後に読み込まれ、それ以前の設定を上書きします。前の手順で作ったconfig_local.pyを読み取り専用でマウントし、イメージのタグを9.19に固定します。
# compose.yaml(抜粋)
services:
pgadmin:
image: dpage/pgadmin4:9.19 # latest ではなく版を固定する
environment:
PGADMIN_DEFAULT_EMAIL: ${PGADMIN_DEFAULT_EMAIL}
PGADMIN_DEFAULT_PASSWORD: ${PGADMIN_DEFAULT_PASSWORD}
volumes:
- pgadmin-data:/var/lib/pgadmin
- ./config_local.py:/pgadmin4/config_local.py:ro
volumes:
pgadmin-data:
更新は次の順で進めます。
docker compose cp pgadmin:/var/lib/pgadmin/pgadmin4.db ./pgadmin4.db.bakで設定DBを退避する- 退避したファイルに
list_passexec.pyを実行し、既存のコマンドを書き出す config_local.pyにSERVER_PASSEXEC_COMMANDSを書き、compose定義のタグを9.19にするdocker compose pull pgadminの後、docker compose up -d pgadminで起動するdocker compose logs pgadminで、Password exec commandの変換に関する警告が出ていないかを見る
1の退避は省かないでください。PostgreSQL本体も同じcomposeで動かしている場合のボリュームの持ち方は、Docker PostgreSQL構築のcompose定義で扱っています。
更新できない期間の緩和策と、pgAdminを社外へ出さない判断基準
更新に数日かかる場合の狭め方と、pgAdminをどこに置くかの判断を整理します。
Import/Export Dataの権限を外し、一般利用者のコマンド実行経路を絞る
CVE-2026-116241の説明によると、Import/Export Dataの権限は管理者以外にも既定で付いています。サーバーモードのユーザー管理には、ロールごとに機能の権限を付与・取り消しできるPermissionsタブがあります。更新までの間は、書き出し機能を使う必要がない利用者のロールから、Import/Export Dataの権限を外してください。
併せて、ENABLE_PSQLがFalse(サーバーモードの既定)のままかを確かめます。config.pyの説明どおり、PSQLツールを開放するとサーバー上で任意のコマンドを実行できるためです。pgAdminのコンテナは、本番DBのネットワークへ届く最小限の経路だけを持たせ、コンテナを動かすOSアカウントに.pgpassやSSH鍵を置かないようにします。この2点は9.19へ上げた後も残す運用です。
standard_conforming_stringsは一般のロールでも切り替えられるため、DBロールを絞ってもこの権限の代わりにはなりません。DBロールの設計はPostgreSQLのロールと権限設計で整理しています。
インターネットに公開したpgAdminをやめる条件と私たちの判断
pgAdminは2026年だけでも6月の9.16から今回の9.19まで、4回続けてセキュリティ修正を出しました。9.17では6月の修正の抜け道が3件(CVE-2026-12044〜12046の不完全な修正)見つかり、今回の116241も9.17の抜け道です。
私たちの判断はこうです。pgAdminのログイン画面をインターネットから直接開ける構成は、今回の更新を機にやめます。VPNか、IdP連携の認証プロキシの内側へ移し、社外からはそれを経由した人だけが届くようにします。ログイン不要の116238のような欠陥が次に出ても、届く人を絞っていれば入口になりません。逆に、社内ネットワークからしか届かず、使う人が数名の運用担当に限られるなら、公開範囲はそのままにして更新の速さを優先して構いません。
サーバーモードを複数部署で共有している場合は、デスクトップ版を各自に配る方式や、DBeaverのような別のクライアントへの分散も選択肢です。1台のpgAdminに全員の資格情報が集まる構成そのものが、今回の15件の多くの前提になっています。DB管理ツールを含めた社外公開面を一度洗い出したい場合は、脆弱性診断の種類と外注時の判断基準で進め方を確認できます。
pgAdminの脆弱性と9.19への更新でよく受ける質問と判断のための回答
今回の更新を進める中で出てきやすい疑問に、公開されている一次情報の範囲で答えます。
PostgreSQL本体も更新しないといけませんか?
今回の15件は管理ツールのpgAdminの欠陥で、PostgreSQL本体の更新では直りません。逆に、pgAdminを上げてもPostgreSQL本体の脆弱性は残ります。両者は別に管理してください。PostgreSQL本体の脆弱性の例は、PostgreSQLの脆弱性CVE-2024-10979の影響と確認手順で解説しています。なお9.19が対応するサーバーはPostgreSQL 14〜18です。
デスクトップ版のpgAdminだけを使っていても更新は必要ですか?
必要ですが、急ぎ度は下がります。15件の多くは、サーバーモードで別の利用者の権限や資格情報に届く欠陥です。1人で使うデスクトップ版では、その前提が成り立ちにくいためです。デスクトップ版は起動時に新しい版を知らせるので、次の定期更新で9.19以降へ上げてください。
9.19へ上げたら接続できなくなったサーバーがあるのはなぜですか?
サーバーモードで、pgAdminを動かすOSアカウントの.pgpass・SSH鍵・サービス定義・Kerberosの資格情報に頼っていた接続は、9.19で通らなくなりました。利用者ごとにパスワードや識別ファイルを設定し直すか、サービス定義を使う場合はSERVER_MODE_ALLOWED_SERVICESに名前を並べます。保存先の外にある接続ファイルも、保存先の中へ移す必要があります。
Password exec commandが消えてしまった場合は戻せますか?
pgAdminの中では戻せません。更新前に退避した設定DBがあれば、そこから元のコマンドを読み出してSERVER_PASSEXEC_COMMANDSに定義し、各サーバーの設定で名前を選び直します。退避が無ければ、各利用者に元のコマンドを確かめるしかありません。
Dockerでlatestタグを使っていれば自動で修正されますか?
されません。latestは取得した時点の版を指すだけで、docker compose pullで取り直し、コンテナを作り直すまで古い版のまま動きます。しかも9.19は更新前の設定が要る版なので、気付かないうちに上がると既存のコマンドが消える原因になります。タグは9.19のように版で固定し、上げる時期を自分で決めてください。
関連記事
- OSコマンドインジェクションとは?仕組みと引数配列で防ぐ実装をPython・Node.js・Goのコード例で解説:CVE-2026-116241と同じ種類の欠陥を、作る側から防ぐ方法です。
- PostgreSQLの脆弱性CVE-2024-10979とは?PL/Perlで任意コード実行に至る仕組みと影響バージョン・確認手順【2026年10月時点】:pgAdminとは別に管理するPostgreSQL本体の脆弱性です。
- Docker PostgreSQL構築の手順|18で変わったボリューム位置とcompose定義・initdb初期化を実装目線で解説:pgAdminと並べて動かすPostgreSQLのcompose定義です。
- PostgreSQLのロールと権限設計|CREATE ROLEの属性とGRANT・既定権限の決め方:pgAdminの利用者に渡すDBロールの決め方です。
- DBeaverとは?インストール・日本語化・接続設定と無料版の範囲【26.2対応】:サーバーモードのpgAdminから移すときの選択肢です。